summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xdebian/patches/33_scripts__mysql_create_system_tables__no_test.dpatch12
-rw-r--r--include/my_sys.h4
-rw-r--r--include/my_user.h6
-rw-r--r--mysql-test/include/mtr_check.sql1
-rwxr-xr-xmysql-test/mysql-test-run.pl1
-rw-r--r--mysql-test/r/1st.result1
-rw-r--r--mysql-test/r/connect.result3
-rw-r--r--mysql-test/r/events_bugs.result2
-rw-r--r--mysql-test/r/grant.result125
-rw-r--r--mysql-test/r/grant2.result4
-rw-r--r--mysql-test/r/grant_cache_no_prot.result4
-rw-r--r--mysql-test/r/grant_cache_ps_prot.result4
-rw-r--r--mysql-test/r/information_schema-big.result8
-rw-r--r--mysql-test/r/information_schema.result12
-rw-r--r--mysql-test/r/information_schema_all_engines.result15
-rw-r--r--mysql-test/r/join_cache.result49
-rw-r--r--mysql-test/r/limit_rows_examined.result14
-rw-r--r--mysql-test/r/log_tables_upgrade.result1
-rw-r--r--mysql-test/r/mysql_upgrade.result7
-rw-r--r--mysql-test/r/mysql_upgrade_ssl.result1
-rw-r--r--mysql-test/r/mysqlcheck.result4
-rw-r--r--mysql-test/r/mysqld--help.result2
-rw-r--r--mysql-test/r/plugin_auth.result30
-rw-r--r--mysql-test/r/plugin_auth_qa.result2
-rw-r--r--mysql-test/r/plugin_auth_qa_1.result30
-rw-r--r--mysql-test/r/ps.result6
-rw-r--r--mysql-test/r/range.result12
-rw-r--r--mysql-test/r/range_mrr_icp.result12
-rw-r--r--mysql-test/r/skip_grants.result6
-rw-r--r--mysql-test/r/sp_notembedded.result8
-rw-r--r--mysql-test/r/subselect_mat.result4
-rw-r--r--mysql-test/r/subselect_sj.result2
-rw-r--r--mysql-test/r/subselect_sj_jcl6.result2
-rw-r--r--mysql-test/r/subselect_sj_mat.result2
-rw-r--r--mysql-test/r/system_mysql_db.result2
-rw-r--r--mysql-test/r/system_mysql_db_fix40123.result2
-rw-r--r--mysql-test/r/system_mysql_db_fix50030.result2
-rw-r--r--mysql-test/r/system_mysql_db_fix50117.result2
-rw-r--r--mysql-test/r/trigger_notembedded.result4
-rw-r--r--mysql-test/r/view_grant.result4
-rw-r--r--mysql-test/suite/federated/federatedx.test2
-rw-r--r--mysql-test/suite/federated/have_federatedx.opt2
-rw-r--r--mysql-test/suite/funcs_1/r/innodb_trig_03e.result2
-rw-r--r--mysql-test/suite/funcs_1/r/is_column_privileges.result6
-rw-r--r--mysql-test/suite/funcs_1/r/is_columns_is.result24
-rw-r--r--mysql-test/suite/funcs_1/r/is_columns_is_embedded.result24
-rw-r--r--mysql-test/suite/funcs_1/r/is_columns_mysql.result10
-rw-r--r--mysql-test/suite/funcs_1/r/is_columns_mysql_embedded.result10
-rw-r--r--mysql-test/suite/funcs_1/r/is_key_column_usage.result3
-rw-r--r--mysql-test/suite/funcs_1/r/is_schema_privileges.result6
-rw-r--r--mysql-test/suite/funcs_1/r/is_statistics.result3
-rw-r--r--mysql-test/suite/funcs_1/r/is_statistics_mysql.result3
-rw-r--r--mysql-test/suite/funcs_1/r/is_statistics_mysql_embedded.result6
-rw-r--r--mysql-test/suite/funcs_1/r/is_table_constraints.result1
-rw-r--r--mysql-test/suite/funcs_1/r/is_table_constraints_mysql.result1
-rw-r--r--mysql-test/suite/funcs_1/r/is_table_constraints_mysql_embedded.result2
-rw-r--r--mysql-test/suite/funcs_1/r/is_table_privileges.result6
-rw-r--r--mysql-test/suite/funcs_1/r/is_tables_is.result92
-rw-r--r--mysql-test/suite/funcs_1/r/is_tables_is_embedded.result92
-rw-r--r--mysql-test/suite/funcs_1/r/is_tables_mysql.result23
-rw-r--r--mysql-test/suite/funcs_1/r/is_tables_mysql_embedded.result46
-rw-r--r--mysql-test/suite/funcs_1/r/is_user_privileges.result39
-rw-r--r--mysql-test/suite/funcs_1/r/memory_trig_03e.result2
-rw-r--r--mysql-test/suite/funcs_1/r/myisam_trig_03e.result2
-rw-r--r--mysql-test/suite/funcs_1/r/processlist_priv_no_prot.result2
-rw-r--r--mysql-test/suite/funcs_1/r/processlist_priv_ps.result2
-rw-r--r--mysql-test/suite/funcs_1/t/is_engines_federated.opt2
-rw-r--r--mysql-test/suite/perfschema/r/column_privilege.result2
-rw-r--r--mysql-test/suite/perfschema/r/digest_table_full.result2
-rw-r--r--mysql-test/suite/perfschema/r/ortho_iter.result2
-rw-r--r--mysql-test/suite/perfschema/r/privilege.result2
-rw-r--r--mysql-test/suite/perfschema/r/privilege_table_io.result3
-rw-r--r--mysql-test/suite/perfschema/r/start_server_disable_idle.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_disable_stages.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_disable_statements.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_disable_waits.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_innodb.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_no_account.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_no_cond_class.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_no_cond_inst.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_no_file_class.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_no_file_inst.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_no_host.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_no_mutex_class.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_no_mutex_inst.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_no_rwlock_class.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_no_rwlock_inst.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_no_setup_actors.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_no_setup_objects.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_no_socket_class.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_no_socket_inst.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_no_stage_class.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_no_stages_history.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_no_stages_history_long.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_no_statements_history.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_no_statements_history_long.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_no_table_hdl.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_no_table_inst.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_no_thread_class.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_no_thread_inst.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_no_user.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_no_waits_history.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_no_waits_history_long.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_off.result2
-rw-r--r--mysql-test/suite/perfschema/r/start_server_on.result2
-rw-r--r--mysql-test/suite/perfschema/r/statement_digest.result74
-rw-r--r--mysql-test/suite/perfschema/r/statement_digest_consumers.result74
-rw-r--r--mysql-test/suite/perfschema/r/statement_digest_long_query.result4
-rw-r--r--mysql-test/suite/perfschema/r/table_aggregate_global_2u_2t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_aggregate_global_2u_3t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_aggregate_global_4u_2t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_aggregate_global_4u_3t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_aggregate_hist_2u_2t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_aggregate_hist_2u_3t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_aggregate_hist_4u_2t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_aggregate_hist_4u_3t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_aggregate_off.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_aggregate_thread_2u_2t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_aggregate_thread_2u_3t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_aggregate_thread_4u_2t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_aggregate_thread_4u_3t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_io_aggregate_global_2u_2t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_io_aggregate_global_2u_3t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_io_aggregate_global_4u_2t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_io_aggregate_global_4u_3t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_io_aggregate_hist_2u_2t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_io_aggregate_hist_2u_3t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_io_aggregate_hist_4u_2t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_io_aggregate_hist_4u_3t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_io_aggregate_thread_2u_2t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_io_aggregate_thread_2u_3t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_io_aggregate_thread_4u_2t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_io_aggregate_thread_4u_3t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_lock_aggregate_global_2u_2t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_lock_aggregate_global_2u_3t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_lock_aggregate_global_4u_2t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_lock_aggregate_global_4u_3t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_lock_aggregate_hist_2u_2t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_lock_aggregate_hist_2u_3t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_lock_aggregate_hist_4u_2t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_lock_aggregate_hist_4u_3t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_lock_aggregate_thread_2u_2t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_lock_aggregate_thread_2u_3t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_lock_aggregate_thread_4u_2t.result2
-rw-r--r--mysql-test/suite/perfschema/r/table_lock_aggregate_thread_4u_3t.result2
-rw-r--r--mysql-test/suite/roles/acl_load_mutex-5170.result8
-rw-r--r--mysql-test/suite/roles/acl_load_mutex-5170.test24
-rw-r--r--mysql-test/suite/roles/admin.result152
-rw-r--r--mysql-test/suite/roles/admin.test102
-rw-r--r--mysql-test/suite/roles/create_and_drop_role.result36
-rw-r--r--mysql-test/suite/roles/create_and_drop_role.test47
-rw-r--r--mysql-test/suite/roles/create_and_drop_role_invalid_user_table.result14
-rw-r--r--mysql-test/suite/roles/create_and_drop_role_invalid_user_table.test25
-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/default_create_user_not_role.result6
-rw-r--r--mysql-test/suite/roles/default_create_user_not_role.test11
-rw-r--r--mysql-test/suite/roles/definer.result625
-rw-r--r--mysql-test/suite/roles/definer.test331
-rw-r--r--mysql-test/suite/roles/drop_current_user-5176.result9
-rw-r--r--mysql-test/suite/roles/drop_current_user-5176.test13
-rw-r--r--mysql-test/suite/roles/drop_routines.result79
-rw-r--r--mysql-test/suite/roles/drop_routines.test69
-rw-r--r--mysql-test/suite/roles/none_public.result56
-rw-r--r--mysql-test/suite/roles/none_public.test57
-rw-r--r--mysql-test/suite/roles/password.result34
-rw-r--r--mysql-test/suite/roles/password.test48
-rw-r--r--mysql-test/suite/roles/rebuild_role_grants.result58
-rw-r--r--mysql-test/suite/roles/rebuild_role_grants.test56
-rw-r--r--mysql-test/suite/roles/recursive.inc258
-rw-r--r--mysql-test/suite/roles/recursive.result328
-rw-r--r--mysql-test/suite/roles/recursive.test4
-rw-r--r--mysql-test/suite/roles/recursive_dbug.result448
-rw-r--r--mysql-test/suite/roles/recursive_dbug.test14
-rw-r--r--mysql-test/suite/roles/rename_user.result27
-rw-r--r--mysql-test/suite/roles/rename_user.test38
-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/rpl_definer.result72
-rw-r--r--mysql-test/suite/roles/rpl_definer.test48
-rw-r--r--mysql-test/suite/roles/set_and_drop.result117
-rw-r--r--mysql-test/suite/roles/set_and_drop.test113
-rw-r--r--mysql-test/suite/roles/set_role-database-recursive.result83
-rw-r--r--mysql-test/suite/roles/set_role-database-recursive.test64
-rw-r--r--mysql-test/suite/roles/set_role-database-simple.result52
-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.result118
-rw-r--r--mysql-test/suite/roles/set_role-recursive.test79
-rw-r--r--mysql-test/suite/roles/set_role-routine-simple.result103
-rw-r--r--mysql-test/suite/roles/set_role-routine-simple.test81
-rw-r--r--mysql-test/suite/roles/set_role-simple.result47
-rw-r--r--mysql-test/suite/roles/set_role-simple.test39
-rw-r--r--mysql-test/suite/roles/set_role-table-column-priv.result69
-rw-r--r--mysql-test/suite/roles/set_role-table-column-priv.test56
-rw-r--r--mysql-test/suite/roles/set_role-table-simple.result67
-rw-r--r--mysql-test/suite/roles/set_role-table-simple.test53
-rw-r--r--mysql-test/suite/roles/show_grants.result148
-rw-r--r--mysql-test/suite/roles/show_grants.test90
-rw-r--r--mysql-test/suite/rpl/r/rpl_do_grant.result8
-rw-r--r--mysql-test/t/grant.test9
-rw-r--r--mysql-test/t/grant2.test2
-rw-r--r--mysql-test/t/join_cache.test44
-rw-r--r--mysql-test/t/limit_rows_examined.test2
-rw-r--r--mysql-test/t/plugin_auth.test25
-rw-r--r--mysql-test/t/plugin_auth_qa_1.test26
-rw-r--r--mysql-test/t/system_mysql_db_fix40123.test5
-rw-r--r--mysql-test/t/system_mysql_db_fix50030.test4
-rw-r--r--mysql-test/t/system_mysql_db_fix50117.test4
-rw-r--r--mysys/array.c17
-rw-r--r--scripts/mysql_system_tables.sql3
-rw-r--r--scripts/mysql_system_tables_data.sql8
-rw-r--r--scripts/mysql_system_tables_fix.sql8
-rw-r--r--sql-common/my_user.c28
-rw-r--r--sql/handler.h6
-rw-r--r--sql/item_strfunc.cc30
-rw-r--r--sql/item_strfunc.h22
-rw-r--r--sql/lex.h3
-rw-r--r--sql/log_event.cc15
-rw-r--r--sql/mysqld.cc45
-rw-r--r--sql/set_var.cc46
-rw-r--r--sql/set_var.h11
-rw-r--r--sql/share/errmsg-utf8.txt19
-rw-r--r--sql/slave.cc4
-rw-r--r--sql/sp.cc50
-rw-r--r--sql/sp_head.cc13
-rw-r--r--sql/sql_acl.cc4988
-rw-r--r--sql/sql_acl.h112
-rw-r--r--sql/sql_array.h27
-rw-r--r--sql/sql_base.cc7
-rw-r--r--sql/sql_class.cc12
-rw-r--r--sql/sql_class.h23
-rw-r--r--sql/sql_cmd.h1
-rw-r--r--sql/sql_db.cc20
-rw-r--r--sql/sql_hset.h20
-rw-r--r--sql/sql_lex.h3
-rw-r--r--sql/sql_parse.cc174
-rw-r--r--sql/sql_parse.h7
-rw-r--r--sql/sql_select.cc92
-rw-r--r--sql/sql_select.h1
-rw-r--r--sql/sql_show.cc41
-rw-r--r--sql/sql_trigger.cc63
-rw-r--r--sql/sql_view.cc85
-rw-r--r--sql/sql_yacc.yy198
-rw-r--r--sql/structs.h8
-rw-r--r--sql/table.h3
-rw-r--r--storage/myisam/mi_write.c4
248 files changed, 10707 insertions, 1881 deletions
diff --git a/debian/patches/33_scripts__mysql_create_system_tables__no_test.dpatch b/debian/patches/33_scripts__mysql_create_system_tables__no_test.dpatch
index 88ed0347cfb..615261713b0 100755
--- a/debian/patches/33_scripts__mysql_create_system_tables__no_test.dpatch
+++ b/debian/patches/33_scripts__mysql_create_system_tables__no_test.dpatch
@@ -10,7 +10,7 @@
@DPATCH@
--- old/scripts/mysql_system_tables_data.sql 2008-12-04 22:59:44.000000000 +0100
+++ new/scripts/mysql_system_tables_data.sql 2008-12-04 23:00:07.000000000 +0100
-@@ -26,8 +26,6 @@
+@@ -31,8 +31,6 @@
-- Fill "db" table with default grants for anyone to
-- access database 'test' and 'test_%' if "db" table didn't exist
CREATE TEMPORARY TABLE tmp_db LIKE db;
@@ -19,12 +19,12 @@
INSERT INTO db SELECT * FROM tmp_db WHERE @had_db_table=0;
DROP TABLE tmp_db;
-@@ -40,8 +38,6 @@
- REPLACE INTO tmp_user SELECT @current_hostname,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','','' FROM dual WHERE LOWER( @current_hostname) != 'localhost';
- REPLACE INTO tmp_user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','','');
- REPLACE INTO tmp_user VALUES ('::1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','','');
+@@ -44,8 +42,6 @@
+ REPLACE INTO tmp_user SELECT @current_hostname,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','','N','N' FROM dual WHERE LOWER( @current_hostname) != 'localhost';
+ REPLACE INTO tmp_user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','','N','N');
+ REPLACE INTO tmp_user VALUES ('::1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','','N','N');
-INSERT INTO tmp_user (host,user) VALUES ('localhost','');
-INSERT INTO tmp_user (host,user) SELECT @current_hostname,'' FROM dual WHERE LOWER(@current_hostname ) != 'localhost';
INSERT INTO user SELECT * FROM tmp_user WHERE @had_user_table=0;
DROP TABLE tmp_user;
-
+
diff --git a/include/my_sys.h b/include/my_sys.h
index e3eb52d6018..bb7a9204678 100644
--- a/include/my_sys.h
+++ b/include/my_sys.h
@@ -327,6 +327,9 @@ struct st_my_file_info
extern struct st_my_file_info *my_file_info;
+/* Free function pointer */
+typedef void (*FREE_FUNC)(void *);
+
typedef struct st_dynamic_array
{
uchar *buffer;
@@ -792,6 +795,7 @@ extern my_bool allocate_dynamic(DYNAMIC_ARRAY *array, uint max_elements);
extern void get_dynamic(DYNAMIC_ARRAY *array, void *element, uint array_index);
extern void delete_dynamic(DYNAMIC_ARRAY *array);
extern void delete_dynamic_element(DYNAMIC_ARRAY *array, uint array_index);
+extern void delete_dynamic_with_callback(DYNAMIC_ARRAY *array, FREE_FUNC f);
extern void freeze_size(DYNAMIC_ARRAY *array);
extern int get_index_dynamic(DYNAMIC_ARRAY *array, void *element);
#define dynamic_array_ptr(array,array_index) ((array)->buffer+(array_index)*(array)->size_of_element)
diff --git a/include/my_user.h b/include/my_user.h
index 46eb11a500d..404996e996c 100644
--- a/include/my_user.h
+++ b/include/my_user.h
@@ -26,9 +26,9 @@
C_MODE_START
-void parse_user(const char *user_id_str, size_t user_id_len,
- char *user_name_str, size_t *user_name_len,
- char *host_name_str, size_t *host_name_len);
+int parse_user(const char *user_id_str, size_t user_id_len,
+ char *user_name_str, size_t *user_name_len,
+ char *host_name_str, size_t *host_name_len);
C_MODE_END
diff --git a/mysql-test/include/mtr_check.sql b/mysql-test/include/mtr_check.sql
index 9267374e15d..46c2d59aa59 100644
--- a/mysql-test/include/mtr_check.sql
+++ b/mysql-test/include/mtr_check.sql
@@ -69,6 +69,7 @@ BEGIN
mysql.host,
mysql.proc,
mysql.procs_priv,
+ mysql.roles_mapping,
mysql.tables_priv,
mysql.time_zone,
mysql.time_zone_leap_second,
diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl
index a7a53166563..3b8cb96eacb 100755
--- a/mysql-test/mysql-test-run.pl
+++ b/mysql-test/mysql-test-run.pl
@@ -182,6 +182,7 @@ my $DEFAULT_SUITES= join(',', map { "$_-" } qw(
percona
perfschema
plugins
+ roles
rpl
sphinx
sys_vars
diff --git a/mysql-test/r/1st.result b/mysql-test/r/1st.result
index cb82cb5fe7d..f9e4b37aa94 100644
--- a/mysql-test/r/1st.result
+++ b/mysql-test/r/1st.result
@@ -26,6 +26,7 @@ plugin
proc
procs_priv
proxies_priv
+roles_mapping
servers
slow_log
table_stats
diff --git a/mysql-test/r/connect.result b/mysql-test/r/connect.result
index 84a1e9dbab2..a4e6f374916 100644
--- a/mysql-test/r/connect.result
+++ b/mysql-test/r/connect.result
@@ -20,6 +20,7 @@ plugin
proc
procs_priv
proxies_priv
+roles_mapping
servers
slow_log
table_stats
@@ -59,6 +60,7 @@ plugin
proc
procs_priv
proxies_priv
+roles_mapping
servers
slow_log
table_stats
@@ -106,6 +108,7 @@ plugin
proc
procs_priv
proxies_priv
+roles_mapping
servers
slow_log
table_stats
diff --git a/mysql-test/r/events_bugs.result b/mysql-test/r/events_bugs.result
index fe90bd3288b..e3599218aac 100644
--- a/mysql-test/r/events_bugs.result
+++ b/mysql-test/r/events_bugs.result
@@ -569,7 +569,7 @@ USE test;
SHOW GRANTS FOR CURRENT_USER;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
-GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
SET GLOBAL event_scheduler = ON;
CREATE TABLE events_test.event_log
(id int KEY AUTO_INCREMENT, ev_nm char(40), ev_cnt int, ev_tm timestamp)
diff --git a/mysql-test/r/grant.result b/mysql-test/r/grant.result
index c50f6e05621..c711f27264d 100644
--- a/mysql-test/r/grant.result
+++ b/mysql-test/r/grant.result
@@ -56,6 +56,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
show grants for mysqltest_1@localhost;
Grants for mysqltest_1@localhost
GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' REQUIRE CIPHER 'EDH-RSA-DES-CBC3-SHA'
@@ -128,6 +129,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
show grants for mysqltest_1@localhost;
Grants for mysqltest_1@localhost
GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' WITH MAX_QUERIES_PER_HOUR 10
@@ -176,6 +178,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
show grants for mysqltest_1@localhost;
Grants for mysqltest_1@localhost
GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' WITH MAX_QUERIES_PER_HOUR 10 MAX_UPDATES_PER_HOUR 20 MAX_CONNECTIONS_PER_HOUR 30
@@ -750,28 +753,28 @@ show grants for root@localhost;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
GRANT SELECT ON `ÂÄ`.* TO 'root'@'localhost'
-GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
flush privileges;
show grants for root@localhost;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
GRANT SELECT ON `ÂÄ`.* TO 'root'@'localhost'
-GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
drop database ÂÄ;
revoke all privileges on ÂÄ.* from root@localhost;
show grants for root@localhost;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
-GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
set names latin1;
create user mysqltest_7@;
set password for mysqltest_7@ = password('systpass');
show grants for mysqltest_7@;
-Grants for mysqltest_7@
-GRANT USAGE ON *.* TO 'mysqltest_7'@'' IDENTIFIED BY PASSWORD '*2FB071A056F9BB745219D9C876814231DAF46517'
+Grants for mysqltest_7@%
+GRANT USAGE ON *.* TO 'mysqltest_7'@'%' IDENTIFIED BY PASSWORD '*2FB071A056F9BB745219D9C876814231DAF46517'
drop user mysqltest_7@;
show grants for mysqltest_7@;
-ERROR 42000: There is no such grant defined for user 'mysqltest_7' on host ''
+ERROR 42000: There is no such grant defined for user 'mysqltest_7' on host '%'
create database mysqltest;
use mysqltest;
create table t1(f1 int);
@@ -807,30 +810,28 @@ create table t2 as select * from mysql.user where user='';
delete from mysql.user where user='';
flush privileges;
create user mysqltest_8@'';
-create user mysqltest_8;
create user mysqltest_8@host8;
create user mysqltest_8@'';
-ERROR HY000: Operation CREATE USER failed for 'mysqltest_8'@''
+ERROR HY000: Operation CREATE USER failed for 'mysqltest_8'@'%'
create user mysqltest_8;
ERROR HY000: Operation CREATE USER failed for 'mysqltest_8'@'%'
create user mysqltest_8@host8;
ERROR HY000: Operation CREATE USER failed for 'mysqltest_8'@'host8'
select user, QUOTE(host) from mysql.user where user="mysqltest_8";
user QUOTE(host)
-mysqltest_8 ''
mysqltest_8 '%'
mysqltest_8 'host8'
Schema privileges
grant select on mysqltest.* to mysqltest_8@'';
show grants for mysqltest_8@'';
-Grants for mysqltest_8@
-GRANT USAGE ON *.* TO 'mysqltest_8'@''
-GRANT SELECT ON `mysqltest`.* TO 'mysqltest_8'@''
+Grants for mysqltest_8@%
+GRANT USAGE ON *.* TO 'mysqltest_8'@'%'
+GRANT SELECT ON `mysqltest`.* TO 'mysqltest_8'@'%'
grant select on mysqltest.* to mysqltest_8@;
show grants for mysqltest_8@;
-Grants for mysqltest_8@
-GRANT USAGE ON *.* TO 'mysqltest_8'@''
-GRANT SELECT ON `mysqltest`.* TO 'mysqltest_8'@''
+Grants for mysqltest_8@%
+GRANT USAGE ON *.* TO 'mysqltest_8'@'%'
+GRANT SELECT ON `mysqltest`.* TO 'mysqltest_8'@'%'
grant select on mysqltest.* to mysqltest_8;
show grants for mysqltest_8;
Grants for mysqltest_8@%
@@ -840,14 +841,12 @@ select * from information_schema.schema_privileges
where grantee like "'mysqltest_8'%";
GRANTEE TABLE_CATALOG TABLE_SCHEMA PRIVILEGE_TYPE IS_GRANTABLE
'mysqltest_8'@'%' def mysqltest SELECT NO
-'mysqltest_8'@'' def mysqltest SELECT NO
select * from t1;
a
revoke select on mysqltest.* from mysqltest_8@'';
-revoke select on mysqltest.* from mysqltest_8;
show grants for mysqltest_8@'';
-Grants for mysqltest_8@
-GRANT USAGE ON *.* TO 'mysqltest_8'@''
+Grants for mysqltest_8@%
+GRANT USAGE ON *.* TO 'mysqltest_8'@'%'
show grants for mysqltest_8;
Grants for mysqltest_8@%
GRANT USAGE ON *.* TO 'mysqltest_8'@'%'
@@ -856,35 +855,35 @@ where grantee like "'mysqltest_8'%";
GRANTEE TABLE_CATALOG TABLE_SCHEMA PRIVILEGE_TYPE IS_GRANTABLE
flush privileges;
show grants for mysqltest_8@'';
-Grants for mysqltest_8@
-GRANT USAGE ON *.* TO 'mysqltest_8'@''
+Grants for mysqltest_8@%
+GRANT USAGE ON *.* TO 'mysqltest_8'@'%'
show grants for mysqltest_8@;
-Grants for mysqltest_8@
-GRANT USAGE ON *.* TO 'mysqltest_8'@''
+Grants for mysqltest_8@%
+GRANT USAGE ON *.* TO 'mysqltest_8'@'%'
grant select on mysqltest.* to mysqltest_8@'';
flush privileges;
show grants for mysqltest_8@;
-Grants for mysqltest_8@
-GRANT USAGE ON *.* TO 'mysqltest_8'@''
-GRANT SELECT ON `mysqltest`.* TO 'mysqltest_8'@''
+Grants for mysqltest_8@%
+GRANT USAGE ON *.* TO 'mysqltest_8'@'%'
+GRANT SELECT ON `mysqltest`.* TO 'mysqltest_8'@'%'
revoke select on mysqltest.* from mysqltest_8@'';
flush privileges;
Column privileges
grant update (a) on t1 to mysqltest_8@'';
grant update (a) on t1 to mysqltest_8;
show grants for mysqltest_8@'';
-Grants for mysqltest_8@
-GRANT USAGE ON *.* TO 'mysqltest_8'@''
-GRANT UPDATE (a) ON `test`.`t1` TO 'mysqltest_8'@''
+Grants for mysqltest_8@%
+GRANT USAGE ON *.* TO 'mysqltest_8'@'%'
+GRANT UPDATE (a) ON `test`.`t1` TO 'mysqltest_8'@'%'
show grants for mysqltest_8;
Grants for mysqltest_8@%
GRANT USAGE ON *.* TO 'mysqltest_8'@'%'
GRANT UPDATE (a) ON `test`.`t1` TO 'mysqltest_8'@'%'
flush privileges;
show grants for mysqltest_8@'';
-Grants for mysqltest_8@
-GRANT USAGE ON *.* TO 'mysqltest_8'@''
-GRANT UPDATE (a) ON `test`.`t1` TO 'mysqltest_8'@''
+Grants for mysqltest_8@%
+GRANT USAGE ON *.* TO 'mysqltest_8'@'%'
+GRANT UPDATE (a) ON `test`.`t1` TO 'mysqltest_8'@'%'
show grants for mysqltest_8;
Grants for mysqltest_8@%
GRANT USAGE ON *.* TO 'mysqltest_8'@'%'
@@ -892,14 +891,12 @@ GRANT UPDATE (a) ON `test`.`t1` TO 'mysqltest_8'@'%'
select * from information_schema.column_privileges;
GRANTEE TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME PRIVILEGE_TYPE IS_GRANTABLE
'mysqltest_8'@'%' def test t1 a UPDATE NO
-'mysqltest_8'@'' def test t1 a UPDATE NO
select * from t1;
a
revoke update (a) on t1 from mysqltest_8@'';
-revoke update (a) on t1 from mysqltest_8;
show grants for mysqltest_8@'';
-Grants for mysqltest_8@
-GRANT USAGE ON *.* TO 'mysqltest_8'@''
+Grants for mysqltest_8@%
+GRANT USAGE ON *.* TO 'mysqltest_8'@'%'
show grants for mysqltest_8;
Grants for mysqltest_8@%
GRANT USAGE ON *.* TO 'mysqltest_8'@'%'
@@ -907,8 +904,8 @@ select * from information_schema.column_privileges;
GRANTEE TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME PRIVILEGE_TYPE IS_GRANTABLE
flush privileges;
show grants for mysqltest_8@'';
-Grants for mysqltest_8@
-GRANT USAGE ON *.* TO 'mysqltest_8'@''
+Grants for mysqltest_8@%
+GRANT USAGE ON *.* TO 'mysqltest_8'@'%'
show grants for mysqltest_8;
Grants for mysqltest_8@%
GRANT USAGE ON *.* TO 'mysqltest_8'@'%'
@@ -916,18 +913,18 @@ Table privileges
grant update on t1 to mysqltest_8@'';
grant update on t1 to mysqltest_8;
show grants for mysqltest_8@'';
-Grants for mysqltest_8@
-GRANT USAGE ON *.* TO 'mysqltest_8'@''
-GRANT UPDATE ON `test`.`t1` TO 'mysqltest_8'@''
+Grants for mysqltest_8@%
+GRANT USAGE ON *.* TO 'mysqltest_8'@'%'
+GRANT UPDATE ON `test`.`t1` TO 'mysqltest_8'@'%'
show grants for mysqltest_8;
Grants for mysqltest_8@%
GRANT USAGE ON *.* TO 'mysqltest_8'@'%'
GRANT UPDATE ON `test`.`t1` TO 'mysqltest_8'@'%'
flush privileges;
show grants for mysqltest_8@'';
-Grants for mysqltest_8@
-GRANT USAGE ON *.* TO 'mysqltest_8'@''
-GRANT UPDATE ON `test`.`t1` TO 'mysqltest_8'@''
+Grants for mysqltest_8@%
+GRANT USAGE ON *.* TO 'mysqltest_8'@'%'
+GRANT UPDATE ON `test`.`t1` TO 'mysqltest_8'@'%'
show grants for mysqltest_8;
Grants for mysqltest_8@%
GRANT USAGE ON *.* TO 'mysqltest_8'@'%'
@@ -935,14 +932,12 @@ GRANT UPDATE ON `test`.`t1` TO 'mysqltest_8'@'%'
select * from information_schema.table_privileges;
GRANTEE TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PRIVILEGE_TYPE IS_GRANTABLE
'mysqltest_8'@'%' def test t1 UPDATE NO
-'mysqltest_8'@'' def test t1 UPDATE NO
select * from t1;
a
revoke update on t1 from mysqltest_8@'';
-revoke update on t1 from mysqltest_8;
show grants for mysqltest_8@'';
-Grants for mysqltest_8@
-GRANT USAGE ON *.* TO 'mysqltest_8'@''
+Grants for mysqltest_8@%
+GRANT USAGE ON *.* TO 'mysqltest_8'@'%'
show grants for mysqltest_8;
Grants for mysqltest_8@%
GRANT USAGE ON *.* TO 'mysqltest_8'@'%'
@@ -950,8 +945,8 @@ select * from information_schema.table_privileges;
GRANTEE TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PRIVILEGE_TYPE IS_GRANTABLE
flush privileges;
show grants for mysqltest_8@'';
-Grants for mysqltest_8@
-GRANT USAGE ON *.* TO 'mysqltest_8'@''
+Grants for mysqltest_8@%
+GRANT USAGE ON *.* TO 'mysqltest_8'@'%'
show grants for mysqltest_8;
Grants for mysqltest_8@%
GRANT USAGE ON *.* TO 'mysqltest_8'@'%'
@@ -962,45 +957,36 @@ grant update on t1 to mysqltest_8@'';
grant update (a) on t1 to mysqltest_8@'';
grant all privileges on mysqltest.* to mysqltest_8;
show grants for mysqltest_8@'';
-Grants for mysqltest_8@
-GRANT USAGE ON *.* TO 'mysqltest_8'@''
-GRANT ALL PRIVILEGES ON `mysqltest`.* TO 'mysqltest_8'@''
-GRANT UPDATE, UPDATE (a) ON `test`.`t1` TO 'mysqltest_8'@''
+Grants for mysqltest_8@%
+GRANT USAGE ON *.* TO 'mysqltest_8'@'%'
+GRANT ALL PRIVILEGES ON `mysqltest`.* TO 'mysqltest_8'@'%'
+GRANT UPDATE, UPDATE (a) ON `test`.`t1` TO 'mysqltest_8'@'%'
show grants for mysqltest_8;
Grants for mysqltest_8@%
GRANT USAGE ON *.* TO 'mysqltest_8'@'%'
GRANT ALL PRIVILEGES ON `mysqltest`.* TO 'mysqltest_8'@'%'
+GRANT UPDATE, UPDATE (a) ON `test`.`t1` TO 'mysqltest_8'@'%'
select * from information_schema.user_privileges
where grantee like "'mysqltest_8'%";
GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE
'mysqltest_8'@'host8' def USAGE NO
'mysqltest_8'@'%' def USAGE NO
-'mysqltest_8'@'' def USAGE NO
select * from t1;
a
flush privileges;
show grants for mysqltest_8@'';
-Grants for mysqltest_8@
-GRANT USAGE ON *.* TO 'mysqltest_8'@''
-GRANT ALL PRIVILEGES ON `mysqltest`.* TO 'mysqltest_8'@''
-GRANT UPDATE, UPDATE (a) ON `test`.`t1` TO 'mysqltest_8'@''
-show grants for mysqltest_8;
Grants for mysqltest_8@%
GRANT USAGE ON *.* TO 'mysqltest_8'@'%'
GRANT ALL PRIVILEGES ON `mysqltest`.* TO 'mysqltest_8'@'%'
-drop user mysqltest_8@'';
-show grants for mysqltest_8@'';
-ERROR 42000: There is no such grant defined for user 'mysqltest_8' on host ''
+GRANT UPDATE, UPDATE (a) ON `test`.`t1` TO 'mysqltest_8'@'%'
show grants for mysqltest_8;
Grants for mysqltest_8@%
GRANT USAGE ON *.* TO 'mysqltest_8'@'%'
GRANT ALL PRIVILEGES ON `mysqltest`.* TO 'mysqltest_8'@'%'
-select * from information_schema.user_privileges
-where grantee like "'mysqltest_8'%";
-GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE
-'mysqltest_8'@'host8' def USAGE NO
-'mysqltest_8'@'%' def USAGE NO
-drop user mysqltest_8;
+GRANT UPDATE, UPDATE (a) ON `test`.`t1` TO 'mysqltest_8'@'%'
+drop user mysqltest_8@'';
+show grants for mysqltest_8@'';
+ERROR 42000: There is no such grant defined for user 'mysqltest_8' on host '%'
connect(localhost,mysqltest_8,,test,MASTER_PORT,MASTER_SOCKET);
ERROR 28000: Access denied for user 'mysqltest_8'@'localhost' (using password: NO)
show grants for mysqltest_8;
@@ -1699,7 +1685,6 @@ use test;
FLUSH PRIVILEGES without procs_priv table.
RENAME TABLE mysql.procs_priv TO mysql.procs_gone;
FLUSH PRIVILEGES;
-ERROR 42S02: Table 'mysql.procs_priv' doesn't exist
Assigning privileges without procs_priv table.
CREATE DATABASE mysqltest1;
CREATE PROCEDURE mysqltest1.test() SQL SECURITY DEFINER
diff --git a/mysql-test/r/grant2.result b/mysql-test/r/grant2.result
index 9d92abbda07..3df9a5480d3 100644
--- a/mysql-test/r/grant2.result
+++ b/mysql-test/r/grant2.result
@@ -36,7 +36,7 @@ select @@sql_mode;
@@sql_mode
NO_AUTO_CREATE_USER
grant select on `my\_1`.* to mysqltest_4@localhost with grant option;
-ERROR 42000: Can't find any matching row in the user table
+ERROR 28000: Can't find any matching row in the user table
grant select on `my\_1`.* to mysqltest_4@localhost identified by 'mypass'
with grant option;
show grants for mysqltest_1@localhost;
@@ -335,7 +335,7 @@ delete from mysql.user where user like 'mysqltest\_1';
flush privileges;
drop database mysqltest_1;
set password = password("changed");
-ERROR 42000: Can't find any matching row in the user table
+ERROR 42000: You are using MariaDB as an anonymous user and anonymous users are not allowed to change passwords
lock table mysql.user write;
flush privileges;
grant all on *.* to 'mysqltest_1'@'localhost';
diff --git a/mysql-test/r/grant_cache_no_prot.result b/mysql-test/r/grant_cache_no_prot.result
index e95a858fd9a..777eb10cda6 100644
--- a/mysql-test/r/grant_cache_no_prot.result
+++ b/mysql-test/r/grant_cache_no_prot.result
@@ -7,11 +7,11 @@ flush status;
show grants for current_user;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
-GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
show grants;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
-GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
create database if not exists mysqltest;
create table mysqltest.t1 (a int,b int,c int);
create table mysqltest.t2 (a int,b int,c int);
diff --git a/mysql-test/r/grant_cache_ps_prot.result b/mysql-test/r/grant_cache_ps_prot.result
index f9786298a81..b504dc4be35 100644
--- a/mysql-test/r/grant_cache_ps_prot.result
+++ b/mysql-test/r/grant_cache_ps_prot.result
@@ -7,11 +7,11 @@ flush status;
show grants for current_user;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
-GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
show grants;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
-GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
create database if not exists mysqltest;
create table mysqltest.t1 (a int,b int,c int);
create table mysqltest.t2 (a int,b int,c int);
diff --git a/mysql-test/r/information_schema-big.result b/mysql-test/r/information_schema-big.result
index c0016a8e5c3..55ed95f6452 100644
--- a/mysql-test/r/information_schema-big.result
+++ b/mysql-test/r/information_schema-big.result
@@ -19,12 +19,15 @@ c2.column_name LIKE '%SCHEMA%'
)
AND t.table_name NOT LIKE 'innodb%';
table_name column_name
+ALL_PLUGINS PLUGIN_NAME
+APPLICABLE_ROLES GRANTEE
CHARACTER_SETS CHARACTER_SET_NAME
CLIENT_STATISTICS CLIENT
COLLATIONS COLLATION_NAME
COLLATION_CHARACTER_SET_APPLICABILITY COLLATION_NAME
COLUMNS TABLE_SCHEMA
COLUMN_PRIVILEGES TABLE_SCHEMA
+ENABLED_ROLES ROLE_NAME
ENGINES ENGINE
EVENTS EVENT_SCHEMA
FILES TABLE_SCHEMA
@@ -36,7 +39,6 @@ KEY_COLUMN_USAGE CONSTRAINT_SCHEMA
PARAMETERS SPECIFIC_SCHEMA
PARTITIONS TABLE_SCHEMA
PLUGINS PLUGIN_NAME
-ALL_PLUGINS PLUGIN_NAME
PROCESSLIST ID
PROFILING QUERY_ID
REFERENTIAL_CONSTRAINTS CONSTRAINT_SCHEMA
@@ -72,12 +74,15 @@ c2.column_name LIKE '%SCHEMA%'
)
AND t.table_name NOT LIKE 'innodb%';
table_name column_name
+ALL_PLUGINS PLUGIN_NAME
+APPLICABLE_ROLES GRANTEE
CHARACTER_SETS CHARACTER_SET_NAME
CLIENT_STATISTICS CLIENT
COLLATIONS COLLATION_NAME
COLLATION_CHARACTER_SET_APPLICABILITY COLLATION_NAME
COLUMNS TABLE_SCHEMA
COLUMN_PRIVILEGES TABLE_SCHEMA
+ENABLED_ROLES ROLE_NAME
ENGINES ENGINE
EVENTS EVENT_SCHEMA
FILES TABLE_SCHEMA
@@ -89,7 +94,6 @@ KEY_COLUMN_USAGE CONSTRAINT_SCHEMA
PARAMETERS SPECIFIC_SCHEMA
PARTITIONS TABLE_SCHEMA
PLUGINS PLUGIN_NAME
-ALL_PLUGINS PLUGIN_NAME
PROCESSLIST ID
PROFILING QUERY_ID
REFERENTIAL_CONSTRAINTS CONSTRAINT_SCHEMA
diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result
index bf25b1c28ee..39543985005 100644
--- a/mysql-test/r/information_schema.result
+++ b/mysql-test/r/information_schema.result
@@ -47,12 +47,14 @@ table_name not like 'xtradb_%';
select * from v1;
c
ALL_PLUGINS
+APPLICABLE_ROLES
CHARACTER_SETS
CLIENT_STATISTICS
COLLATIONS
COLLATION_CHARACTER_SET_APPLICABILITY
COLUMNS
COLUMN_PRIVILEGES
+ENABLED_ROLES
ENGINES
EVENTS
FILES
@@ -99,6 +101,7 @@ plugin
proc
procs_priv
proxies_priv
+roles_mapping
servers
slow_log
t1
@@ -650,8 +653,8 @@ select TABLE_NAME,TABLE_TYPE,ENGINE
from information_schema.tables
where table_schema='information_schema' limit 2;
TABLE_NAME TABLE_TYPE ENGINE
-CHARACTER_SETS SYSTEM VIEW MEMORY
-CLIENT_STATISTICS SYSTEM VIEW MEMORY
+ALL_PLUGINS SYSTEM VIEW Aria
+APPLICABLE_ROLES SYSTEM VIEW MEMORY
show tables from information_schema like "T%";
Tables_in_information_schema (T%)
TABLES
@@ -706,6 +709,7 @@ max_questions select,insert,update,references
max_connections select,insert,update,references
max_user_connections select,insert,update,references
authentication_string select,insert,update,references
+is_role select,insert,update,references
use test;
create function sub1(i int) returns int
return i+1;
@@ -796,6 +800,7 @@ select table_schema,table_name, column_name from
information_schema.columns
where data_type = 'longtext';
table_schema table_name column_name
+information_schema ALL_PLUGINS PLUGIN_DESCRIPTION
information_schema COLUMNS COLUMN_DEFAULT
information_schema COLUMNS COLUMN_TYPE
information_schema EVENTS EVENT_DEFINITION
@@ -804,7 +809,6 @@ information_schema PARTITIONS PARTITION_EXPRESSION
information_schema PARTITIONS SUBPARTITION_EXPRESSION
information_schema PARTITIONS PARTITION_DESCRIPTION
information_schema PLUGINS PLUGIN_DESCRIPTION
-information_schema ALL_PLUGINS PLUGIN_DESCRIPTION
information_schema PROCESSLIST INFO
information_schema ROUTINES DTD_IDENTIFIER
information_schema ROUTINES ROUTINE_DEFINITION
@@ -1183,7 +1187,7 @@ group by column_type order by num;
column_type group_concat(table_schema, '.', table_name) num
varchar(27) information_schema.COLUMNS 1
varchar(7) information_schema.ROUTINES,information_schema.VIEWS 2
-varchar(20) information_schema.FILES,information_schema.FILES,information_schema.PLUGINS,information_schema.PLUGINS,information_schema.PLUGINS,information_schema.ALL_PLUGINS,information_schema.ALL_PLUGINS,information_schema.ALL_PLUGINS,information_schema.PROFILING 9
+varchar(20) information_schema.ALL_PLUGINS,information_schema.ALL_PLUGINS,information_schema.ALL_PLUGINS,information_schema.FILES,information_schema.FILES,information_schema.PLUGINS,information_schema.PLUGINS,information_schema.PLUGINS,information_schema.PROFILING 9
create table t1(f1 char(1) not null, f2 char(9) not null)
default character set utf8;
select CHARACTER_MAXIMUM_LENGTH, CHARACTER_OCTET_LENGTH from
diff --git a/mysql-test/r/information_schema_all_engines.result b/mysql-test/r/information_schema_all_engines.result
index 3e95d9892cb..ec9027fa32f 100644
--- a/mysql-test/r/information_schema_all_engines.result
+++ b/mysql-test/r/information_schema_all_engines.result
@@ -2,12 +2,14 @@ use INFORMATION_SCHEMA;
show tables;
Tables_in_information_schema
ALL_PLUGINS
+APPLICABLE_ROLES
CHARACTER_SETS
CLIENT_STATISTICS
COLLATIONS
COLLATION_CHARACTER_SET_APPLICABILITY
COLUMNS
COLUMN_PRIVILEGES
+ENABLED_ROLES
ENGINES
EVENTS
FILES
@@ -76,12 +78,14 @@ c2.column_name LIKE '%SCHEMA%'
) order by t.table_name;
table_name column_name
ALL_PLUGINS PLUGIN_NAME
+APPLICABLE_ROLES GRANTEE
CHARACTER_SETS CHARACTER_SET_NAME
CLIENT_STATISTICS CLIENT
COLLATIONS COLLATION_NAME
COLLATION_CHARACTER_SET_APPLICABILITY COLLATION_NAME
COLUMNS TABLE_SCHEMA
COLUMN_PRIVILEGES TABLE_SCHEMA
+ENABLED_ROLES ROLE_NAME
ENGINES ENGINE
EVENTS EVENT_SCHEMA
FILES TABLE_SCHEMA
@@ -150,12 +154,14 @@ c2.column_name LIKE '%SCHEMA%'
) order by t.table_name;
table_name column_name
ALL_PLUGINS PLUGIN_NAME
+APPLICABLE_ROLES GRANTEE
CHARACTER_SETS CHARACTER_SET_NAME
CLIENT_STATISTICS CLIENT
COLLATIONS COLLATION_NAME
COLLATION_CHARACTER_SET_APPLICABILITY COLLATION_NAME
COLUMNS TABLE_SCHEMA
COLUMN_PRIVILEGES TABLE_SCHEMA
+ENABLED_ROLES ROLE_NAME
ENGINES ENGINE
EVENTS EVENT_SCHEMA
FILES TABLE_SCHEMA
@@ -230,6 +236,7 @@ group by c2.column_type order by num limit 1)
group by t.table_name order by num1, t.table_name;
table_name group_concat(t.table_schema, '.', t.table_name) num1
ALL_PLUGINS information_schema.ALL_PLUGINS 1
+APPLICABLE_ROLES information_schema.APPLICABLE_ROLES 1
CHARACTER_SETS information_schema.CHARACTER_SETS 1
CLIENT_STATISTICS information_schema.CLIENT_STATISTICS 1
COLLATIONS information_schema.COLLATIONS 1
@@ -293,12 +300,14 @@ VIEWS information_schema.VIEWS 1
Database: information_schema
| Tables |
| ALL_PLUGINS |
+| APPLICABLE_ROLES |
| CHARACTER_SETS |
| CLIENT_STATISTICS |
| COLLATIONS |
| COLLATION_CHARACTER_SET_APPLICABILITY |
| COLUMNS |
| COLUMN_PRIVILEGES |
+| ENABLED_ROLES |
| ENGINES |
| EVENTS |
| FILES |
@@ -357,12 +366,14 @@ Database: information_schema
Database: INFORMATION_SCHEMA
| Tables |
| ALL_PLUGINS |
+| APPLICABLE_ROLES |
| CHARACTER_SETS |
| CLIENT_STATISTICS |
| COLLATIONS |
| COLLATION_CHARACTER_SET_APPLICABILITY |
| COLUMNS |
| COLUMN_PRIVILEGES |
+| ENABLED_ROLES |
| ENGINES |
| EVENTS |
| FILES |
@@ -423,5 +434,5 @@ Wildcard: inf_rmation_schema
| information_schema |
SELECT table_schema, count(*) FROM information_schema.TABLES WHERE table_schema IN ('mysql', 'INFORMATION_SCHEMA', 'test', 'mysqltest') AND table_name<>'ndb_binlog_index' AND table_name<>'ndb_apply_status' GROUP BY TABLE_SCHEMA;
table_schema count(*)
-information_schema 59
-mysql 27
+information_schema 61
+mysql 28
diff --git a/mysql-test/r/join_cache.result b/mysql-test/r/join_cache.result
index 618ce7e540b..476f3e92caa 100644
--- a/mysql-test/r/join_cache.result
+++ b/mysql-test/r/join_cache.result
@@ -5058,7 +5058,7 @@ EXPLAIN
SELECT * FROM t1,t2 WHERE t1.a < 3 and t2.a IS NULL;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 3 Using where; Using index
-1 SIMPLE t2 hash_range idx #hash#idx:idx 5:5 const 4 Using index condition; Rowid-ordered scan; Using join buffer (flat, BNLH join)
+1 SIMPLE t2 hash_range idx #hash#idx:idx 5:5 const 4 Using index condition; Using where; Rowid-ordered scan; Using join buffer (flat, BNLH join)
SELECT * FROM t1,t2 WHERE t1.a < 3 and t2.a IS NULL;
a a b
1 NULL 10
@@ -5637,4 +5637,51 @@ c
set join_buffer_size=default;
set optimizer_switch=@tmp_optimizer_switch;
DROP table t1,t2,t3;
+#
+# MDEV-5123 Remove duplicated conditions pushed both to join_tab->select_cond and join_tab->cache_select->cond for blocked joins.
+#
+set join_cache_level=default;
+set expensive_subquery_limit=0;
+create table t1 (c1 int);
+create table t2 (c2 int);
+create table t3 (c3 int);
+insert into t1 values (1), (2);
+insert into t2 values (1), (2);
+insert into t3 values (2);
+explain
+select count(*) from t1 straight_join t2
+where c1 = c2-0 and c2 <= (select max(c3) from t3 where c3 = 2 and @counter:=@counter+1);
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 2
+1 PRIMARY t2 ALL NULL NULL NULL NULL 2 Using where; Using join buffer (flat, BNL join)
+2 UNCACHEABLE SUBQUERY t3 system NULL NULL NULL NULL 1
+set @counter=0;
+select count(*) from t1 straight_join t2
+where c1 = c2-0 and c2 <= (select max(c3) from t3 where c3 = 2 and @counter:=@counter+1);
+count(*)
+2
+select @counter;
+@counter
+2
+explain
+select count(*) from t1 straight_join t2
+where c1 = c2-0 and
+c2 <= (select max(c3) from t3 where c3 = 2 and @counter:=@counter+1) and
+c2 / 2 = 1;
+id select_type table type possible_keys key key_len ref rows Extra
+1 PRIMARY t1 ALL NULL NULL NULL NULL 2
+1 PRIMARY t2 ALL NULL NULL NULL NULL 2 Using where; Using join buffer (flat, BNL join)
+2 UNCACHEABLE SUBQUERY t3 system NULL NULL NULL NULL 1
+set @counter=0;
+select count(*) from t1 straight_join t2
+where c1 = c2-0 and
+c2 <= (select max(c3) from t3 where c3 = 2 and @counter:=@counter+1) and
+c2 / 2 = 1;
+count(*)
+1
+select @counter;
+@counter
+2
+drop table t1,t2,t3;
+set expensive_subquery_limit=default;
set @@optimizer_switch=@save_optimizer_switch;
diff --git a/mysql-test/r/limit_rows_examined.result b/mysql-test/r/limit_rows_examined.result
index 5dbe01eef4f..130d17ae270 100644
--- a/mysql-test/r/limit_rows_examined.result
+++ b/mysql-test/r/limit_rows_examined.result
@@ -747,27 +747,27 @@ ERROR HY000: Sort aborted:
SHOW STATUS LIKE 'Handler_read%';
Variable_name Value
Handler_read_first 0
-Handler_read_key 4
+Handler_read_key 5
Handler_read_last 0
Handler_read_next 0
Handler_read_prev 0
Handler_read_rnd 0
Handler_read_rnd_deleted 0
-Handler_read_rnd_next 52
+Handler_read_rnd_next 46
SHOW STATUS LIKE 'Handler_tmp%';
Variable_name Value
Handler_tmp_update 0
-Handler_tmp_write 66
+Handler_tmp_write 70
FLUSH STATUS;
SELECT a AS field1, alias2.d AS field2, alias2.f AS field3, alias2.e AS field4, b AS field5
FROM t1, t2 AS alias2, t2 AS alias3
WHERE alias3.c IN ( SELECT 1 UNION SELECT 6 )
GROUP BY field1, field2, field3, field4, field5
-LIMIT ROWS EXAMINED 250;
+LIMIT ROWS EXAMINED 124;
field1 field2 field3 field4 field5
00:21:38 06:07:10 a 2007-06-08 04:35:26 2007-05-28 00:00:00
Warnings:
-Warning 1931 Query execution was interrupted. The query examined at least 251 rows, which exceeds LIMIT ROWS EXAMINED (250). The query result may be incomplete.
+Warning 1931 Query execution was interrupted. The query examined at least 125 rows, which exceeds LIMIT ROWS EXAMINED (124). The query result may be incomplete.
SHOW STATUS LIKE 'Handler_read%';
Variable_name Value
Handler_read_first 0
@@ -777,11 +777,11 @@ Handler_read_next 0
Handler_read_prev 0
Handler_read_rnd 2
Handler_read_rnd_deleted 1
-Handler_read_rnd_next 110
+Handler_read_rnd_next 47
SHOW STATUS LIKE 'Handler_tmp%';
Variable_name Value
Handler_tmp_update 0
-Handler_tmp_write 133
+Handler_tmp_write 70
drop table t1, t2;
MDEV-161 LIMIT_ROWS EXAMINED: query with the limit and NOT EXISTS, without GROUP BY or aggregate,
diff --git a/mysql-test/r/log_tables_upgrade.result b/mysql-test/r/log_tables_upgrade.result
index 5a53ca03736..6f7bd64eba3 100644
--- a/mysql-test/r/log_tables_upgrade.result
+++ b/mysql-test/r/log_tables_upgrade.result
@@ -38,6 +38,7 @@ mysql.proc OK
mysql.procs_priv OK
mysql.proxies_priv OK
mysql.renamed_general_log OK
+mysql.roles_mapping OK
mysql.servers OK
mysql.table_stats OK
mysql.tables_priv OK
diff --git a/mysql-test/r/mysql_upgrade.result b/mysql-test/r/mysql_upgrade.result
index 74832162afb..06efaf65f5e 100644
--- a/mysql-test/r/mysql_upgrade.result
+++ b/mysql-test/r/mysql_upgrade.result
@@ -25,6 +25,7 @@ mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
mysql.proxies_priv OK
+mysql.roles_mapping OK
mysql.servers OK
mysql.table_stats OK
mysql.tables_priv OK
@@ -67,6 +68,7 @@ mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
mysql.proxies_priv OK
+mysql.roles_mapping OK
mysql.servers OK
mysql.table_stats OK
mysql.tables_priv OK
@@ -109,6 +111,7 @@ mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
mysql.proxies_priv OK
+mysql.roles_mapping OK
mysql.servers OK
mysql.table_stats OK
mysql.tables_priv OK
@@ -153,6 +156,7 @@ mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
mysql.proxies_priv OK
+mysql.roles_mapping OK
mysql.servers OK
mysql.table_stats OK
mysql.tables_priv OK
@@ -201,6 +205,7 @@ mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
mysql.proxies_priv OK
+mysql.roles_mapping OK
mysql.servers OK
mysql.table_stats OK
mysql.tables_priv OK
@@ -252,6 +257,7 @@ mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
mysql.proxies_priv OK
+mysql.roles_mapping OK
mysql.servers OK
mysql.table_stats OK
mysql.tables_priv OK
@@ -306,6 +312,7 @@ mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
mysql.proxies_priv OK
+mysql.roles_mapping OK
mysql.servers OK
mysql.table_stats OK
mysql.tables_priv OK
diff --git a/mysql-test/r/mysql_upgrade_ssl.result b/mysql-test/r/mysql_upgrade_ssl.result
index d0609deb552..87b7d51e8b7 100644
--- a/mysql-test/r/mysql_upgrade_ssl.result
+++ b/mysql-test/r/mysql_upgrade_ssl.result
@@ -27,6 +27,7 @@ mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
mysql.proxies_priv OK
+mysql.roles_mapping OK
mysql.servers OK
mysql.table_stats OK
mysql.tables_priv OK
diff --git a/mysql-test/r/mysqlcheck.result b/mysql-test/r/mysqlcheck.result
index ce9bf367945..81ed26ce96b 100644
--- a/mysql-test/r/mysqlcheck.result
+++ b/mysql-test/r/mysqlcheck.result
@@ -21,6 +21,7 @@ mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
mysql.proxies_priv OK
+mysql.roles_mapping OK
mysql.servers OK
mysql.table_stats OK
mysql.tables_priv OK
@@ -54,6 +55,7 @@ mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
mysql.proxies_priv OK
+mysql.roles_mapping OK
mysql.servers OK
mysql.table_stats OK
mysql.tables_priv OK
@@ -81,6 +83,7 @@ mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
mysql.proxies_priv OK
+mysql.roles_mapping OK
mysql.servers OK
mysql.table_stats OK
mysql.tables_priv OK
@@ -112,6 +115,7 @@ mysql.plugin Table is already up to date
mysql.proc Table is already up to date
mysql.procs_priv Table is already up to date
mysql.proxies_priv Table is already up to date
+mysql.roles_mapping Table is already up to date
mysql.servers Table is already up to date
mysql.table_stats Table is already up to date
mysql.tables_priv Table is already up to date
diff --git a/mysql-test/r/mysqld--help.result b/mysql-test/r/mysqld--help.result
index 08531847c66..0e4808aff4c 100644
--- a/mysql-test/r/mysqld--help.result
+++ b/mysql-test/r/mysqld--help.result
@@ -1185,7 +1185,7 @@ performance-schema-max-rwlock-instances 1724
performance-schema-max-socket-classes 10
performance-schema-max-socket-instances 179
performance-schema-max-stage-classes 150
-performance-schema-max-statement-classes 175
+performance-schema-max-statement-classes 179
performance-schema-max-table-handles 445
performance-schema-max-table-instances 445
performance-schema-max-thread-classes 50
diff --git a/mysql-test/r/plugin_auth.result b/mysql-test/r/plugin_auth.result
index 356ac76962a..ac5eaa4460b 100644
--- a/mysql-test/r/plugin_auth.result
+++ b/mysql-test/r/plugin_auth.result
@@ -36,8 +36,6 @@ USER() CURRENT_USER()
plug@localhost plug_dest@%
## test SET PASSWORD
SET PASSWORD = PASSWORD('plug_dest');
-Warnings:
-Note 1699 SET PASSWORD has no significance for users authenticating via plugins
## test bad credentials
ERROR 28000: Access denied for user 'plug'@'localhost' (using password: YES)
## test bad default plugin : should get CR_AUTH_PLUGIN_CANNOT_LOAD
@@ -116,7 +114,7 @@ ERROR 42000: There is no such grant defined for user 'grant_plug' on host '%'
in grant_plug_dest_con
## testing what an ordinary user can grant
this should fail : no rights to grant all
-GRANT PROXY ON ''@'' TO grant_plug;
+GRANT PROXY ON ''@'%%' TO grant_plug;
ERROR 28000: Access denied for user 'grant_plug_dest'@'localhost'
this should fail : not the same user
GRANT PROXY ON grant_plug TO grant_plug_dest;
@@ -139,15 +137,15 @@ ERROR 42000: You are not allowed to create a user with GRANT
in default connection
# test what root can grant
should work : root has PROXY to all users
-GRANT PROXY ON ''@'' TO grant_plug;
-REVOKE PROXY ON ''@'' FROM grant_plug;
+GRANT PROXY ON ''@'%%' TO grant_plug;
+REVOKE PROXY ON ''@'%%' FROM grant_plug;
should work : root has PROXY to all users
-GRANT PROXY ON ''@'' TO proxy_admin IDENTIFIED BY 'test'
+GRANT PROXY ON ''@'%%' TO proxy_admin IDENTIFIED BY 'test'
WITH GRANT OPTION;
need USAGE : PROXY doesn't contain it.
GRANT USAGE on *.* TO proxy_admin;
in proxy_admin_con;
-should work : proxy_admin has proxy to ''@''
+should work : proxy_admin has proxy to ''@'%%'
GRANT PROXY ON future_user TO grant_plug;
in default connection
SHOW GRANTS FOR grant_plug;
@@ -234,25 +232,25 @@ DROP USER plug_dest;
# Bug #56798 : Wrong credentials assigned when using a proxy user.
#
GRANT ALL PRIVILEGES ON *.* TO power_user;
-GRANT USAGE ON anonymous_db.* TO ''@''
+GRANT USAGE ON anonymous_db.* TO ''@'%%'
IDENTIFIED WITH 'test_plugin_server' AS 'power_user';
-GRANT PROXY ON power_user TO ''@'';
+GRANT PROXY ON power_user TO ''@'%%';
CREATE DATABASE confidential_db;
SELECT user(),current_user(),@@proxy_user;
user() current_user() @@proxy_user
-test_login_user@localhost power_user@% ''@''
+test_login_user@localhost power_user@% ''@'%%'
DROP USER power_user;
-DROP USER ''@'';
+DROP USER ''@'%%';
DROP DATABASE confidential_db;
# Test case #2 (crash with double grant proxy)
-CREATE USER ''@'' IDENTIFIED WITH 'test_plugin_server' AS 'standard_user';
+CREATE USER ''@'%%' IDENTIFIED WITH 'test_plugin_server' AS 'standard_user';
CREATE USER standard_user;
CREATE DATABASE shared;
GRANT ALL PRIVILEGES ON shared.* TO standard_user;
-GRANT PROXY ON standard_user TO ''@'';
+GRANT PROXY ON standard_user TO ''@'%%';
#should not crash
-GRANT PROXY ON standard_user TO ''@'';
-DROP USER ''@'';
+GRANT PROXY ON standard_user TO ''@'%%';
+DROP USER ''@'%%';
DROP USER standard_user;
DROP DATABASE shared;
#
@@ -426,8 +424,6 @@ SELECT USER(),CURRENT_USER();
USER() CURRENT_USER()
bug12818542@localhost bug12818542_dest@localhost
SET PASSWORD = PASSWORD('bruhaha');
-Warnings:
-Note 1699 SET PASSWORD has no significance for users authenticating via plugins
SELECT USER(),CURRENT_USER();
USER() CURRENT_USER()
bug12818542@localhost bug12818542_dest@localhost
diff --git a/mysql-test/r/plugin_auth_qa.result b/mysql-test/r/plugin_auth_qa.result
index 72d21994792..b86a01f6b52 100644
--- a/mysql-test/r/plugin_auth_qa.result
+++ b/mysql-test/r/plugin_auth_qa.result
@@ -303,7 +303,7 @@ grant_user test_plugin_server plug_dest
CREATE USER plug_dest;
DROP USER plug_dest;
GRANT ALL PRIVILEGES ON test_user_db.* TO plug_dest;
-ERROR 42000: Can't find any matching row in the user table
+ERROR 28000: Can't find any matching row in the user table
DROP USER grant_user;
GRANT INSERT ON test_user_db.* TO grant_user IDENTIFIED WITH test_plugin_server AS 'plug_dest';
SELECT user,plugin,authentication_string FROM mysql.user WHERE user != 'root';
diff --git a/mysql-test/r/plugin_auth_qa_1.result b/mysql-test/r/plugin_auth_qa_1.result
index d700be36ac0..44b5a88de1e 100644
--- a/mysql-test/r/plugin_auth_qa_1.result
+++ b/mysql-test/r/plugin_auth_qa_1.result
@@ -186,7 +186,7 @@ connection default;
disconnect plug_user;
DROP USER new_user,new_dest,plug_dest;
========== test 2, 2.1, 2.2 ================================
-CREATE USER ''@'' IDENTIFIED WITH test_plugin_server AS 'proxied_user';
+CREATE USER ''@'%%' IDENTIFIED WITH test_plugin_server AS 'proxied_user';
CREATE USER proxied_user IDENTIFIED BY 'proxied_user_passwd';
SELECT user,plugin,authentication_string FROM mysql.user WHERE user != 'root';
user plugin authentication_string
@@ -204,7 +204,7 @@ connection default;
disconnect proxy_con;
connect(proxy_con,localhost,proxy_user,proxied_user);
ERROR 28000: Access denied for user 'proxy_user'@'localhost' (using password: YES)
-GRANT PROXY ON proxied_user TO ''@'';
+GRANT PROXY ON proxied_user TO ''@'%%';
connect(proxy_con,localhost,proxied_user,proxied_user_passwd);
SELECT USER(),CURRENT_USER();
USER() CURRENT_USER()
@@ -218,11 +218,11 @@ proxy_user@localhost proxied_user@%
========== test 2.2.1 ======================================
SELECT @@proxy_user;
@@proxy_user
-''@''
+''@'%%'
connection default;
disconnect proxy_con;
-DROP USER ''@'',proxied_user;
-GRANT ALL PRIVILEGES ON test_user_db.* TO ''@''
+DROP USER ''@'%%',proxied_user;
+GRANT ALL PRIVILEGES ON test_user_db.* TO ''@'%%'
IDENTIFIED WITH test_plugin_server AS 'proxied_user';
CREATE USER proxied_user IDENTIFIED BY 'proxied_user_passwd';
SELECT user,plugin,authentication_string FROM mysql.user WHERE user != 'root';
@@ -240,7 +240,7 @@ connection default;
disconnect proxy_con;
connect(proxy_con,localhost,proxy_user,proxied_user);
ERROR 28000: Access denied for user 'proxy_user'@'localhost' (using password: YES)
-GRANT PROXY ON proxied_user TO ''@'';
+GRANT PROXY ON proxied_user TO ''@'%%';
connect(proxy_con,localhost,proxied_user,proxied_user_passwd);
SELECT USER(),CURRENT_USER();
USER() CURRENT_USER()
@@ -253,21 +253,21 @@ USER() CURRENT_USER()
proxy_user@localhost proxied_user@%
SELECT @@proxy_user;
@@proxy_user
-''@''
+''@'%%'
connection default;
disconnect proxy_con;
-DROP USER ''@'',proxied_user;
-CREATE USER ''@'' IDENTIFIED WITH test_plugin_server AS 'proxied_user';
+DROP USER ''@'%%',proxied_user;
+CREATE USER ''@'%%' IDENTIFIED WITH test_plugin_server AS 'proxied_user';
CREATE USER proxied_user_1 IDENTIFIED BY 'proxied_user_1_pwd';
CREATE USER proxied_user_2 IDENTIFIED BY 'proxied_user_2_pwd';
CREATE USER proxied_user_3 IDENTIFIED BY 'proxied_user_3_pwd';
CREATE USER proxied_user_4 IDENTIFIED BY 'proxied_user_4_pwd';
CREATE USER proxied_user_5 IDENTIFIED BY 'proxied_user_5_pwd';
-GRANT PROXY ON proxied_user_1 TO ''@'';
-GRANT PROXY ON proxied_user_2 TO ''@'';
-GRANT PROXY ON proxied_user_3 TO ''@'';
-GRANT PROXY ON proxied_user_4 TO ''@'';
-GRANT PROXY ON proxied_user_5 TO ''@'';
+GRANT PROXY ON proxied_user_1 TO ''@'%%';
+GRANT PROXY ON proxied_user_2 TO ''@'%%';
+GRANT PROXY ON proxied_user_3 TO ''@'%%';
+GRANT PROXY ON proxied_user_4 TO ''@'%%';
+GRANT PROXY ON proxied_user_5 TO ''@'%%';
SELECT user,plugin,authentication_string FROM mysql.user WHERE user != 'root';
user plugin authentication_string
test_plugin_server proxied_user
@@ -322,7 +322,7 @@ disconnect proxy_con_2;
disconnect proxy_con_3;
disconnect proxy_con_4;
disconnect proxy_con_5;
-DROP USER ''@'',proxied_user_1,proxied_user_2,proxied_user_3,proxied_user_4,proxied_user_5;
+DROP USER ''@'%%',proxied_user_1,proxied_user_2,proxied_user_3,proxied_user_4,proxied_user_5;
========== test 3 ==========================================
GRANT ALL PRIVILEGES ON *.* TO plug_user
IDENTIFIED WITH test_plugin_server AS 'plug_dest';
diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result
index f6a2a16f038..50fb5e1e55b 100644
--- a/mysql-test/r/ps.result
+++ b/mysql-test/r/ps.result
@@ -1201,13 +1201,13 @@ SET @aux= "SELECT COUNT(*)
prepare my_stmt from @aux;
execute my_stmt;
COUNT(*)
-43
+44
execute my_stmt;
COUNT(*)
-43
+44
execute my_stmt;
COUNT(*)
-43
+44
deallocate prepare my_stmt;
drop procedure if exists p1|
drop table if exists t1|
diff --git a/mysql-test/r/range.result b/mysql-test/r/range.result
index d41bfaa2c67..e169dcb40b0 100644
--- a/mysql-test/r/range.result
+++ b/mysql-test/r/range.result
@@ -221,27 +221,27 @@ update t1 set y=x;
explain select * from t1, t1 t2 where t1.y = 8 and t2.x between 7 and t1.y+0;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref y y 5 const 1
-1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Using join buffer (flat, BNL join)
+1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Using where; Using join buffer (flat, BNL join)
explain select * from t1, t1 t2 where t1.y = 8 and t2.x >= 7 and t2.x <= t1.y+0;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref y y 5 const 1
-1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Using join buffer (flat, BNL join)
+1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Using where; Using join buffer (flat, BNL join)
explain select * from t1, t1 t2 where t1.y = 2 and t2.x between t1.y-1 and t1.y+1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref y y 5 const 1
-1 SIMPLE t2 range x x 5 NULL 3 Using index condition; Using join buffer (flat, BNL join)
+1 SIMPLE t2 range x x 5 NULL 3 Using index condition; Using where; Using join buffer (flat, BNL join)
explain select * from t1, t1 t2 where t1.y = 2 and t2.x >= t1.y-1 and t2.x <= t1.y+1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref y y 5 const 1
-1 SIMPLE t2 range x x 5 NULL 3 Using index condition; Using join buffer (flat, BNL join)
+1 SIMPLE t2 range x x 5 NULL 3 Using index condition; Using where; Using join buffer (flat, BNL join)
explain select * from t1, t1 t2 where t1.y = 2 and t2.x between 0 and t1.y;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref y y 5 const 1
-1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Using join buffer (flat, BNL join)
+1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Using where; Using join buffer (flat, BNL join)
explain select * from t1, t1 t2 where t1.y = 2 and t2.x >= 0 and t2.x <= t1.y;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref y y 5 const 1
-1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Using join buffer (flat, BNL join)
+1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Using where; Using join buffer (flat, BNL join)
explain select count(*) from t1 where x in (1);
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref x x 5 const 1 Using index
diff --git a/mysql-test/r/range_mrr_icp.result b/mysql-test/r/range_mrr_icp.result
index a5c14d99975..0d6bfc2467b 100644
--- a/mysql-test/r/range_mrr_icp.result
+++ b/mysql-test/r/range_mrr_icp.result
@@ -223,27 +223,27 @@ update t1 set y=x;
explain select * from t1, t1 t2 where t1.y = 8 and t2.x between 7 and t1.y+0;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref y y 5 const 1
-1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Rowid-ordered scan; Using join buffer (flat, BNL join)
+1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Using where; Rowid-ordered scan; Using join buffer (flat, BNL join)
explain select * from t1, t1 t2 where t1.y = 8 and t2.x >= 7 and t2.x <= t1.y+0;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref y y 5 const 1
-1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Rowid-ordered scan; Using join buffer (flat, BNL join)
+1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Using where; Rowid-ordered scan; Using join buffer (flat, BNL join)
explain select * from t1, t1 t2 where t1.y = 2 and t2.x between t1.y-1 and t1.y+1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref y y 5 const 1
-1 SIMPLE t2 range x x 5 NULL 3 Using index condition; Rowid-ordered scan; Using join buffer (flat, BNL join)
+1 SIMPLE t2 range x x 5 NULL 3 Using index condition; Using where; Rowid-ordered scan; Using join buffer (flat, BNL join)
explain select * from t1, t1 t2 where t1.y = 2 and t2.x >= t1.y-1 and t2.x <= t1.y+1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref y y 5 const 1
-1 SIMPLE t2 range x x 5 NULL 3 Using index condition; Rowid-ordered scan; Using join buffer (flat, BNL join)
+1 SIMPLE t2 range x x 5 NULL 3 Using index condition; Using where; Rowid-ordered scan; Using join buffer (flat, BNL join)
explain select * from t1, t1 t2 where t1.y = 2 and t2.x between 0 and t1.y;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref y y 5 const 1
-1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Rowid-ordered scan; Using join buffer (flat, BNL join)
+1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Using where; Rowid-ordered scan; Using join buffer (flat, BNL join)
explain select * from t1, t1 t2 where t1.y = 2 and t2.x >= 0 and t2.x <= t1.y;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref y y 5 const 1
-1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Rowid-ordered scan; Using join buffer (flat, BNL join)
+1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Using where; Rowid-ordered scan; Using join buffer (flat, BNL join)
explain select count(*) from t1 where x in (1);
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ref x x 5 const 1 Using index
diff --git a/mysql-test/r/skip_grants.result b/mysql-test/r/skip_grants.result
index cd691d882fa..b178e0ecfa7 100644
--- a/mysql-test/r/skip_grants.result
+++ b/mysql-test/r/skip_grants.result
@@ -36,14 +36,14 @@ CREATE DEFINER=a@'' FUNCTION f3() RETURNS INT
RETURN 3;
SHOW CREATE VIEW v3;
View Create View character_set_client collation_connection
-v3 CREATE ALGORITHM=UNDEFINED DEFINER=`a`@`` SQL SECURITY DEFINER VIEW `v3` AS select `t1`.`c` AS `c` from `t1` latin1 latin1_swedish_ci
+v3 CREATE ALGORITHM=UNDEFINED DEFINER=`a`@`%` SQL SECURITY DEFINER VIEW `v3` AS select `t1`.`c` AS `c` from `t1` latin1 latin1_swedish_ci
SHOW CREATE PROCEDURE p3;
Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation
-p3 CREATE DEFINER=`a`@`` PROCEDURE `p3`()
+p3 CREATE DEFINER=`a`@`%` PROCEDURE `p3`()
SELECT 3 latin1 latin1_swedish_ci latin1_swedish_ci
SHOW CREATE FUNCTION f3;
Function sql_mode Create Function character_set_client collation_connection Database Collation
-f3 CREATE DEFINER=`a`@`` FUNCTION `f3`() RETURNS int(11)
+f3 CREATE DEFINER=`a`@`%` FUNCTION `f3`() RETURNS int(11)
RETURN 3 latin1 latin1_swedish_ci latin1_swedish_ci
DROP TRIGGER t1_bi;
DROP TRIGGER ti_ai;
diff --git a/mysql-test/r/sp_notembedded.result b/mysql-test/r/sp_notembedded.result
index e95bae39920..410441b63e3 100644
--- a/mysql-test/r/sp_notembedded.result
+++ b/mysql-test/r/sp_notembedded.result
@@ -9,11 +9,11 @@ end|
call bug4902()|
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
-GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
call bug4902()|
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
-GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
drop procedure bug4902|
drop procedure if exists bug4902_2|
create procedure bug4902_2()
@@ -156,11 +156,11 @@ create procedure 15298_2 () sql security definer show grants;
call 15298_1();
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
-GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
call 15298_2();
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
-GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
drop user mysqltest_1@localhost;
drop procedure 15298_1;
drop procedure 15298_2;
diff --git a/mysql-test/r/subselect_mat.result b/mysql-test/r/subselect_mat.result
index e1c9df3b00b..05635dd0985 100644
--- a/mysql-test/r/subselect_mat.result
+++ b/mysql-test/r/subselect_mat.result
@@ -1474,7 +1474,7 @@ EXPLAIN SELECT pk FROM t1 WHERE (a) IN (SELECT a FROM t2 WHERE pk > 0);
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 2
1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1
-2 MATERIALIZED t2 range PRIMARY PRIMARY 4 NULL 2 Using index condition; Rowid-ordered scan
+2 MATERIALIZED t2 range PRIMARY PRIMARY 4 NULL 2 Using index condition; Using where; Rowid-ordered scan
SELECT pk FROM t1 WHERE (a) IN (SELECT a FROM t2 WHERE pk > 0);
pk
2
@@ -2158,7 +2158,7 @@ EXPLAIN SELECT pk FROM t1 WHERE (a) IN (SELECT a FROM t2 WHERE pk > 0);
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 2
1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1
-2 MATERIALIZED t2 range PRIMARY PRIMARY 4 NULL 2 Using index condition
+2 MATERIALIZED t2 range PRIMARY PRIMARY 4 NULL 2 Using index condition; Using where
SELECT pk FROM t1 WHERE (a) IN (SELECT a FROM t2 WHERE pk > 0);
pk
2
diff --git a/mysql-test/r/subselect_sj.result b/mysql-test/r/subselect_sj.result
index 51e5c5e54d0..b8040731cb5 100644
--- a/mysql-test/r/subselect_sj.result
+++ b/mysql-test/r/subselect_sj.result
@@ -803,7 +803,7 @@ EXPLAIN EXTENDED SELECT pk FROM t1 WHERE (a, b) IN (SELECT a, b FROM t2 WHERE pk
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 2 100.00
1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 11 func,func 1 100.00
-2 MATERIALIZED t2 range PRIMARY PRIMARY 4 NULL 2 100.00 Using index condition; Rowid-ordered scan
+2 MATERIALIZED t2 range PRIMARY PRIMARY 4 NULL 2 100.00 Using index condition; Using where; Rowid-ordered scan
Warnings:
Note 1003 select `test`.`t1`.`pk` AS `pk` from `test`.`t1` semi join (`test`.`t2`) where ((`test`.`t2`.`pk` > 0))
SELECT pk FROM t1 WHERE (a, b) IN (SELECT a, b FROM t2 WHERE pk > 0);
diff --git a/mysql-test/r/subselect_sj_jcl6.result b/mysql-test/r/subselect_sj_jcl6.result
index 73777dd71c3..a42fc1efaad 100644
--- a/mysql-test/r/subselect_sj_jcl6.result
+++ b/mysql-test/r/subselect_sj_jcl6.result
@@ -816,7 +816,7 @@ EXPLAIN EXTENDED SELECT pk FROM t1 WHERE (a, b) IN (SELECT a, b FROM t2 WHERE pk
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 2 100.00
1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 11 func,func 1 100.00
-2 MATERIALIZED t2 range PRIMARY PRIMARY 4 NULL 2 100.00 Using index condition; Rowid-ordered scan
+2 MATERIALIZED t2 range PRIMARY PRIMARY 4 NULL 2 100.00 Using index condition; Using where; Rowid-ordered scan
Warnings:
Note 1003 select `test`.`t1`.`pk` AS `pk` from `test`.`t1` semi join (`test`.`t2`) where ((`test`.`t2`.`pk` > 0))
SELECT pk FROM t1 WHERE (a, b) IN (SELECT a, b FROM t2 WHERE pk > 0);
diff --git a/mysql-test/r/subselect_sj_mat.result b/mysql-test/r/subselect_sj_mat.result
index bcdd82b790c..15cb9eeaa0e 100644
--- a/mysql-test/r/subselect_sj_mat.result
+++ b/mysql-test/r/subselect_sj_mat.result
@@ -1511,7 +1511,7 @@ EXPLAIN SELECT pk FROM t1 WHERE (a) IN (SELECT a FROM t2 WHERE pk > 0);
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY t1 ALL NULL NULL NULL NULL 2
1 PRIMARY <subquery2> eq_ref distinct_key distinct_key 4 func 1
-2 MATERIALIZED t2 range PRIMARY PRIMARY 4 NULL 2 Using index condition; Rowid-ordered scan
+2 MATERIALIZED t2 range PRIMARY PRIMARY 4 NULL 2 Using index condition; Using where; Rowid-ordered scan
SELECT pk FROM t1 WHERE (a) IN (SELECT a FROM t2 WHERE pk > 0);
pk
2
diff --git a/mysql-test/r/system_mysql_db.result b/mysql-test/r/system_mysql_db.result
index eda11d95b22..fa9195e705b 100644
--- a/mysql-test/r/system_mysql_db.result
+++ b/mysql-test/r/system_mysql_db.result
@@ -19,6 +19,7 @@ plugin
proc
procs_priv
proxies_priv
+roles_mapping
servers
slow_log
table_stats
@@ -128,6 +129,7 @@ user CREATE TABLE `user` (
`plugin` char(64) CHARACTER SET latin1 NOT NULL DEFAULT '',
`authentication_string` text COLLATE utf8_bin NOT NULL,
`password_expired` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
+ `is_role` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
PRIMARY KEY (`Host`,`User`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Users and global privileges'
show create table func;
diff --git a/mysql-test/r/system_mysql_db_fix40123.result b/mysql-test/r/system_mysql_db_fix40123.result
index 71e94a7432d..0d6349a9b2f 100644
--- a/mysql-test/r/system_mysql_db_fix40123.result
+++ b/mysql-test/r/system_mysql_db_fix40123.result
@@ -19,6 +19,7 @@ plugin
proc
procs_priv
proxies_priv
+roles_mapping
servers
slow_log
table_stats
@@ -125,6 +126,7 @@ user CREATE TABLE `user` (
`max_updates` int(11) unsigned NOT NULL DEFAULT '0',
`max_connections` int(11) unsigned NOT NULL DEFAULT '0',
`max_user_connections` int(11) NOT NULL DEFAULT '0',
+ `is_role` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`plugin` char(64) CHARACTER SET latin1 NOT NULL DEFAULT '',
`authentication_string` text COLLATE utf8_bin NOT NULL,
`password_expired` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
diff --git a/mysql-test/r/system_mysql_db_fix50030.result b/mysql-test/r/system_mysql_db_fix50030.result
index 71e94a7432d..0d6349a9b2f 100644
--- a/mysql-test/r/system_mysql_db_fix50030.result
+++ b/mysql-test/r/system_mysql_db_fix50030.result
@@ -19,6 +19,7 @@ plugin
proc
procs_priv
proxies_priv
+roles_mapping
servers
slow_log
table_stats
@@ -125,6 +126,7 @@ user CREATE TABLE `user` (
`max_updates` int(11) unsigned NOT NULL DEFAULT '0',
`max_connections` int(11) unsigned NOT NULL DEFAULT '0',
`max_user_connections` int(11) NOT NULL DEFAULT '0',
+ `is_role` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`plugin` char(64) CHARACTER SET latin1 NOT NULL DEFAULT '',
`authentication_string` text COLLATE utf8_bin NOT NULL,
`password_expired` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
diff --git a/mysql-test/r/system_mysql_db_fix50117.result b/mysql-test/r/system_mysql_db_fix50117.result
index 71e94a7432d..0d6349a9b2f 100644
--- a/mysql-test/r/system_mysql_db_fix50117.result
+++ b/mysql-test/r/system_mysql_db_fix50117.result
@@ -19,6 +19,7 @@ plugin
proc
procs_priv
proxies_priv
+roles_mapping
servers
slow_log
table_stats
@@ -125,6 +126,7 @@ user CREATE TABLE `user` (
`max_updates` int(11) unsigned NOT NULL DEFAULT '0',
`max_connections` int(11) unsigned NOT NULL DEFAULT '0',
`max_user_connections` int(11) NOT NULL DEFAULT '0',
+ `is_role` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
`plugin` char(64) CHARACTER SET latin1 NOT NULL DEFAULT '',
`authentication_string` text COLLATE utf8_bin NOT NULL,
`password_expired` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
diff --git a/mysql-test/r/trigger_notembedded.result b/mysql-test/r/trigger_notembedded.result
index 0597a52a674..ef86e4c496c 100644
--- a/mysql-test/r/trigger_notembedded.result
+++ b/mysql-test/r/trigger_notembedded.result
@@ -162,7 +162,7 @@ SELECT trigger_name, definer FROM INFORMATION_SCHEMA.TRIGGERS ORDER BY trigger_n
trigger_name definer
trg1
trg2 @
-trg3 @abc@def@@
+trg3 @abc@def@@%
trg4 @hostname
trg5 @abcdef@@@hostname
@@ -170,7 +170,7 @@ SELECT * FROM INFORMATION_SCHEMA.TRIGGERS ORDER BY trigger_name;
TRIGGER_CATALOG TRIGGER_SCHEMA TRIGGER_NAME EVENT_MANIPULATION EVENT_OBJECT_CATALOG EVENT_OBJECT_SCHEMA EVENT_OBJECT_TABLE ACTION_ORDER ACTION_CONDITION ACTION_STATEMENT ACTION_ORIENTATION ACTION_TIMING ACTION_REFERENCE_OLD_TABLE ACTION_REFERENCE_NEW_TABLE ACTION_REFERENCE_OLD_ROW ACTION_REFERENCE_NEW_ROW CREATED SQL_MODE DEFINER CHARACTER_SET_CLIENT COLLATION_CONNECTION DATABASE_COLLATION
def mysqltest_db1 trg1 INSERT def mysqltest_db1 t1 0 NULL SET @a = 1 ROW BEFORE NULL NULL OLD NEW NULL latin1 latin1_swedish_ci latin1_swedish_ci
def mysqltest_db1 trg2 INSERT def mysqltest_db1 t1 0 NULL SET @a = 2 ROW AFTER NULL NULL OLD NEW NULL @ latin1 latin1_swedish_ci latin1_swedish_ci
-def mysqltest_db1 trg3 UPDATE def mysqltest_db1 t1 0 NULL SET @a = 3 ROW BEFORE NULL NULL OLD NEW NULL @abc@def@@ latin1 latin1_swedish_ci latin1_swedish_ci
+def mysqltest_db1 trg3 UPDATE def mysqltest_db1 t1 0 NULL SET @a = 3 ROW BEFORE NULL NULL OLD NEW NULL @abc@def@@% latin1 latin1_swedish_ci latin1_swedish_ci
def mysqltest_db1 trg4 UPDATE def mysqltest_db1 t1 0 NULL SET @a = 4 ROW AFTER NULL NULL OLD NEW NULL @hostname latin1 latin1_swedish_ci latin1_swedish_ci
def mysqltest_db1 trg5 DELETE def mysqltest_db1 t1 0 NULL SET @a = 5 ROW BEFORE NULL NULL OLD NEW NULL @abcdef@@@hostname latin1 latin1_swedish_ci latin1_swedish_ci
diff --git a/mysql-test/r/view_grant.result b/mysql-test/r/view_grant.result
index 52e8cd54695..85e64078daf 100644
--- a/mysql-test/r/view_grant.result
+++ b/mysql-test/r/view_grant.result
@@ -523,13 +523,13 @@ drop user mysqltest_1@localhost;
drop database mysqltest;
create definer=some_user@`` sql security invoker view v1 as select 1;
Warnings:
-Note 1449 The user specified as a definer ('some_user'@'') does not exist
+Note 1449 The user specified as a definer ('some_user'@'%') does not exist
create definer=some_user@localhost sql security invoker view v2 as select 1;
Warnings:
Note 1449 The user specified as a definer ('some_user'@'localhost') does not exist
show create view v1;
View Create View character_set_client collation_connection
-v1 CREATE ALGORITHM=UNDEFINED DEFINER=`some_user`@`` SQL SECURITY INVOKER VIEW `v1` AS select 1 AS `1` latin1 latin1_swedish_ci
+v1 CREATE ALGORITHM=UNDEFINED DEFINER=`some_user`@`%` SQL SECURITY INVOKER VIEW `v1` AS select 1 AS `1` latin1 latin1_swedish_ci
show create view v2;
View Create View character_set_client collation_connection
v2 CREATE ALGORITHM=UNDEFINED DEFINER=`some_user`@`localhost` SQL SECURITY INVOKER VIEW `v2` AS select 1 AS `1` latin1 latin1_swedish_ci
diff --git a/mysql-test/suite/federated/federatedx.test b/mysql-test/suite/federated/federatedx.test
index cfe6a425944..818cc2c1681 100644
--- a/mysql-test/suite/federated/federatedx.test
+++ b/mysql-test/suite/federated/federatedx.test
@@ -4,8 +4,8 @@
#
---source include/federated.inc
--source have_federatedx.inc
+--source include/federated.inc
connection default;
diff --git a/mysql-test/suite/federated/have_federatedx.opt b/mysql-test/suite/federated/have_federatedx.opt
index 05d01b26b29..2c34f634517 100644
--- a/mysql-test/suite/federated/have_federatedx.opt
+++ b/mysql-test/suite/federated/have_federatedx.opt
@@ -1 +1 @@
---plugin-load-add=$HA_FEDERATEDX_SO --federated
+--plugin-load-add=$HA_FEDERATEDX_SO --loose-federated
diff --git a/mysql-test/suite/funcs_1/r/innodb_trig_03e.result b/mysql-test/suite/funcs_1/r/innodb_trig_03e.result
index 5dfc734cf5c..cf93657885c 100644
--- a/mysql-test/suite/funcs_1/r/innodb_trig_03e.result
+++ b/mysql-test/suite/funcs_1/r/innodb_trig_03e.result
@@ -573,7 +573,7 @@ root@localhost
show grants;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
-GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
drop trigger trg1_1;
use priv_db;
diff --git a/mysql-test/suite/funcs_1/r/is_column_privileges.result b/mysql-test/suite/funcs_1/r/is_column_privileges.result
index edf0a401697..a57970dbcaa 100644
--- a/mysql-test/suite/funcs_1/r/is_column_privileges.result
+++ b/mysql-test/suite/funcs_1/r/is_column_privileges.result
@@ -28,7 +28,7 @@ DROP FUNCTION test.f1;
#########################################################################
DESCRIBE information_schema.COLUMN_PRIVILEGES;
Field Type Null Key Default Extra
-GRANTEE varchar(81) NO
+GRANTEE varchar(190) NO
TABLE_CATALOG varchar(512) NO
TABLE_SCHEMA varchar(64) NO
TABLE_NAME varchar(64) NO
@@ -38,7 +38,7 @@ IS_GRANTABLE varchar(3) NO
SHOW CREATE TABLE information_schema.COLUMN_PRIVILEGES;
Table Create Table
COLUMN_PRIVILEGES CREATE TEMPORARY TABLE `COLUMN_PRIVILEGES` (
- `GRANTEE` varchar(81) NOT NULL DEFAULT '',
+ `GRANTEE` varchar(190) NOT NULL DEFAULT '',
`TABLE_CATALOG` varchar(512) NOT NULL DEFAULT '',
`TABLE_SCHEMA` varchar(64) NOT NULL DEFAULT '',
`TABLE_NAME` varchar(64) NOT NULL DEFAULT '',
@@ -48,7 +48,7 @@ COLUMN_PRIVILEGES CREATE TEMPORARY TABLE `COLUMN_PRIVILEGES` (
) ENGINE=MEMORY DEFAULT CHARSET=utf8
SHOW COLUMNS FROM information_schema.COLUMN_PRIVILEGES;
Field Type Null Key Default Extra
-GRANTEE varchar(81) NO
+GRANTEE varchar(190) NO
TABLE_CATALOG varchar(512) NO
TABLE_SCHEMA varchar(64) NO
TABLE_NAME varchar(64) NO
diff --git a/mysql-test/suite/funcs_1/r/is_columns_is.result b/mysql-test/suite/funcs_1/r/is_columns_is.result
index a875d16b54a..0fb2b8988df 100644
--- a/mysql-test/suite/funcs_1/r/is_columns_is.result
+++ b/mysql-test/suite/funcs_1/r/is_columns_is.result
@@ -16,6 +16,9 @@ def information_schema ALL_PLUGINS PLUGIN_STATUS 3 NO varchar 16 48 NULL NULL N
def information_schema ALL_PLUGINS PLUGIN_TYPE 4 NO varchar 80 240 NULL NULL NULL utf8 utf8_general_ci varchar(80) select
def information_schema ALL_PLUGINS PLUGIN_TYPE_VERSION 5 NO varchar 20 60 NULL NULL NULL utf8 utf8_general_ci varchar(20) select
def information_schema ALL_PLUGINS PLUGIN_VERSION 2 NO varchar 20 60 NULL NULL NULL utf8 utf8_general_ci varchar(20) select
+def information_schema APPLICABLE_ROLES GRANTEE 1 NO varchar 190 570 NULL NULL NULL utf8 utf8_general_ci varchar(190) select
+def information_schema APPLICABLE_ROLES IS_GRANTABLE 3 NO varchar 3 9 NULL NULL NULL utf8 utf8_general_ci varchar(3) select
+def information_schema APPLICABLE_ROLES ROLE_NAME 2 NO varchar 128 384 NULL NULL NULL utf8 utf8_general_ci varchar(128) select
def information_schema CHARACTER_SETS CHARACTER_SET_NAME 1 NO varchar 32 96 NULL NULL NULL utf8 utf8_general_ci varchar(32) select
def information_schema CHARACTER_SETS DEFAULT_COLLATE_NAME 2 NO varchar 32 96 NULL NULL NULL utf8 utf8_general_ci varchar(32) select
def information_schema CHARACTER_SETS DESCRIPTION 3 NO varchar 60 180 NULL NULL NULL utf8 utf8_general_ci varchar(60) select
@@ -72,12 +75,13 @@ def information_schema COLUMNS TABLE_CATALOG 1 NO varchar 512 1536 NULL NULL NU
def information_schema COLUMNS TABLE_NAME 3 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select
def information_schema COLUMNS TABLE_SCHEMA 2 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select
def information_schema COLUMN_PRIVILEGES COLUMN_NAME 5 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select
-def information_schema COLUMN_PRIVILEGES GRANTEE 1 NO varchar 81 243 NULL NULL NULL utf8 utf8_general_ci varchar(81) select
+def information_schema COLUMN_PRIVILEGES GRANTEE 1 NO varchar 190 570 NULL NULL NULL utf8 utf8_general_ci varchar(190) select
def information_schema COLUMN_PRIVILEGES IS_GRANTABLE 7 NO varchar 3 9 NULL NULL NULL utf8 utf8_general_ci varchar(3) select
def information_schema COLUMN_PRIVILEGES PRIVILEGE_TYPE 6 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select
def information_schema COLUMN_PRIVILEGES TABLE_CATALOG 2 NO varchar 512 1536 NULL NULL NULL utf8 utf8_general_ci varchar(512) select
def information_schema COLUMN_PRIVILEGES TABLE_NAME 4 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select
def information_schema COLUMN_PRIVILEGES TABLE_SCHEMA 3 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select
+def information_schema ENABLED_ROLES ROLE_NAME 1 NULL YES varchar 128 384 NULL NULL NULL utf8 utf8_general_ci varchar(128) select
def information_schema ENGINES COMMENT 3 NO varchar 160 480 NULL NULL NULL utf8 utf8_general_ci varchar(160) select
def information_schema ENGINES ENGINE 1 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select
def information_schema ENGINES SAVEPOINTS 6 NULL YES varchar 3 9 NULL NULL NULL utf8 utf8_general_ci varchar(3) select
@@ -294,7 +298,7 @@ def information_schema SCHEMATA DEFAULT_CHARACTER_SET_NAME 3 NO varchar 32 96 N
def information_schema SCHEMATA DEFAULT_COLLATION_NAME 4 NO varchar 32 96 NULL NULL NULL utf8 utf8_general_ci varchar(32) select
def information_schema SCHEMATA SCHEMA_NAME 2 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select
def information_schema SCHEMATA SQL_PATH 5 NULL YES varchar 512 1536 NULL NULL NULL utf8 utf8_general_ci varchar(512) select
-def information_schema SCHEMA_PRIVILEGES GRANTEE 1 NO varchar 81 243 NULL NULL NULL utf8 utf8_general_ci varchar(81) select
+def information_schema SCHEMA_PRIVILEGES GRANTEE 1 NO varchar 190 570 NULL NULL NULL utf8 utf8_general_ci varchar(190) select
def information_schema SCHEMA_PRIVILEGES IS_GRANTABLE 5 NO varchar 3 9 NULL NULL NULL utf8 utf8_general_ci varchar(3) select
def information_schema SCHEMA_PRIVILEGES PRIVILEGE_TYPE 4 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select
def information_schema SCHEMA_PRIVILEGES TABLE_CATALOG 2 NO varchar 512 1536 NULL NULL NULL utf8 utf8_general_ci varchar(512) select
@@ -355,7 +359,7 @@ def information_schema TABLE_CONSTRAINTS CONSTRAINT_SCHEMA 2 NO varchar 64 192
def information_schema TABLE_CONSTRAINTS CONSTRAINT_TYPE 6 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select
def information_schema TABLE_CONSTRAINTS TABLE_NAME 5 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select
def information_schema TABLE_CONSTRAINTS TABLE_SCHEMA 4 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select
-def information_schema TABLE_PRIVILEGES GRANTEE 1 NO varchar 81 243 NULL NULL NULL utf8 utf8_general_ci varchar(81) select
+def information_schema TABLE_PRIVILEGES GRANTEE 1 NO varchar 190 570 NULL NULL NULL utf8 utf8_general_ci varchar(190) select
def information_schema TABLE_PRIVILEGES IS_GRANTABLE 6 NO varchar 3 9 NULL NULL NULL utf8 utf8_general_ci varchar(3) select
def information_schema TABLE_PRIVILEGES PRIVILEGE_TYPE 5 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select
def information_schema TABLE_PRIVILEGES TABLE_CATALOG 2 NO varchar 512 1536 NULL NULL NULL utf8 utf8_general_ci varchar(512) select
@@ -388,7 +392,7 @@ def information_schema TRIGGERS SQL_MODE 18 NO varchar 8192 24576 NULL NULL NUL
def information_schema TRIGGERS TRIGGER_CATALOG 1 NO varchar 512 1536 NULL NULL NULL utf8 utf8_general_ci varchar(512) select
def information_schema TRIGGERS TRIGGER_NAME 3 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select
def information_schema TRIGGERS TRIGGER_SCHEMA 2 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select
-def information_schema USER_PRIVILEGES GRANTEE 1 NO varchar 81 243 NULL NULL NULL utf8 utf8_general_ci varchar(81) select
+def information_schema USER_PRIVILEGES GRANTEE 1 NO varchar 190 570 NULL NULL NULL utf8 utf8_general_ci varchar(190) select
def information_schema USER_PRIVILEGES IS_GRANTABLE 4 NO varchar 3 9 NULL NULL NULL utf8 utf8_general_ci varchar(3) select
def information_schema USER_PRIVILEGES PRIVILEGE_TYPE 3 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64) select
def information_schema USER_PRIVILEGES TABLE_CATALOG 2 NO varchar 512 1536 NULL NULL NULL utf8 utf8_general_ci varchar(512) select
@@ -500,6 +504,9 @@ COL_CML TABLE_SCHEMA TABLE_NAME COLUMN_NAME DATA_TYPE CHARACTER_MAXIMUM_LENGTH C
3.0000 information_schema ALL_PLUGINS LOAD_OPTION varchar 64 192 utf8 utf8_general_ci varchar(64)
3.0000 information_schema ALL_PLUGINS PLUGIN_MATURITY varchar 12 36 utf8 utf8_general_ci varchar(12)
3.0000 information_schema ALL_PLUGINS PLUGIN_AUTH_VERSION varchar 80 240 utf8 utf8_general_ci varchar(80)
+3.0000 information_schema APPLICABLE_ROLES GRANTEE varchar 190 570 utf8 utf8_general_ci varchar(190)
+3.0000 information_schema APPLICABLE_ROLES ROLE_NAME varchar 128 384 utf8 utf8_general_ci varchar(128)
+3.0000 information_schema APPLICABLE_ROLES IS_GRANTABLE varchar 3 9 utf8 utf8_general_ci varchar(3)
3.0000 information_schema CHARACTER_SETS CHARACTER_SET_NAME varchar 32 96 utf8 utf8_general_ci varchar(32)
3.0000 information_schema CHARACTER_SETS DEFAULT_COLLATE_NAME varchar 32 96 utf8 utf8_general_ci varchar(32)
3.0000 information_schema CHARACTER_SETS DESCRIPTION varchar 60 180 utf8 utf8_general_ci varchar(60)
@@ -555,13 +562,14 @@ NULL information_schema COLUMNS DATETIME_PRECISION bigint NULL NULL NULL NULL bi
3.0000 information_schema COLUMNS EXTRA varchar 27 81 utf8 utf8_general_ci varchar(27)
3.0000 information_schema COLUMNS PRIVILEGES varchar 80 240 utf8 utf8_general_ci varchar(80)
3.0000 information_schema COLUMNS COLUMN_COMMENT varchar 1024 3072 utf8 utf8_general_ci varchar(1024)
-3.0000 information_schema COLUMN_PRIVILEGES GRANTEE varchar 81 243 utf8 utf8_general_ci varchar(81)
+3.0000 information_schema COLUMN_PRIVILEGES GRANTEE varchar 190 570 utf8 utf8_general_ci varchar(190)
3.0000 information_schema COLUMN_PRIVILEGES TABLE_CATALOG varchar 512 1536 utf8 utf8_general_ci varchar(512)
3.0000 information_schema COLUMN_PRIVILEGES TABLE_SCHEMA varchar 64 192 utf8 utf8_general_ci varchar(64)
3.0000 information_schema COLUMN_PRIVILEGES TABLE_NAME varchar 64 192 utf8 utf8_general_ci varchar(64)
3.0000 information_schema COLUMN_PRIVILEGES COLUMN_NAME varchar 64 192 utf8 utf8_general_ci varchar(64)
3.0000 information_schema COLUMN_PRIVILEGES PRIVILEGE_TYPE varchar 64 192 utf8 utf8_general_ci varchar(64)
3.0000 information_schema COLUMN_PRIVILEGES IS_GRANTABLE varchar 3 9 utf8 utf8_general_ci varchar(3)
+3.0000 information_schema ENABLED_ROLES ROLE_NAME varchar 128 384 utf8 utf8_general_ci varchar(128)
3.0000 information_schema ENGINES ENGINE varchar 64 192 utf8 utf8_general_ci varchar(64)
3.0000 information_schema ENGINES SUPPORT varchar 8 24 utf8 utf8_general_ci varchar(8)
3.0000 information_schema ENGINES COMMENT varchar 160 480 utf8 utf8_general_ci varchar(160)
@@ -778,7 +786,7 @@ NULL information_schema ROUTINES LAST_ALTERED datetime NULL NULL NULL NULL datet
3.0000 information_schema SCHEMATA DEFAULT_CHARACTER_SET_NAME varchar 32 96 utf8 utf8_general_ci varchar(32)
3.0000 information_schema SCHEMATA DEFAULT_COLLATION_NAME varchar 32 96 utf8 utf8_general_ci varchar(32)
3.0000 information_schema SCHEMATA SQL_PATH varchar 512 1536 utf8 utf8_general_ci varchar(512)
-3.0000 information_schema SCHEMA_PRIVILEGES GRANTEE varchar 81 243 utf8 utf8_general_ci varchar(81)
+3.0000 information_schema SCHEMA_PRIVILEGES GRANTEE varchar 190 570 utf8 utf8_general_ci varchar(190)
3.0000 information_schema SCHEMA_PRIVILEGES TABLE_CATALOG varchar 512 1536 utf8 utf8_general_ci varchar(512)
3.0000 information_schema SCHEMA_PRIVILEGES TABLE_SCHEMA varchar 64 192 utf8 utf8_general_ci varchar(64)
3.0000 information_schema SCHEMA_PRIVILEGES PRIVILEGE_TYPE varchar 64 192 utf8 utf8_general_ci varchar(64)
@@ -839,7 +847,7 @@ NULL information_schema TABLESPACES NODEGROUP_ID bigint NULL NULL NULL NULL bigi
3.0000 information_schema TABLE_CONSTRAINTS TABLE_SCHEMA varchar 64 192 utf8 utf8_general_ci varchar(64)
3.0000 information_schema TABLE_CONSTRAINTS TABLE_NAME varchar 64 192 utf8 utf8_general_ci varchar(64)
3.0000 information_schema TABLE_CONSTRAINTS CONSTRAINT_TYPE varchar 64 192 utf8 utf8_general_ci varchar(64)
-3.0000 information_schema TABLE_PRIVILEGES GRANTEE varchar 81 243 utf8 utf8_general_ci varchar(81)
+3.0000 information_schema TABLE_PRIVILEGES GRANTEE varchar 190 570 utf8 utf8_general_ci varchar(190)
3.0000 information_schema TABLE_PRIVILEGES TABLE_CATALOG varchar 512 1536 utf8 utf8_general_ci varchar(512)
3.0000 information_schema TABLE_PRIVILEGES TABLE_SCHEMA varchar 64 192 utf8 utf8_general_ci varchar(64)
3.0000 information_schema TABLE_PRIVILEGES TABLE_NAME varchar 64 192 utf8 utf8_general_ci varchar(64)
@@ -872,7 +880,7 @@ NULL information_schema TRIGGERS CREATED datetime NULL NULL NULL NULL datetime
3.0000 information_schema TRIGGERS CHARACTER_SET_CLIENT varchar 32 96 utf8 utf8_general_ci varchar(32)
3.0000 information_schema TRIGGERS COLLATION_CONNECTION varchar 32 96 utf8 utf8_general_ci varchar(32)
3.0000 information_schema TRIGGERS DATABASE_COLLATION varchar 32 96 utf8 utf8_general_ci varchar(32)
-3.0000 information_schema USER_PRIVILEGES GRANTEE varchar 81 243 utf8 utf8_general_ci varchar(81)
+3.0000 information_schema USER_PRIVILEGES GRANTEE varchar 190 570 utf8 utf8_general_ci varchar(190)
3.0000 information_schema USER_PRIVILEGES TABLE_CATALOG varchar 512 1536 utf8 utf8_general_ci varchar(512)
3.0000 information_schema USER_PRIVILEGES PRIVILEGE_TYPE varchar 64 192 utf8 utf8_general_ci varchar(64)
3.0000 information_schema USER_PRIVILEGES IS_GRANTABLE varchar 3 9 utf8 utf8_general_ci varchar(3)
diff --git a/mysql-test/suite/funcs_1/r/is_columns_is_embedded.result b/mysql-test/suite/funcs_1/r/is_columns_is_embedded.result
index 7b8074f82f2..76b7571fb7d 100644
--- a/mysql-test/suite/funcs_1/r/is_columns_is_embedded.result
+++ b/mysql-test/suite/funcs_1/r/is_columns_is_embedded.result
@@ -16,6 +16,9 @@ def information_schema ALL_PLUGINS PLUGIN_STATUS 3 NO varchar 16 48 NULL NULL N
def information_schema ALL_PLUGINS PLUGIN_TYPE 4 NO varchar 80 240 NULL NULL NULL utf8 utf8_general_ci varchar(80)
def information_schema ALL_PLUGINS PLUGIN_TYPE_VERSION 5 NO varchar 20 60 NULL NULL NULL utf8 utf8_general_ci varchar(20)
def information_schema ALL_PLUGINS PLUGIN_VERSION 2 NO varchar 20 60 NULL NULL NULL utf8 utf8_general_ci varchar(20)
+def information_schema APPLICABLE_ROLES GRANTEE 1 NO varchar 190 570 NULL NULL NULL utf8 utf8_general_ci varchar(190)
+def information_schema APPLICABLE_ROLES IS_GRANTABLE 3 NO varchar 3 9 NULL NULL NULL utf8 utf8_general_ci varchar(3)
+def information_schema APPLICABLE_ROLES ROLE_NAME 2 NO varchar 128 384 NULL NULL NULL utf8 utf8_general_ci varchar(128)
def information_schema CHARACTER_SETS CHARACTER_SET_NAME 1 NO varchar 32 96 NULL NULL NULL utf8 utf8_general_ci varchar(32)
def information_schema CHARACTER_SETS DEFAULT_COLLATE_NAME 2 NO varchar 32 96 NULL NULL NULL utf8 utf8_general_ci varchar(32)
def information_schema CHARACTER_SETS DESCRIPTION 3 NO varchar 60 180 NULL NULL NULL utf8 utf8_general_ci varchar(60)
@@ -72,12 +75,13 @@ def information_schema COLUMNS TABLE_CATALOG 1 NO varchar 512 1536 NULL NULL NU
def information_schema COLUMNS TABLE_NAME 3 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64)
def information_schema COLUMNS TABLE_SCHEMA 2 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64)
def information_schema COLUMN_PRIVILEGES COLUMN_NAME 5 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64)
-def information_schema COLUMN_PRIVILEGES GRANTEE 1 NO varchar 81 243 NULL NULL NULL utf8 utf8_general_ci varchar(81)
+def information_schema COLUMN_PRIVILEGES GRANTEE 1 NO varchar 190 570 NULL NULL NULL utf8 utf8_general_ci varchar(190)
def information_schema COLUMN_PRIVILEGES IS_GRANTABLE 7 NO varchar 3 9 NULL NULL NULL utf8 utf8_general_ci varchar(3)
def information_schema COLUMN_PRIVILEGES PRIVILEGE_TYPE 6 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64)
def information_schema COLUMN_PRIVILEGES TABLE_CATALOG 2 NO varchar 512 1536 NULL NULL NULL utf8 utf8_general_ci varchar(512)
def information_schema COLUMN_PRIVILEGES TABLE_NAME 4 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64)
def information_schema COLUMN_PRIVILEGES TABLE_SCHEMA 3 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64)
+def information_schema ENABLED_ROLES ROLE_NAME 1 NULL YES varchar 128 384 NULL NULL NULL utf8 utf8_general_ci varchar(128)
def information_schema ENGINES COMMENT 3 NO varchar 160 480 NULL NULL NULL utf8 utf8_general_ci varchar(160)
def information_schema ENGINES ENGINE 1 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64)
def information_schema ENGINES SAVEPOINTS 6 NULL YES varchar 3 9 NULL NULL NULL utf8 utf8_general_ci varchar(3)
@@ -294,7 +298,7 @@ def information_schema SCHEMATA DEFAULT_CHARACTER_SET_NAME 3 NO varchar 32 96 N
def information_schema SCHEMATA DEFAULT_COLLATION_NAME 4 NO varchar 32 96 NULL NULL NULL utf8 utf8_general_ci varchar(32)
def information_schema SCHEMATA SCHEMA_NAME 2 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64)
def information_schema SCHEMATA SQL_PATH 5 NULL YES varchar 512 1536 NULL NULL NULL utf8 utf8_general_ci varchar(512)
-def information_schema SCHEMA_PRIVILEGES GRANTEE 1 NO varchar 81 243 NULL NULL NULL utf8 utf8_general_ci varchar(81)
+def information_schema SCHEMA_PRIVILEGES GRANTEE 1 NO varchar 190 570 NULL NULL NULL utf8 utf8_general_ci varchar(190)
def information_schema SCHEMA_PRIVILEGES IS_GRANTABLE 5 NO varchar 3 9 NULL NULL NULL utf8 utf8_general_ci varchar(3)
def information_schema SCHEMA_PRIVILEGES PRIVILEGE_TYPE 4 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64)
def information_schema SCHEMA_PRIVILEGES TABLE_CATALOG 2 NO varchar 512 1536 NULL NULL NULL utf8 utf8_general_ci varchar(512)
@@ -355,7 +359,7 @@ def information_schema TABLE_CONSTRAINTS CONSTRAINT_SCHEMA 2 NO varchar 64 192
def information_schema TABLE_CONSTRAINTS CONSTRAINT_TYPE 6 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64)
def information_schema TABLE_CONSTRAINTS TABLE_NAME 5 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64)
def information_schema TABLE_CONSTRAINTS TABLE_SCHEMA 4 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64)
-def information_schema TABLE_PRIVILEGES GRANTEE 1 NO varchar 81 243 NULL NULL NULL utf8 utf8_general_ci varchar(81)
+def information_schema TABLE_PRIVILEGES GRANTEE 1 NO varchar 190 570 NULL NULL NULL utf8 utf8_general_ci varchar(190)
def information_schema TABLE_PRIVILEGES IS_GRANTABLE 6 NO varchar 3 9 NULL NULL NULL utf8 utf8_general_ci varchar(3)
def information_schema TABLE_PRIVILEGES PRIVILEGE_TYPE 5 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64)
def information_schema TABLE_PRIVILEGES TABLE_CATALOG 2 NO varchar 512 1536 NULL NULL NULL utf8 utf8_general_ci varchar(512)
@@ -388,7 +392,7 @@ def information_schema TRIGGERS SQL_MODE 18 NO varchar 8192 24576 NULL NULL NUL
def information_schema TRIGGERS TRIGGER_CATALOG 1 NO varchar 512 1536 NULL NULL NULL utf8 utf8_general_ci varchar(512)
def information_schema TRIGGERS TRIGGER_NAME 3 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64)
def information_schema TRIGGERS TRIGGER_SCHEMA 2 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64)
-def information_schema USER_PRIVILEGES GRANTEE 1 NO varchar 81 243 NULL NULL NULL utf8 utf8_general_ci varchar(81)
+def information_schema USER_PRIVILEGES GRANTEE 1 NO varchar 190 570 NULL NULL NULL utf8 utf8_general_ci varchar(190)
def information_schema USER_PRIVILEGES IS_GRANTABLE 4 NO varchar 3 9 NULL NULL NULL utf8 utf8_general_ci varchar(3)
def information_schema USER_PRIVILEGES PRIVILEGE_TYPE 3 NO varchar 64 192 NULL NULL NULL utf8 utf8_general_ci varchar(64)
def information_schema USER_PRIVILEGES TABLE_CATALOG 2 NO varchar 512 1536 NULL NULL NULL utf8 utf8_general_ci varchar(512)
@@ -500,6 +504,9 @@ COL_CML TABLE_SCHEMA TABLE_NAME COLUMN_NAME DATA_TYPE CHARACTER_MAXIMUM_LENGTH C
3.0000 information_schema ALL_PLUGINS LOAD_OPTION varchar 64 192 utf8 utf8_general_ci varchar(64)
3.0000 information_schema ALL_PLUGINS PLUGIN_MATURITY varchar 12 36 utf8 utf8_general_ci varchar(12)
3.0000 information_schema ALL_PLUGINS PLUGIN_AUTH_VERSION varchar 80 240 utf8 utf8_general_ci varchar(80)
+3.0000 information_schema APPLICABLE_ROLES GRANTEE varchar 190 570 utf8 utf8_general_ci varchar(190)
+3.0000 information_schema APPLICABLE_ROLES ROLE_NAME varchar 128 384 utf8 utf8_general_ci varchar(128)
+3.0000 information_schema APPLICABLE_ROLES IS_GRANTABLE varchar 3 9 utf8 utf8_general_ci varchar(3)
3.0000 information_schema CHARACTER_SETS CHARACTER_SET_NAME varchar 32 96 utf8 utf8_general_ci varchar(32)
3.0000 information_schema CHARACTER_SETS DEFAULT_COLLATE_NAME varchar 32 96 utf8 utf8_general_ci varchar(32)
3.0000 information_schema CHARACTER_SETS DESCRIPTION varchar 60 180 utf8 utf8_general_ci varchar(60)
@@ -555,13 +562,14 @@ NULL information_schema COLUMNS DATETIME_PRECISION bigint NULL NULL NULL NULL bi
3.0000 information_schema COLUMNS EXTRA varchar 27 81 utf8 utf8_general_ci varchar(27)
3.0000 information_schema COLUMNS PRIVILEGES varchar 80 240 utf8 utf8_general_ci varchar(80)
3.0000 information_schema COLUMNS COLUMN_COMMENT varchar 1024 3072 utf8 utf8_general_ci varchar(1024)
-3.0000 information_schema COLUMN_PRIVILEGES GRANTEE varchar 81 243 utf8 utf8_general_ci varchar(81)
+3.0000 information_schema COLUMN_PRIVILEGES GRANTEE varchar 190 570 utf8 utf8_general_ci varchar(190)
3.0000 information_schema COLUMN_PRIVILEGES TABLE_CATALOG varchar 512 1536 utf8 utf8_general_ci varchar(512)
3.0000 information_schema COLUMN_PRIVILEGES TABLE_SCHEMA varchar 64 192 utf8 utf8_general_ci varchar(64)
3.0000 information_schema COLUMN_PRIVILEGES TABLE_NAME varchar 64 192 utf8 utf8_general_ci varchar(64)
3.0000 information_schema COLUMN_PRIVILEGES COLUMN_NAME varchar 64 192 utf8 utf8_general_ci varchar(64)
3.0000 information_schema COLUMN_PRIVILEGES PRIVILEGE_TYPE varchar 64 192 utf8 utf8_general_ci varchar(64)
3.0000 information_schema COLUMN_PRIVILEGES IS_GRANTABLE varchar 3 9 utf8 utf8_general_ci varchar(3)
+3.0000 information_schema ENABLED_ROLES ROLE_NAME varchar 128 384 utf8 utf8_general_ci varchar(128)
3.0000 information_schema ENGINES ENGINE varchar 64 192 utf8 utf8_general_ci varchar(64)
3.0000 information_schema ENGINES SUPPORT varchar 8 24 utf8 utf8_general_ci varchar(8)
3.0000 information_schema ENGINES COMMENT varchar 160 480 utf8 utf8_general_ci varchar(160)
@@ -778,7 +786,7 @@ NULL information_schema ROUTINES LAST_ALTERED datetime NULL NULL NULL NULL datet
3.0000 information_schema SCHEMATA DEFAULT_CHARACTER_SET_NAME varchar 32 96 utf8 utf8_general_ci varchar(32)
3.0000 information_schema SCHEMATA DEFAULT_COLLATION_NAME varchar 32 96 utf8 utf8_general_ci varchar(32)
3.0000 information_schema SCHEMATA SQL_PATH varchar 512 1536 utf8 utf8_general_ci varchar(512)
-3.0000 information_schema SCHEMA_PRIVILEGES GRANTEE varchar 81 243 utf8 utf8_general_ci varchar(81)
+3.0000 information_schema SCHEMA_PRIVILEGES GRANTEE varchar 190 570 utf8 utf8_general_ci varchar(190)
3.0000 information_schema SCHEMA_PRIVILEGES TABLE_CATALOG varchar 512 1536 utf8 utf8_general_ci varchar(512)
3.0000 information_schema SCHEMA_PRIVILEGES TABLE_SCHEMA varchar 64 192 utf8 utf8_general_ci varchar(64)
3.0000 information_schema SCHEMA_PRIVILEGES PRIVILEGE_TYPE varchar 64 192 utf8 utf8_general_ci varchar(64)
@@ -839,7 +847,7 @@ NULL information_schema TABLESPACES NODEGROUP_ID bigint NULL NULL NULL NULL bigi
3.0000 information_schema TABLE_CONSTRAINTS TABLE_SCHEMA varchar 64 192 utf8 utf8_general_ci varchar(64)
3.0000 information_schema TABLE_CONSTRAINTS TABLE_NAME varchar 64 192 utf8 utf8_general_ci varchar(64)
3.0000 information_schema TABLE_CONSTRAINTS CONSTRAINT_TYPE varchar 64 192 utf8 utf8_general_ci varchar(64)
-3.0000 information_schema TABLE_PRIVILEGES GRANTEE varchar 81 243 utf8 utf8_general_ci varchar(81)
+3.0000 information_schema TABLE_PRIVILEGES GRANTEE varchar 190 570 utf8 utf8_general_ci varchar(190)
3.0000 information_schema TABLE_PRIVILEGES TABLE_CATALOG varchar 512 1536 utf8 utf8_general_ci varchar(512)
3.0000 information_schema TABLE_PRIVILEGES TABLE_SCHEMA varchar 64 192 utf8 utf8_general_ci varchar(64)
3.0000 information_schema TABLE_PRIVILEGES TABLE_NAME varchar 64 192 utf8 utf8_general_ci varchar(64)
@@ -872,7 +880,7 @@ NULL information_schema TRIGGERS CREATED datetime NULL NULL NULL NULL datetime
3.0000 information_schema TRIGGERS CHARACTER_SET_CLIENT varchar 32 96 utf8 utf8_general_ci varchar(32)
3.0000 information_schema TRIGGERS COLLATION_CONNECTION varchar 32 96 utf8 utf8_general_ci varchar(32)
3.0000 information_schema TRIGGERS DATABASE_COLLATION varchar 32 96 utf8 utf8_general_ci varchar(32)
-3.0000 information_schema USER_PRIVILEGES GRANTEE varchar 81 243 utf8 utf8_general_ci varchar(81)
+3.0000 information_schema USER_PRIVILEGES GRANTEE varchar 190 570 utf8 utf8_general_ci varchar(190)
3.0000 information_schema USER_PRIVILEGES TABLE_CATALOG varchar 512 1536 utf8 utf8_general_ci varchar(512)
3.0000 information_schema USER_PRIVILEGES PRIVILEGE_TYPE varchar 64 192 utf8 utf8_general_ci varchar(64)
3.0000 information_schema USER_PRIVILEGES IS_GRANTABLE varchar 3 9 utf8 utf8_general_ci varchar(3)
diff --git a/mysql-test/suite/funcs_1/r/is_columns_mysql.result b/mysql-test/suite/funcs_1/r/is_columns_mysql.result
index e2fba35c7f8..00667e8e9d0 100644
--- a/mysql-test/suite/funcs_1/r/is_columns_mysql.result
+++ b/mysql-test/suite/funcs_1/r/is_columns_mysql.result
@@ -154,6 +154,10 @@ def mysql proxies_priv Proxied_user 4 NO char 16 48 NULL NULL NULL utf8 utf8_bi
def mysql proxies_priv Timestamp 7 CURRENT_TIMESTAMP NO timestamp NULL NULL NULL NULL 0 NULL NULL timestamp on update CURRENT_TIMESTAMP select,insert,update,references
def mysql proxies_priv User 2 NO char 16 48 NULL NULL NULL utf8 utf8_bin char(16) PRI select,insert,update,references
def mysql proxies_priv With_grant 5 0 NO tinyint NULL NULL 3 0 NULL NULL NULL tinyint(1) select,insert,update,references
+def mysql roles_mapping Admin_option 4 N NO enum 1 3 NULL NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references
+def mysql roles_mapping Host 1 NO char 60 180 NULL NULL NULL utf8 utf8_bin char(60) PRI select,insert,update,references
+def mysql roles_mapping Role 3 NO char 16 48 NULL NULL NULL utf8 utf8_bin char(16) PRI select,insert,update,references
+def mysql roles_mapping User 2 NO char 16 48 NULL NULL NULL utf8 utf8_bin char(16) PRI select,insert,update,references
def mysql servers Db 3 NO char 64 192 NULL NULL NULL utf8 utf8_general_ci char(64) select,insert,update,references
def mysql servers Host 2 NO char 64 192 NULL NULL NULL utf8 utf8_general_ci char(64) select,insert,update,references
def mysql servers Owner 9 NO char 64 192 NULL NULL NULL utf8 utf8_general_ci char(64) select,insert,update,references
@@ -218,6 +222,7 @@ def mysql user Grant_priv 14 N NO enum 1 3 NULL NULL NULL utf8 utf8_general_ci e
def mysql user Host 1 NO char 60 180 NULL NULL NULL utf8 utf8_bin char(60) PRI select,insert,update,references
def mysql user Index_priv 16 N NO enum 1 3 NULL NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references
def mysql user Insert_priv 5 N NO enum 1 3 NULL NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references
+def mysql user is_role 44 N NO enum 1 3 NULL NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references
def mysql user Lock_tables_priv 21 N NO enum 1 3 NULL NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references
def mysql user max_connections 39 0 NO int NULL NULL 10 0 NULL NULL NULL int(11) unsigned select,insert,update,references
def mysql user max_questions 37 0 NO int NULL NULL 10 0 NULL NULL NULL int(11) unsigned select,insert,update,references
@@ -467,6 +472,10 @@ NULL mysql procs_priv Timestamp timestamp NULL NULL NULL NULL timestamp
NULL mysql proxies_priv With_grant tinyint NULL NULL NULL NULL tinyint(1)
3.0000 mysql proxies_priv Grantor char 77 231 utf8 utf8_bin char(77)
NULL mysql proxies_priv Timestamp timestamp NULL NULL NULL NULL timestamp
+3.0000 mysql roles_mapping Host char 60 180 utf8 utf8_bin char(60)
+3.0000 mysql roles_mapping User char 16 48 utf8 utf8_bin char(16)
+3.0000 mysql roles_mapping Role char 16 48 utf8 utf8_bin char(16)
+3.0000 mysql roles_mapping Admin_option enum 1 3 utf8 utf8_general_ci enum('N','Y')
3.0000 mysql servers Server_name char 64 192 utf8 utf8_general_ci char(64)
3.0000 mysql servers Host char 64 192 utf8 utf8_general_ci char(64)
3.0000 mysql servers Db char 64 192 utf8 utf8_general_ci char(64)
@@ -556,3 +565,4 @@ NULL mysql user max_user_connections int NULL NULL NULL NULL int(11)
1.0000 mysql user plugin char 64 64 latin1 latin1_swedish_ci char(64)
1.0000 mysql user authentication_string text 65535 65535 utf8 utf8_bin text
3.0000 mysql user password_expired enum 1 3 utf8 utf8_general_ci enum('N','Y')
+3.0000 mysql user is_role enum 1 3 utf8 utf8_general_ci enum('N','Y')
diff --git a/mysql-test/suite/funcs_1/r/is_columns_mysql_embedded.result b/mysql-test/suite/funcs_1/r/is_columns_mysql_embedded.result
index 94e1ed45152..d3de1f1c2d6 100644
--- a/mysql-test/suite/funcs_1/r/is_columns_mysql_embedded.result
+++ b/mysql-test/suite/funcs_1/r/is_columns_mysql_embedded.result
@@ -154,6 +154,10 @@ def mysql proxies_priv Proxied_user 4 NO char 16 48 NULL NULL NULL utf8 utf8_bi
def mysql proxies_priv Timestamp 7 CURRENT_TIMESTAMP NO timestamp NULL NULL NULL NULL 0 NULL NULL timestamp on update CURRENT_TIMESTAMP
def mysql proxies_priv User 2 NO char 16 48 NULL NULL NULL utf8 utf8_bin char(16) PRI
def mysql proxies_priv With_grant 5 0 NO tinyint NULL NULL 3 0 NULL NULL NULL tinyint(1)
+def mysql roles_mapping Admin_option 4 N NO enum 1 3 NULL NULL NULL utf8 utf8_general_ci enum('N','Y')
+def mysql roles_mapping Host 1 NO char 60 180 NULL NULL NULL utf8 utf8_bin char(60) PRI
+def mysql roles_mapping Role 3 NO char 16 48 NULL NULL NULL utf8 utf8_bin char(16) PRI
+def mysql roles_mapping User 2 NO char 16 48 NULL NULL NULL utf8 utf8_bin char(16) PRI
def mysql servers Db 3 NO char 64 192 NULL NULL NULL utf8 utf8_general_ci char(64)
def mysql servers Host 2 NO char 64 192 NULL NULL NULL utf8 utf8_general_ci char(64)
def mysql servers Owner 9 NO char 64 192 NULL NULL NULL utf8 utf8_general_ci char(64)
@@ -218,6 +222,7 @@ def mysql user Grant_priv 14 N NO enum 1 3 NULL NULL NULL utf8 utf8_general_ci e
def mysql user Host 1 NO char 60 180 NULL NULL NULL utf8 utf8_bin char(60) PRI
def mysql user Index_priv 16 N NO enum 1 3 NULL NULL NULL utf8 utf8_general_ci enum('N','Y')
def mysql user Insert_priv 5 N NO enum 1 3 NULL NULL NULL utf8 utf8_general_ci enum('N','Y')
+def mysql user is_role 44 N NO enum 1 3 NULL NULL NULL utf8 utf8_general_ci enum('N','Y')
def mysql user Lock_tables_priv 21 N NO enum 1 3 NULL NULL NULL utf8 utf8_general_ci enum('N','Y')
def mysql user max_connections 39 0 NO int NULL NULL 10 0 NULL NULL NULL int(11) unsigned
def mysql user max_questions 37 0 NO int NULL NULL 10 0 NULL NULL NULL int(11) unsigned
@@ -467,6 +472,10 @@ NULL mysql procs_priv Timestamp timestamp NULL NULL NULL NULL timestamp
NULL mysql proxies_priv With_grant tinyint NULL NULL NULL NULL tinyint(1)
3.0000 mysql proxies_priv Grantor char 77 231 utf8 utf8_bin char(77)
NULL mysql proxies_priv Timestamp timestamp NULL NULL NULL NULL timestamp
+3.0000 mysql roles_mapping Host char 60 180 utf8 utf8_bin char(60)
+3.0000 mysql roles_mapping User char 16 48 utf8 utf8_bin char(16)
+3.0000 mysql roles_mapping Role char 16 48 utf8 utf8_bin char(16)
+3.0000 mysql roles_mapping Admin_option enum 1 3 utf8 utf8_general_ci enum('N','Y')
3.0000 mysql servers Server_name char 64 192 utf8 utf8_general_ci char(64)
3.0000 mysql servers Host char 64 192 utf8 utf8_general_ci char(64)
3.0000 mysql servers Db char 64 192 utf8 utf8_general_ci char(64)
@@ -556,3 +565,4 @@ NULL mysql user max_user_connections int NULL NULL NULL NULL int(11)
1.0000 mysql user plugin char 64 64 latin1 latin1_swedish_ci char(64)
1.0000 mysql user authentication_string text 65535 65535 utf8 utf8_bin text
3.0000 mysql user password_expired enum 1 3 utf8 utf8_general_ci enum('N','Y')
+3.0000 mysql user is_role enum 1 3 utf8 utf8_general_ci enum('N','Y')
diff --git a/mysql-test/suite/funcs_1/r/is_key_column_usage.result b/mysql-test/suite/funcs_1/r/is_key_column_usage.result
index 75cd59604ff..e27b4d06d25 100644
--- a/mysql-test/suite/funcs_1/r/is_key_column_usage.result
+++ b/mysql-test/suite/funcs_1/r/is_key_column_usage.result
@@ -124,6 +124,9 @@ def mysql PRIMARY def mysql proxies_priv Host
def mysql PRIMARY def mysql proxies_priv User
def mysql PRIMARY def mysql proxies_priv Proxied_host
def mysql PRIMARY def mysql proxies_priv Proxied_user
+def mysql Host def mysql roles_mapping Host
+def mysql Host def mysql roles_mapping User
+def mysql Host def mysql roles_mapping Role
def mysql PRIMARY def mysql servers Server_name
def mysql PRIMARY def mysql table_stats db_name
def mysql PRIMARY def mysql table_stats table_name
diff --git a/mysql-test/suite/funcs_1/r/is_schema_privileges.result b/mysql-test/suite/funcs_1/r/is_schema_privileges.result
index 40e9b8d8bd7..9d4b2a537d6 100644
--- a/mysql-test/suite/funcs_1/r/is_schema_privileges.result
+++ b/mysql-test/suite/funcs_1/r/is_schema_privileges.result
@@ -28,7 +28,7 @@ DROP FUNCTION test.f1;
#########################################################################
DESCRIBE information_schema.SCHEMA_PRIVILEGES;
Field Type Null Key Default Extra
-GRANTEE varchar(81) NO
+GRANTEE varchar(190) NO
TABLE_CATALOG varchar(512) NO
TABLE_SCHEMA varchar(64) NO
PRIVILEGE_TYPE varchar(64) NO
@@ -36,7 +36,7 @@ IS_GRANTABLE varchar(3) NO
SHOW CREATE TABLE information_schema.SCHEMA_PRIVILEGES;
Table Create Table
SCHEMA_PRIVILEGES CREATE TEMPORARY TABLE `SCHEMA_PRIVILEGES` (
- `GRANTEE` varchar(81) NOT NULL DEFAULT '',
+ `GRANTEE` varchar(190) NOT NULL DEFAULT '',
`TABLE_CATALOG` varchar(512) NOT NULL DEFAULT '',
`TABLE_SCHEMA` varchar(64) NOT NULL DEFAULT '',
`PRIVILEGE_TYPE` varchar(64) NOT NULL DEFAULT '',
@@ -44,7 +44,7 @@ SCHEMA_PRIVILEGES CREATE TEMPORARY TABLE `SCHEMA_PRIVILEGES` (
) ENGINE=MEMORY DEFAULT CHARSET=utf8
SHOW COLUMNS FROM information_schema.SCHEMA_PRIVILEGES;
Field Type Null Key Default Extra
-GRANTEE varchar(81) NO
+GRANTEE varchar(190) NO
TABLE_CATALOG varchar(512) NO
TABLE_SCHEMA varchar(64) NO
PRIVILEGE_TYPE varchar(64) NO
diff --git a/mysql-test/suite/funcs_1/r/is_statistics.result b/mysql-test/suite/funcs_1/r/is_statistics.result
index c1afeb8db9b..e800d9b63fd 100644
--- a/mysql-test/suite/funcs_1/r/is_statistics.result
+++ b/mysql-test/suite/funcs_1/r/is_statistics.result
@@ -131,6 +131,9 @@ def mysql proxies_priv mysql PRIMARY
def mysql proxies_priv mysql PRIMARY
def mysql proxies_priv mysql PRIMARY
def mysql proxies_priv mysql Grantor
+def mysql roles_mapping mysql Host
+def mysql roles_mapping mysql Host
+def mysql roles_mapping mysql Host
def mysql servers mysql PRIMARY
def mysql table_stats mysql PRIMARY
def mysql table_stats mysql PRIMARY
diff --git a/mysql-test/suite/funcs_1/r/is_statistics_mysql.result b/mysql-test/suite/funcs_1/r/is_statistics_mysql.result
index 5dbf3bc1488..c9e21a12663 100644
--- a/mysql-test/suite/funcs_1/r/is_statistics_mysql.result
+++ b/mysql-test/suite/funcs_1/r/is_statistics_mysql.result
@@ -59,6 +59,9 @@ def mysql proxies_priv 0 mysql PRIMARY 1 Host A #CARD# NULL NULL BTREE
def mysql proxies_priv 0 mysql PRIMARY 2 User A #CARD# NULL NULL BTREE
def mysql proxies_priv 0 mysql PRIMARY 3 Proxied_host A #CARD# NULL NULL BTREE
def mysql proxies_priv 0 mysql PRIMARY 4 Proxied_user A #CARD# NULL NULL BTREE
+def mysql roles_mapping 0 mysql Host 1 Host A #CARD# NULL NULL BTREE
+def mysql roles_mapping 0 mysql Host 2 User A #CARD# NULL NULL BTREE
+def mysql roles_mapping 0 mysql Host 3 Role A #CARD# NULL NULL BTREE
def mysql servers 0 mysql PRIMARY 1 Server_name A #CARD# NULL NULL BTREE
def mysql tables_priv 1 mysql Grantor 1 Grantor A #CARD# NULL NULL BTREE
def mysql tables_priv 0 mysql PRIMARY 1 Host A #CARD# NULL NULL BTREE
diff --git a/mysql-test/suite/funcs_1/r/is_statistics_mysql_embedded.result b/mysql-test/suite/funcs_1/r/is_statistics_mysql_embedded.result
index f818db6784e..dbe786d39ef 100644
--- a/mysql-test/suite/funcs_1/r/is_statistics_mysql_embedded.result
+++ b/mysql-test/suite/funcs_1/r/is_statistics_mysql_embedded.result
@@ -59,6 +59,9 @@ def mysql proxies_priv 0 mysql PRIMARY 1 Host A #CARD# NULL NULL BTREE
def mysql proxies_priv 0 mysql PRIMARY 2 User A #CARD# NULL NULL BTREE
def mysql proxies_priv 0 mysql PRIMARY 3 Proxied_host A #CARD# NULL NULL BTREE
def mysql proxies_priv 0 mysql PRIMARY 4 Proxied_user A #CARD# NULL NULL BTREE
+def mysql roles_mapping 0 mysql Host 1 Host A #CARD# NULL NULL BTREE
+def mysql roles_mapping 0 mysql Host 2 User A #CARD# NULL NULL BTREE
+def mysql roles_mapping 0 mysql Host 3 Role A #CARD# NULL NULL BTREE
def mysql servers 0 mysql PRIMARY 1 Server_name A #CARD# NULL NULL BTREE
def mysql tables_priv 1 mysql Grantor 1 Grantor A #CARD# NULL NULL BTREE
def mysql tables_priv 0 mysql PRIMARY 1 Host A #CARD# NULL NULL BTREE
@@ -133,6 +136,9 @@ def mysql proxies_priv 0 mysql PRIMARY 1 Host A #CARD# NULL NULL BTREE
def mysql proxies_priv 0 mysql PRIMARY 2 User A #CARD# NULL NULL BTREE
def mysql proxies_priv 0 mysql PRIMARY 3 Proxied_host A #CARD# NULL NULL BTREE
def mysql proxies_priv 0 mysql PRIMARY 4 Proxied_user A #CARD# NULL NULL BTREE
+def mysql roles_mapping 0 mysql Host 1 Host A #CARD# NULL NULL BTREE
+def mysql roles_mapping 0 mysql Host 2 User A #CARD# NULL NULL BTREE
+def mysql roles_mapping 0 mysql Host 3 Role A #CARD# NULL NULL BTREE
def mysql servers 0 mysql PRIMARY 1 Server_name A #CARD# NULL NULL BTREE
def mysql tables_priv 1 mysql Grantor 1 Grantor A #CARD# NULL NULL BTREE
def mysql tables_priv 0 mysql PRIMARY 1 Host A #CARD# NULL NULL BTREE
diff --git a/mysql-test/suite/funcs_1/r/is_table_constraints.result b/mysql-test/suite/funcs_1/r/is_table_constraints.result
index 0077f74396c..ea2dd5ccd98 100644
--- a/mysql-test/suite/funcs_1/r/is_table_constraints.result
+++ b/mysql-test/suite/funcs_1/r/is_table_constraints.result
@@ -78,6 +78,7 @@ def mysql PRIMARY mysql plugin
def mysql PRIMARY mysql proc
def mysql PRIMARY mysql procs_priv
def mysql PRIMARY mysql proxies_priv
+def mysql Host mysql roles_mapping
def mysql PRIMARY mysql servers
def mysql PRIMARY mysql table_stats
def mysql PRIMARY mysql tables_priv
diff --git a/mysql-test/suite/funcs_1/r/is_table_constraints_mysql.result b/mysql-test/suite/funcs_1/r/is_table_constraints_mysql.result
index 38f79f30068..05df41570cf 100644
--- a/mysql-test/suite/funcs_1/r/is_table_constraints_mysql.result
+++ b/mysql-test/suite/funcs_1/r/is_table_constraints_mysql.result
@@ -28,6 +28,7 @@ def mysql PRIMARY mysql plugin PRIMARY KEY
def mysql PRIMARY mysql proc PRIMARY KEY
def mysql PRIMARY mysql procs_priv PRIMARY KEY
def mysql PRIMARY mysql proxies_priv PRIMARY KEY
+def mysql Host mysql roles_mapping UNIQUE
def mysql PRIMARY mysql servers PRIMARY KEY
def mysql PRIMARY mysql tables_priv PRIMARY KEY
def mysql PRIMARY mysql table_stats PRIMARY KEY
diff --git a/mysql-test/suite/funcs_1/r/is_table_constraints_mysql_embedded.result b/mysql-test/suite/funcs_1/r/is_table_constraints_mysql_embedded.result
index 8195abd9657..fd13d67f07c 100644
--- a/mysql-test/suite/funcs_1/r/is_table_constraints_mysql_embedded.result
+++ b/mysql-test/suite/funcs_1/r/is_table_constraints_mysql_embedded.result
@@ -28,6 +28,7 @@ def mysql PRIMARY mysql plugin PRIMARY KEY
def mysql PRIMARY mysql proc PRIMARY KEY
def mysql PRIMARY mysql procs_priv PRIMARY KEY
def mysql PRIMARY mysql proxies_priv PRIMARY KEY
+def mysql Host mysql roles_mapping UNIQUE
def mysql PRIMARY mysql servers PRIMARY KEY
def mysql PRIMARY mysql tables_priv PRIMARY KEY
def mysql PRIMARY mysql table_stats PRIMARY KEY
@@ -63,6 +64,7 @@ def mysql PRIMARY mysql plugin PRIMARY KEY
def mysql PRIMARY mysql proc PRIMARY KEY
def mysql PRIMARY mysql procs_priv PRIMARY KEY
def mysql PRIMARY mysql proxies_priv PRIMARY KEY
+def mysql Host mysql roles_mapping UNIQUE
def mysql PRIMARY mysql servers PRIMARY KEY
def mysql PRIMARY mysql tables_priv PRIMARY KEY
def mysql PRIMARY mysql table_stats PRIMARY KEY
diff --git a/mysql-test/suite/funcs_1/r/is_table_privileges.result b/mysql-test/suite/funcs_1/r/is_table_privileges.result
index 8f51d99b6db..f7e443bbd35 100644
--- a/mysql-test/suite/funcs_1/r/is_table_privileges.result
+++ b/mysql-test/suite/funcs_1/r/is_table_privileges.result
@@ -28,7 +28,7 @@ DROP FUNCTION test.f1;
#########################################################################
DESCRIBE information_schema.TABLE_PRIVILEGES;
Field Type Null Key Default Extra
-GRANTEE varchar(81) NO
+GRANTEE varchar(190) NO
TABLE_CATALOG varchar(512) NO
TABLE_SCHEMA varchar(64) NO
TABLE_NAME varchar(64) NO
@@ -37,7 +37,7 @@ IS_GRANTABLE varchar(3) NO
SHOW CREATE TABLE information_schema.TABLE_PRIVILEGES;
Table Create Table
TABLE_PRIVILEGES CREATE TEMPORARY TABLE `TABLE_PRIVILEGES` (
- `GRANTEE` varchar(81) NOT NULL DEFAULT '',
+ `GRANTEE` varchar(190) NOT NULL DEFAULT '',
`TABLE_CATALOG` varchar(512) NOT NULL DEFAULT '',
`TABLE_SCHEMA` varchar(64) NOT NULL DEFAULT '',
`TABLE_NAME` varchar(64) NOT NULL DEFAULT '',
@@ -46,7 +46,7 @@ TABLE_PRIVILEGES CREATE TEMPORARY TABLE `TABLE_PRIVILEGES` (
) ENGINE=MEMORY DEFAULT CHARSET=utf8
SHOW COLUMNS FROM information_schema.TABLE_PRIVILEGES;
Field Type Null Key Default Extra
-GRANTEE varchar(81) NO
+GRANTEE varchar(190) NO
TABLE_CATALOG varchar(512) NO
TABLE_SCHEMA varchar(64) NO
TABLE_NAME varchar(64) NO
diff --git a/mysql-test/suite/funcs_1/r/is_tables_is.result b/mysql-test/suite/funcs_1/r/is_tables_is.result
index 5d3770c42de..5ecac2f296f 100644
--- a/mysql-test/suite/funcs_1/r/is_tables_is.result
+++ b/mysql-test/suite/funcs_1/r/is_tables_is.result
@@ -39,6 +39,29 @@ user_comment
Separator -----------------------------------------------------
TABLE_CATALOG def
TABLE_SCHEMA information_schema
+TABLE_NAME APPLICABLE_ROLES
+TABLE_TYPE SYSTEM VIEW
+ENGINE MEMORY
+VERSION 10
+ROW_FORMAT Fixed
+TABLE_ROWS #TBLR#
+AVG_ROW_LENGTH #ARL#
+DATA_LENGTH #DL#
+MAX_DATA_LENGTH #MDL#
+INDEX_LENGTH #IL#
+DATA_FREE #DF#
+AUTO_INCREMENT NULL
+CREATE_TIME #CRT#
+UPDATE_TIME #UT#
+CHECK_TIME #CT#
+TABLE_COLLATION utf8_general_ci
+CHECKSUM NULL
+CREATE_OPTIONS #CO#
+TABLE_COMMENT #TC#
+user_comment
+Separator -----------------------------------------------------
+TABLE_CATALOG def
+TABLE_SCHEMA information_schema
TABLE_NAME CHARACTER_SETS
TABLE_TYPE SYSTEM VIEW
ENGINE MEMORY
@@ -177,6 +200,29 @@ user_comment
Separator -----------------------------------------------------
TABLE_CATALOG def
TABLE_SCHEMA information_schema
+TABLE_NAME ENABLED_ROLES
+TABLE_TYPE SYSTEM VIEW
+ENGINE MEMORY
+VERSION 10
+ROW_FORMAT Fixed
+TABLE_ROWS #TBLR#
+AVG_ROW_LENGTH #ARL#
+DATA_LENGTH #DL#
+MAX_DATA_LENGTH #MDL#
+INDEX_LENGTH #IL#
+DATA_FREE #DF#
+AUTO_INCREMENT NULL
+CREATE_TIME #CRT#
+UPDATE_TIME #UT#
+CHECK_TIME #CT#
+TABLE_COLLATION utf8_general_ci
+CHECKSUM NULL
+CREATE_OPTIONS #CO#
+TABLE_COMMENT #TC#
+user_comment
+Separator -----------------------------------------------------
+TABLE_CATALOG def
+TABLE_SCHEMA information_schema
TABLE_NAME ENGINES
TABLE_TYPE SYSTEM VIEW
ENGINE MEMORY
@@ -862,6 +908,29 @@ user_comment
Separator -----------------------------------------------------
TABLE_CATALOG def
TABLE_SCHEMA information_schema
+TABLE_NAME APPLICABLE_ROLES
+TABLE_TYPE SYSTEM VIEW
+ENGINE MEMORY
+VERSION 10
+ROW_FORMAT Fixed
+TABLE_ROWS #TBLR#
+AVG_ROW_LENGTH #ARL#
+DATA_LENGTH #DL#
+MAX_DATA_LENGTH #MDL#
+INDEX_LENGTH #IL#
+DATA_FREE #DF#
+AUTO_INCREMENT NULL
+CREATE_TIME #CRT#
+UPDATE_TIME #UT#
+CHECK_TIME #CT#
+TABLE_COLLATION utf8_general_ci
+CHECKSUM NULL
+CREATE_OPTIONS #CO#
+TABLE_COMMENT #TC#
+user_comment
+Separator -----------------------------------------------------
+TABLE_CATALOG def
+TABLE_SCHEMA information_schema
TABLE_NAME CHARACTER_SETS
TABLE_TYPE SYSTEM VIEW
ENGINE MEMORY
@@ -1000,6 +1069,29 @@ user_comment
Separator -----------------------------------------------------
TABLE_CATALOG def
TABLE_SCHEMA information_schema
+TABLE_NAME ENABLED_ROLES
+TABLE_TYPE SYSTEM VIEW
+ENGINE MEMORY
+VERSION 10
+ROW_FORMAT Fixed
+TABLE_ROWS #TBLR#
+AVG_ROW_LENGTH #ARL#
+DATA_LENGTH #DL#
+MAX_DATA_LENGTH #MDL#
+INDEX_LENGTH #IL#
+DATA_FREE #DF#
+AUTO_INCREMENT NULL
+CREATE_TIME #CRT#
+UPDATE_TIME #UT#
+CHECK_TIME #CT#
+TABLE_COLLATION utf8_general_ci
+CHECKSUM NULL
+CREATE_OPTIONS #CO#
+TABLE_COMMENT #TC#
+user_comment
+Separator -----------------------------------------------------
+TABLE_CATALOG def
+TABLE_SCHEMA information_schema
TABLE_NAME ENGINES
TABLE_TYPE SYSTEM VIEW
ENGINE MEMORY
diff --git a/mysql-test/suite/funcs_1/r/is_tables_is_embedded.result b/mysql-test/suite/funcs_1/r/is_tables_is_embedded.result
index 5d3770c42de..5ecac2f296f 100644
--- a/mysql-test/suite/funcs_1/r/is_tables_is_embedded.result
+++ b/mysql-test/suite/funcs_1/r/is_tables_is_embedded.result
@@ -39,6 +39,29 @@ user_comment
Separator -----------------------------------------------------
TABLE_CATALOG def
TABLE_SCHEMA information_schema
+TABLE_NAME APPLICABLE_ROLES
+TABLE_TYPE SYSTEM VIEW
+ENGINE MEMORY
+VERSION 10
+ROW_FORMAT Fixed
+TABLE_ROWS #TBLR#
+AVG_ROW_LENGTH #ARL#
+DATA_LENGTH #DL#
+MAX_DATA_LENGTH #MDL#
+INDEX_LENGTH #IL#
+DATA_FREE #DF#
+AUTO_INCREMENT NULL
+CREATE_TIME #CRT#
+UPDATE_TIME #UT#
+CHECK_TIME #CT#
+TABLE_COLLATION utf8_general_ci
+CHECKSUM NULL
+CREATE_OPTIONS #CO#
+TABLE_COMMENT #TC#
+user_comment
+Separator -----------------------------------------------------
+TABLE_CATALOG def
+TABLE_SCHEMA information_schema
TABLE_NAME CHARACTER_SETS
TABLE_TYPE SYSTEM VIEW
ENGINE MEMORY
@@ -177,6 +200,29 @@ user_comment
Separator -----------------------------------------------------
TABLE_CATALOG def
TABLE_SCHEMA information_schema
+TABLE_NAME ENABLED_ROLES
+TABLE_TYPE SYSTEM VIEW
+ENGINE MEMORY
+VERSION 10
+ROW_FORMAT Fixed
+TABLE_ROWS #TBLR#
+AVG_ROW_LENGTH #ARL#
+DATA_LENGTH #DL#
+MAX_DATA_LENGTH #MDL#
+INDEX_LENGTH #IL#
+DATA_FREE #DF#
+AUTO_INCREMENT NULL
+CREATE_TIME #CRT#
+UPDATE_TIME #UT#
+CHECK_TIME #CT#
+TABLE_COLLATION utf8_general_ci
+CHECKSUM NULL
+CREATE_OPTIONS #CO#
+TABLE_COMMENT #TC#
+user_comment
+Separator -----------------------------------------------------
+TABLE_CATALOG def
+TABLE_SCHEMA information_schema
TABLE_NAME ENGINES
TABLE_TYPE SYSTEM VIEW
ENGINE MEMORY
@@ -862,6 +908,29 @@ user_comment
Separator -----------------------------------------------------
TABLE_CATALOG def
TABLE_SCHEMA information_schema
+TABLE_NAME APPLICABLE_ROLES
+TABLE_TYPE SYSTEM VIEW
+ENGINE MEMORY
+VERSION 10
+ROW_FORMAT Fixed
+TABLE_ROWS #TBLR#
+AVG_ROW_LENGTH #ARL#
+DATA_LENGTH #DL#
+MAX_DATA_LENGTH #MDL#
+INDEX_LENGTH #IL#
+DATA_FREE #DF#
+AUTO_INCREMENT NULL
+CREATE_TIME #CRT#
+UPDATE_TIME #UT#
+CHECK_TIME #CT#
+TABLE_COLLATION utf8_general_ci
+CHECKSUM NULL
+CREATE_OPTIONS #CO#
+TABLE_COMMENT #TC#
+user_comment
+Separator -----------------------------------------------------
+TABLE_CATALOG def
+TABLE_SCHEMA information_schema
TABLE_NAME CHARACTER_SETS
TABLE_TYPE SYSTEM VIEW
ENGINE MEMORY
@@ -1000,6 +1069,29 @@ user_comment
Separator -----------------------------------------------------
TABLE_CATALOG def
TABLE_SCHEMA information_schema
+TABLE_NAME ENABLED_ROLES
+TABLE_TYPE SYSTEM VIEW
+ENGINE MEMORY
+VERSION 10
+ROW_FORMAT Fixed
+TABLE_ROWS #TBLR#
+AVG_ROW_LENGTH #ARL#
+DATA_LENGTH #DL#
+MAX_DATA_LENGTH #MDL#
+INDEX_LENGTH #IL#
+DATA_FREE #DF#
+AUTO_INCREMENT NULL
+CREATE_TIME #CRT#
+UPDATE_TIME #UT#
+CHECK_TIME #CT#
+TABLE_COLLATION utf8_general_ci
+CHECKSUM NULL
+CREATE_OPTIONS #CO#
+TABLE_COMMENT #TC#
+user_comment
+Separator -----------------------------------------------------
+TABLE_CATALOG def
+TABLE_SCHEMA information_schema
TABLE_NAME ENGINES
TABLE_TYPE SYSTEM VIEW
ENGINE MEMORY
diff --git a/mysql-test/suite/funcs_1/r/is_tables_mysql.result b/mysql-test/suite/funcs_1/r/is_tables_mysql.result
index 4587538ea0b..1eeaba15838 100644
--- a/mysql-test/suite/funcs_1/r/is_tables_mysql.result
+++ b/mysql-test/suite/funcs_1/r/is_tables_mysql.result
@@ -451,6 +451,29 @@ user_comment User proxy privileges
Separator -----------------------------------------------------
TABLE_CATALOG def
TABLE_SCHEMA mysql
+TABLE_NAME roles_mapping
+TABLE_TYPE BASE TABLE
+ENGINE MYISAM_OR_MARIA
+VERSION 10
+ROW_FORMAT Fixed
+TABLE_ROWS #TBLR#
+AVG_ROW_LENGTH #ARL#
+DATA_LENGTH #DL#
+MAX_DATA_LENGTH #MDL#
+INDEX_LENGTH #IL#
+DATA_FREE #DF#
+AUTO_INCREMENT NULL
+CREATE_TIME #CRT#
+UPDATE_TIME #UT#
+CHECK_TIME #CT#
+TABLE_COLLATION utf8_bin
+CHECKSUM NULL
+CREATE_OPTIONS #CO#
+TABLE_COMMENT #TC#
+user_comment Granted roles
+Separator -----------------------------------------------------
+TABLE_CATALOG def
+TABLE_SCHEMA mysql
TABLE_NAME servers
TABLE_TYPE BASE TABLE
ENGINE MYISAM_OR_MARIA
diff --git a/mysql-test/suite/funcs_1/r/is_tables_mysql_embedded.result b/mysql-test/suite/funcs_1/r/is_tables_mysql_embedded.result
index 1d0951c1de2..6c0b0db07eb 100644
--- a/mysql-test/suite/funcs_1/r/is_tables_mysql_embedded.result
+++ b/mysql-test/suite/funcs_1/r/is_tables_mysql_embedded.result
@@ -451,6 +451,29 @@ user_comment User proxy privileges
Separator -----------------------------------------------------
TABLE_CATALOG def
TABLE_SCHEMA mysql
+TABLE_NAME roles_mapping
+TABLE_TYPE BASE TABLE
+ENGINE MYISAM_OR_MARIA
+VERSION 10
+ROW_FORMAT Fixed
+TABLE_ROWS #TBLR#
+AVG_ROW_LENGTH #ARL#
+DATA_LENGTH #DL#
+MAX_DATA_LENGTH #MDL#
+INDEX_LENGTH #IL#
+DATA_FREE #DF#
+AUTO_INCREMENT NULL
+CREATE_TIME #CRT#
+UPDATE_TIME #UT#
+CHECK_TIME #CT#
+TABLE_COLLATION utf8_bin
+CHECKSUM NULL
+CREATE_OPTIONS #CO#
+TABLE_COMMENT #TC#
+user_comment Granted roles
+Separator -----------------------------------------------------
+TABLE_CATALOG def
+TABLE_SCHEMA mysql
TABLE_NAME servers
TABLE_TYPE BASE TABLE
ENGINE MYISAM_OR_MARIA
@@ -1134,6 +1157,29 @@ user_comment User proxy privileges
Separator -----------------------------------------------------
TABLE_CATALOG def
TABLE_SCHEMA mysql
+TABLE_NAME roles_mapping
+TABLE_TYPE BASE TABLE
+ENGINE MYISAM_OR_MARIA
+VERSION 10
+ROW_FORMAT Fixed
+TABLE_ROWS #TBLR#
+AVG_ROW_LENGTH #ARL#
+DATA_LENGTH #DL#
+MAX_DATA_LENGTH #MDL#
+INDEX_LENGTH #IL#
+DATA_FREE #DF#
+AUTO_INCREMENT NULL
+CREATE_TIME #CRT#
+UPDATE_TIME #UT#
+CHECK_TIME #CT#
+TABLE_COLLATION utf8_bin
+CHECKSUM NULL
+CREATE_OPTIONS #CO#
+TABLE_COMMENT #TC#
+user_comment Granted roles
+Separator -----------------------------------------------------
+TABLE_CATALOG def
+TABLE_SCHEMA mysql
TABLE_NAME servers
TABLE_TYPE BASE TABLE
ENGINE MYISAM_OR_MARIA
diff --git a/mysql-test/suite/funcs_1/r/is_user_privileges.result b/mysql-test/suite/funcs_1/r/is_user_privileges.result
index 031067f2e64..030d7152121 100644
--- a/mysql-test/suite/funcs_1/r/is_user_privileges.result
+++ b/mysql-test/suite/funcs_1/r/is_user_privileges.result
@@ -28,21 +28,21 @@ DROP FUNCTION test.f1;
#########################################################################
DESCRIBE information_schema.USER_PRIVILEGES;
Field Type Null Key Default Extra
-GRANTEE varchar(81) NO
+GRANTEE varchar(190) NO
TABLE_CATALOG varchar(512) NO
PRIVILEGE_TYPE varchar(64) NO
IS_GRANTABLE varchar(3) NO
SHOW CREATE TABLE information_schema.USER_PRIVILEGES;
Table Create Table
USER_PRIVILEGES CREATE TEMPORARY TABLE `USER_PRIVILEGES` (
- `GRANTEE` varchar(81) NOT NULL DEFAULT '',
+ `GRANTEE` varchar(190) NOT NULL DEFAULT '',
`TABLE_CATALOG` varchar(512) NOT NULL DEFAULT '',
`PRIVILEGE_TYPE` varchar(64) NOT NULL DEFAULT '',
`IS_GRANTABLE` varchar(3) NOT NULL DEFAULT ''
) ENGINE=MEMORY DEFAULT CHARSET=utf8
SHOW COLUMNS FROM information_schema.USER_PRIVILEGES;
Field Type Null Key Default Extra
-GRANTEE varchar(81) NO
+GRANTEE varchar(190) NO
TABLE_CATALOG varchar(512) NO
PRIVILEGE_TYPE varchar(64) NO
IS_GRANTABLE varchar(3) NO
@@ -130,6 +130,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
Host localhost
User testuser2
Password
@@ -173,6 +174,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
Host localhost
User testuser3
Password
@@ -216,6 +218,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
#
# Add GRANT OPTION db_datadict.* to testuser1;
GRANT UPDATE ON db_datadict.* TO 'testuser1'@'localhost' WITH GRANT OPTION;
@@ -283,6 +286,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
Host localhost
User testuser2
Password
@@ -326,6 +330,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
Host localhost
User testuser3
Password
@@ -369,6 +374,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
# Establish connection testuser1 (user=testuser1)
SELECT * FROM information_schema.user_privileges
WHERE grantee LIKE '''testuser%'''
@@ -422,6 +428,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
Host localhost
User testuser2
Password
@@ -465,6 +472,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
Host localhost
User testuser3
Password
@@ -508,6 +516,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
SHOW GRANTS;
Grants for testuser1@localhost
GRANT USAGE ON *.* TO 'testuser1'@'localhost'
@@ -583,6 +592,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
Host localhost
User testuser2
Password
@@ -626,6 +636,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
Host localhost
User testuser3
Password
@@ -669,6 +680,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
GRANT SELECT ON *.* TO 'testuser1'@'localhost' WITH GRANT OPTION;
#
# Here <SELECT YES> is shown correctly for testuser1;
@@ -736,6 +748,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
Host localhost
User testuser2
Password
@@ -779,6 +792,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
Host localhost
User testuser3
Password
@@ -822,6 +836,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
# Switch to connection testuser1
SELECT * FROM information_schema.user_privileges
WHERE grantee LIKE '''testuser%'''
@@ -875,6 +890,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
Host localhost
User testuser2
Password
@@ -918,6 +934,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
Host localhost
User testuser3
Password
@@ -961,6 +978,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
SHOW GRANTS;
Grants for testuser1@localhost
GRANT SELECT ON *.* TO 'testuser1'@'localhost' WITH GRANT OPTION
@@ -1066,6 +1084,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
Host localhost
User testuser2
Password
@@ -1109,6 +1128,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
Host localhost
User testuser3
Password
@@ -1152,6 +1172,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
# Switch to connection testuser1
SELECT * FROM information_schema.user_privileges
WHERE grantee LIKE '''testuser%'''
@@ -1252,6 +1273,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
Host localhost
User testuser2
Password
@@ -1295,6 +1317,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
Host localhost
User testuser3
Password
@@ -1338,6 +1361,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
# Switch to connection testuser1
SELECT * FROM information_schema.user_privileges
WHERE grantee LIKE '''testuser%'''
@@ -1391,6 +1415,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
Host localhost
User testuser2
Password
@@ -1434,6 +1459,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
Host localhost
User testuser3
Password
@@ -1477,6 +1503,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
SHOW GRANTS;
Grants for testuser1@localhost
GRANT USAGE ON *.* TO 'testuser1'@'localhost'
@@ -1537,6 +1564,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
Host localhost
User testuser2
Password
@@ -1580,6 +1608,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
Host localhost
User testuser3
Password
@@ -1623,6 +1652,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
SHOW GRANTS;
Grants for testuser1@localhost
GRANT USAGE ON *.* TO 'testuser1'@'localhost'
@@ -1698,6 +1728,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
Host localhost
User testuser2
Password
@@ -1741,6 +1772,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
Host localhost
User testuser3
Password
@@ -1784,6 +1816,7 @@ max_user_connections 0
plugin
authentication_string
password_expired N
+is_role N
# Switch to connection testuser1
SELECT * FROM information_schema.user_privileges
WHERE grantee LIKE '''testuser%'''
diff --git a/mysql-test/suite/funcs_1/r/memory_trig_03e.result b/mysql-test/suite/funcs_1/r/memory_trig_03e.result
index 32252c93890..5f05f7c0af5 100644
--- a/mysql-test/suite/funcs_1/r/memory_trig_03e.result
+++ b/mysql-test/suite/funcs_1/r/memory_trig_03e.result
@@ -574,7 +574,7 @@ root@localhost
show grants;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
-GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
drop trigger trg1_1;
use priv_db;
diff --git a/mysql-test/suite/funcs_1/r/myisam_trig_03e.result b/mysql-test/suite/funcs_1/r/myisam_trig_03e.result
index 1b3342ea581..c34525a52f8 100644
--- a/mysql-test/suite/funcs_1/r/myisam_trig_03e.result
+++ b/mysql-test/suite/funcs_1/r/myisam_trig_03e.result
@@ -574,7 +574,7 @@ root@localhost
show grants;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
-GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
drop trigger trg1_1;
use priv_db;
diff --git a/mysql-test/suite/funcs_1/r/processlist_priv_no_prot.result b/mysql-test/suite/funcs_1/r/processlist_priv_no_prot.result
index f4b60c8c252..f803780f57e 100644
--- a/mysql-test/suite/funcs_1/r/processlist_priv_no_prot.result
+++ b/mysql-test/suite/funcs_1/r/processlist_priv_no_prot.result
@@ -70,7 +70,7 @@ ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_
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 PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
CREATE INDEX i_processlist ON processlist (user);
ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_schema'
DROP TABLE processlist;
diff --git a/mysql-test/suite/funcs_1/r/processlist_priv_ps.result b/mysql-test/suite/funcs_1/r/processlist_priv_ps.result
index 6efa1397796..70d88904e60 100644
--- a/mysql-test/suite/funcs_1/r/processlist_priv_ps.result
+++ b/mysql-test/suite/funcs_1/r/processlist_priv_ps.result
@@ -70,7 +70,7 @@ ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_
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 PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
CREATE INDEX i_processlist ON processlist (user);
ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_schema'
DROP TABLE processlist;
diff --git a/mysql-test/suite/funcs_1/t/is_engines_federated.opt b/mysql-test/suite/funcs_1/t/is_engines_federated.opt
deleted file mode 100644
index 198144ce5ac..00000000000
--- a/mysql-test/suite/funcs_1/t/is_engines_federated.opt
+++ /dev/null
@@ -1,2 +0,0 @@
---loose-federated
---plugin-load-add=$HA_FEDERATEDX_SO
diff --git a/mysql-test/suite/perfschema/r/column_privilege.result b/mysql-test/suite/perfschema/r/column_privilege.result
index b013ea3fcad..e4ad9200272 100644
--- a/mysql-test/suite/perfschema/r/column_privilege.result
+++ b/mysql-test/suite/perfschema/r/column_privilege.result
@@ -1,7 +1,7 @@
show grants;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
-GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
grant usage on *.* to 'pfs_user_5'@localhost with GRANT OPTION;
grant SELECT(thread_id, event_id) on performance_schema.events_waits_current
to 'pfs_user_5'@localhost;
diff --git a/mysql-test/suite/perfschema/r/digest_table_full.result b/mysql-test/suite/perfschema/r/digest_table_full.result
index 51c6404e332..bba3bc8d9e5 100644
--- a/mysql-test/suite/perfschema/r/digest_table_full.result
+++ b/mysql-test/suite/perfschema/r/digest_table_full.result
@@ -113,7 +113,7 @@ SELECT SCHEMA_NAME, DIGEST, DIGEST_TEXT, COUNT_STAR, SUM_ROWS_AFFECTED, SUM_WARN
SUM_ERRORS FROM performance_schema.events_statements_summary_by_digest;
SCHEMA_NAME DIGEST DIGEST_TEXT COUNT_STAR SUM_ROWS_AFFECTED SUM_WARNINGS SUM_ERRORS
NULL NULL NULL 55 32 1 2
-statements_digest 9dc10607f0c5f847fdec5021ec55454c TRUNCATE TABLE performance_schema . events_statements_summary_by_digest 1 0 0 0
+statements_digest b7123a38bb99ce09f09d127df4e39b18 TRUNCATE TABLE performance_schema . events_statements_summary_by_digest 1 0 0 0
SHOW VARIABLES LIKE "performance_schema_digests_size";
Variable_name Value
performance_schema_digests_size 2
diff --git a/mysql-test/suite/perfschema/r/ortho_iter.result b/mysql-test/suite/perfschema/r/ortho_iter.result
index 0887b9e4c92..0eb5ff8c737 100644
--- a/mysql-test/suite/perfschema/r/ortho_iter.result
+++ b/mysql-test/suite/perfschema/r/ortho_iter.result
@@ -115,7 +115,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/privilege.result b/mysql-test/suite/perfschema/r/privilege.result
index 85e46759f22..49d72fc499f 100644
--- a/mysql-test/suite/perfschema/r/privilege.result
+++ b/mysql-test/suite/perfschema/r/privilege.result
@@ -1,7 +1,7 @@
show grants;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
-GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
grant ALL on *.* to 'pfs_user_1'@localhost with GRANT OPTION;
grant ALL on performance_schema.* to 'pfs_user_2'@localhost
with GRANT OPTION;
diff --git a/mysql-test/suite/perfschema/r/privilege_table_io.result b/mysql-test/suite/perfschema/r/privilege_table_io.result
index 9a50503e232..02e3ed7da62 100644
--- a/mysql-test/suite/perfschema/r/privilege_table_io.result
+++ b/mysql-test/suite/perfschema/r/privilege_table_io.result
@@ -46,7 +46,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
@@ -104,6 +104,7 @@ wait/io/table/sql/handler handler.cc: TABLE mysql db fetch NULL
wait/io/table/sql/handler handler.cc: TABLE mysql proxies_priv fetch NULL
wait/io/table/sql/handler handler.cc: TABLE mysql proxies_priv fetch NULL
wait/io/table/sql/handler handler.cc: TABLE mysql proxies_priv fetch NULL
+wait/io/table/sql/handler handler.cc: TABLE mysql roles_mapping fetch NULL
wait/io/table/sql/handler handler.cc: TABLE mysql tables_priv fetch NULL
wait/io/table/sql/handler handler.cc: TABLE mysql procs_priv fetch NULL
wait/io/table/sql/handler handler.cc: TABLE mysql servers fetch NULL
diff --git a/mysql-test/suite/perfschema/r/start_server_disable_idle.result b/mysql-test/suite/perfschema/r/start_server_disable_idle.result
index edc3ab5199a..dc652f8bbbf 100644
--- a/mysql-test/suite/perfschema/r/start_server_disable_idle.result
+++ b/mysql-test/suite/perfschema/r/start_server_disable_idle.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_disable_stages.result b/mysql-test/suite/perfschema/r/start_server_disable_stages.result
index 5b5d9d3f975..2984360fad9 100644
--- a/mysql-test/suite/perfschema/r/start_server_disable_stages.result
+++ b/mysql-test/suite/perfschema/r/start_server_disable_stages.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_disable_statements.result b/mysql-test/suite/perfschema/r/start_server_disable_statements.result
index 039cd964a26..2f0cff977e7 100644
--- a/mysql-test/suite/perfschema/r/start_server_disable_statements.result
+++ b/mysql-test/suite/perfschema/r/start_server_disable_statements.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_disable_waits.result b/mysql-test/suite/perfschema/r/start_server_disable_waits.result
index 686933640a2..1a1062d3201 100644
--- a/mysql-test/suite/perfschema/r/start_server_disable_waits.result
+++ b/mysql-test/suite/perfschema/r/start_server_disable_waits.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_innodb.result b/mysql-test/suite/perfschema/r/start_server_innodb.result
index 212cee202c2..58d4e611812 100644
--- a/mysql-test/suite/perfschema/r/start_server_innodb.result
+++ b/mysql-test/suite/perfschema/r/start_server_innodb.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_no_account.result b/mysql-test/suite/perfschema/r/start_server_no_account.result
index 37b150d2bbc..cf5b5ba09e0 100644
--- a/mysql-test/suite/perfschema/r/start_server_no_account.result
+++ b/mysql-test/suite/perfschema/r/start_server_no_account.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_no_cond_class.result b/mysql-test/suite/perfschema/r/start_server_no_cond_class.result
index de0e7deade7..805bb3a2cce 100644
--- a/mysql-test/suite/perfschema/r/start_server_no_cond_class.result
+++ b/mysql-test/suite/perfschema/r/start_server_no_cond_class.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_no_cond_inst.result b/mysql-test/suite/perfschema/r/start_server_no_cond_inst.result
index 21cd2dae2cd..172ca779e18 100644
--- a/mysql-test/suite/perfschema/r/start_server_no_cond_inst.result
+++ b/mysql-test/suite/perfschema/r/start_server_no_cond_inst.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_no_file_class.result b/mysql-test/suite/perfschema/r/start_server_no_file_class.result
index 2db110df4aa..e8c1580fd03 100644
--- a/mysql-test/suite/perfschema/r/start_server_no_file_class.result
+++ b/mysql-test/suite/perfschema/r/start_server_no_file_class.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_no_file_inst.result b/mysql-test/suite/perfschema/r/start_server_no_file_inst.result
index 84b9b09cd80..02be2fe9585 100644
--- a/mysql-test/suite/perfschema/r/start_server_no_file_inst.result
+++ b/mysql-test/suite/perfschema/r/start_server_no_file_inst.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_no_host.result b/mysql-test/suite/perfschema/r/start_server_no_host.result
index 6ef5d352c24..7d5fdaf7134 100644
--- a/mysql-test/suite/perfschema/r/start_server_no_host.result
+++ b/mysql-test/suite/perfschema/r/start_server_no_host.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_no_mutex_class.result b/mysql-test/suite/perfschema/r/start_server_no_mutex_class.result
index 2ea53fb03d5..1ee6c681d01 100644
--- a/mysql-test/suite/perfschema/r/start_server_no_mutex_class.result
+++ b/mysql-test/suite/perfschema/r/start_server_no_mutex_class.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_no_mutex_inst.result b/mysql-test/suite/perfschema/r/start_server_no_mutex_inst.result
index 91544690640..6d51baa0d2f 100644
--- a/mysql-test/suite/perfschema/r/start_server_no_mutex_inst.result
+++ b/mysql-test/suite/perfschema/r/start_server_no_mutex_inst.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_no_rwlock_class.result b/mysql-test/suite/perfschema/r/start_server_no_rwlock_class.result
index 701c9ef1f3d..7bead1375fe 100644
--- a/mysql-test/suite/perfschema/r/start_server_no_rwlock_class.result
+++ b/mysql-test/suite/perfschema/r/start_server_no_rwlock_class.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_no_rwlock_inst.result b/mysql-test/suite/perfschema/r/start_server_no_rwlock_inst.result
index 4076a36e70e..f4a6264e573 100644
--- a/mysql-test/suite/perfschema/r/start_server_no_rwlock_inst.result
+++ b/mysql-test/suite/perfschema/r/start_server_no_rwlock_inst.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 0
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_no_setup_actors.result b/mysql-test/suite/perfschema/r/start_server_no_setup_actors.result
index 4e1d4d99c7b..6f2dc52e485 100644
--- a/mysql-test/suite/perfschema/r/start_server_no_setup_actors.result
+++ b/mysql-test/suite/perfschema/r/start_server_no_setup_actors.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_no_setup_objects.result b/mysql-test/suite/perfschema/r/start_server_no_setup_objects.result
index 6ba9e528ed1..210a5e677e9 100644
--- a/mysql-test/suite/perfschema/r/start_server_no_setup_objects.result
+++ b/mysql-test/suite/perfschema/r/start_server_no_setup_objects.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_no_socket_class.result b/mysql-test/suite/perfschema/r/start_server_no_socket_class.result
index 9805a87afb7..e495686fa63 100644
--- a/mysql-test/suite/perfschema/r/start_server_no_socket_class.result
+++ b/mysql-test/suite/perfschema/r/start_server_no_socket_class.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 0
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_no_socket_inst.result b/mysql-test/suite/perfschema/r/start_server_no_socket_inst.result
index a3bacb07316..474875f2de7 100644
--- a/mysql-test/suite/perfschema/r/start_server_no_socket_inst.result
+++ b/mysql-test/suite/perfschema/r/start_server_no_socket_inst.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 0
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_no_stage_class.result b/mysql-test/suite/perfschema/r/start_server_no_stage_class.result
index 31ea6354c8e..6ccd55451ac 100644
--- a/mysql-test/suite/perfschema/r/start_server_no_stage_class.result
+++ b/mysql-test/suite/perfschema/r/start_server_no_stage_class.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 0
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_no_stages_history.result b/mysql-test/suite/perfschema/r/start_server_no_stages_history.result
index c398c4f5e6b..6443cba13df 100644
--- a/mysql-test/suite/perfschema/r/start_server_no_stages_history.result
+++ b/mysql-test/suite/perfschema/r/start_server_no_stages_history.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_no_stages_history_long.result b/mysql-test/suite/perfschema/r/start_server_no_stages_history_long.result
index febdc87f8f9..ad44f26b880 100644
--- a/mysql-test/suite/perfschema/r/start_server_no_stages_history_long.result
+++ b/mysql-test/suite/perfschema/r/start_server_no_stages_history_long.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_no_statements_history.result b/mysql-test/suite/perfschema/r/start_server_no_statements_history.result
index 78520399866..cd21d415dad 100644
--- a/mysql-test/suite/perfschema/r/start_server_no_statements_history.result
+++ b/mysql-test/suite/perfschema/r/start_server_no_statements_history.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_no_statements_history_long.result b/mysql-test/suite/perfschema/r/start_server_no_statements_history_long.result
index c45966bd74b..2fa222208b3 100644
--- a/mysql-test/suite/perfschema/r/start_server_no_statements_history_long.result
+++ b/mysql-test/suite/perfschema/r/start_server_no_statements_history_long.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_no_table_hdl.result b/mysql-test/suite/perfschema/r/start_server_no_table_hdl.result
index 0c35006b0de..46d18a2e354 100644
--- a/mysql-test/suite/perfschema/r/start_server_no_table_hdl.result
+++ b/mysql-test/suite/perfschema/r/start_server_no_table_hdl.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 0
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_no_table_inst.result b/mysql-test/suite/perfschema/r/start_server_no_table_inst.result
index 63e71dd7361..a098c3556f0 100644
--- a/mysql-test/suite/perfschema/r/start_server_no_table_inst.result
+++ b/mysql-test/suite/perfschema/r/start_server_no_table_inst.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 0
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_no_thread_class.result b/mysql-test/suite/perfschema/r/start_server_no_thread_class.result
index 7e87cf4363b..da1de089ef8 100644
--- a/mysql-test/suite/perfschema/r/start_server_no_thread_class.result
+++ b/mysql-test/suite/perfschema/r/start_server_no_thread_class.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 0
diff --git a/mysql-test/suite/perfschema/r/start_server_no_thread_inst.result b/mysql-test/suite/perfschema/r/start_server_no_thread_inst.result
index 5b0233d6727..e8663c0baf3 100644
--- a/mysql-test/suite/perfschema/r/start_server_no_thread_inst.result
+++ b/mysql-test/suite/perfschema/r/start_server_no_thread_inst.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_no_user.result b/mysql-test/suite/perfschema/r/start_server_no_user.result
index 485110838ff..84c9da61842 100644
--- a/mysql-test/suite/perfschema/r/start_server_no_user.result
+++ b/mysql-test/suite/perfschema/r/start_server_no_user.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_no_waits_history.result b/mysql-test/suite/perfschema/r/start_server_no_waits_history.result
index 1a692dfb5ae..ba1b38ec1ae 100644
--- a/mysql-test/suite/perfschema/r/start_server_no_waits_history.result
+++ b/mysql-test/suite/perfschema/r/start_server_no_waits_history.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_no_waits_history_long.result b/mysql-test/suite/perfschema/r/start_server_no_waits_history_long.result
index 02ce9db20be..db1f905da73 100644
--- a/mysql-test/suite/perfschema/r/start_server_no_waits_history_long.result
+++ b/mysql-test/suite/perfschema/r/start_server_no_waits_history_long.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_off.result b/mysql-test/suite/perfschema/r/start_server_off.result
index d8bf697ea73..9d1259394a3 100644
--- a/mysql-test/suite/perfschema/r/start_server_off.result
+++ b/mysql-test/suite/perfschema/r/start_server_off.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/start_server_on.result b/mysql-test/suite/perfschema/r/start_server_on.result
index 212cee202c2..58d4e611812 100644
--- a/mysql-test/suite/perfschema/r/start_server_on.result
+++ b/mysql-test/suite/perfschema/r/start_server_on.result
@@ -93,7 +93,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/statement_digest.result b/mysql-test/suite/perfschema/r/statement_digest.result
index 862239b015c..82bb1211cbc 100644
--- a/mysql-test/suite/perfschema/r/statement_digest.result
+++ b/mysql-test/suite/perfschema/r/statement_digest.result
@@ -112,43 +112,43 @@ DROP TRIGGER trg;
SELECT SCHEMA_NAME, DIGEST, DIGEST_TEXT, COUNT_STAR, SUM_ROWS_AFFECTED, SUM_WARNINGS,
SUM_ERRORS FROM performance_schema.events_statements_summary_by_digest;
SCHEMA_NAME DIGEST DIGEST_TEXT COUNT_STAR SUM_ROWS_AFFECTED SUM_WARNINGS SUM_ERRORS
-statements_digest 9dc10607f0c5f847fdec5021ec55454c TRUNCATE TABLE performance_schema . events_statements_summary_by_digest 1 0 0 0
-statements_digest 70dfef34974722c072dfde594161c53f SELECT ? FROM t1 1 0 0 0
-statements_digest 92afc5f0de28802cf90bac0d2ecc28ea SELECT ? FROM `t1` 1 0 0 0
-statements_digest a3c82be9beb57590c35da4ffdde342f4 SELECT ?, ... FROM t1 2 0 0 0
-statements_digest 4aa210c131d9f75ee7acfea2b4f84f8e SELECT ? FROM t2 1 0 0 0
-statements_digest 4160fd3e7a59e6a4fab0d5188e306d8f SELECT ?, ... FROM t2 2 0 0 0
-statements_digest 033964070b3cfe70ea0e6edf21ab5595 INSERT INTO t1 VALUES (?) 1 1 0 0
-statements_digest cd2843f72a1484fb137664855d268a23 INSERT INTO t2 VALUES (?) 1 1 0 0
-statements_digest 51de047a21efe42b07e3af030330af0e INSERT INTO t3 VALUES (...) 4 4 0 0
-statements_digest 159f7021cd6b766d3f8f98d6f49c26c7 INSERT INTO t4 VALUES (...) 1 1 0 0
-statements_digest 0d298ad85eb763cfed01450d45ed311f INSERT INTO t5 VALUES (...) 1 1 0 0
-statements_digest 2872669f092c0878080b93d8d55a9efa INSERT INTO t1 VALUES (?) /* , ... */ 2 7 0 0
-statements_digest f8b8ffcea0840f0b796c04be7c4586a7 INSERT INTO t3 VALUES (...) /* , ... */ 1 3 0 0
-statements_digest ba4e21efb9e3704e86b2036505d76608 INSERT INTO t5 VALUES (...) /* , ... */ 1 3 0 0
-statements_digest 7b81bc3a5cc54098b68df4297456ca42 INSERT INTO t1 VALUES ( NULL ) 1 1 0 0
-statements_digest dc50d0a03e9f4c25493bbb660614a979 INSERT INTO t6 VALUES (...) 5 5 0 0
-statements_digest 772f22c3ca1b998449570d41d677b739 SELECT ? + ? 3 0 0 0
-statements_digest 4077abb1c6924eb2e61a4e6b5832e610 SELECT ? 1 0 0 0
-statements_digest dec987af5d13596c58a0b3382bf4be0a CREATE SCHEMA statements_digest_temp 2 2 0 0
-statements_digest 310050e7e30e166116fd9b04b9a39cec DROP SCHEMA statements_digest_temp 2 0 0 0
-statements_digest 1de70c587e0cc8f72f18f5532ea9ce41 SELECT ? FROM no_such_table 1 0 0 1
-statements_digest a0fcbd276415d695cbbd0ff9ea988fd1 CREATE TABLE dup_table ( c CHARACTER (?) ) 2 0 0 1
-statements_digest 47eff8114ef3ff7156ea1f894792a780 DROP TABLE dup_table 1 0 0 0
-statements_digest ca7b7a57283e104af725938959e04acc INSERT INTO t11 VALUES (?) 1 1 1 0
-statements_digest 09d296fc41f9a0a8e3badbfb548e69d9 SHOW WARNINGS 1 0 0 0
-statements_digest daef8b2773ba22b629205ac8d37275ba PREPARE stmt FROM ? 1 0 0 0
-statements_digest 933bf6630b020a5215edb27059bd68cc EXECUTE stmt 2 0 0 0
-statements_digest 45459d112476cdf389c4c43b25fe1751 DEALLOCATE PREPARE stmt 1 0 0 0
-statements_digest 317d254d2506653e151e8ca097fc8f18 CREATE PROCEDURE p1 ( ) BEGIN SELECT * FROM t12 ; END 1 0 0 0
-statements_digest 554e43a1cba81ed9076c04f39721c94b CALL p1 ( ) 2 0 0 0
-statements_digest aa9d2a149756e50d528de565a14d1d18 DROP PROCEDURE p1 1 0 0 0
-statements_digest e5674b0df28911bcb4fb4a1902b6e30a CREATE FUNCTION `func` ( a INTEGER , b INTEGER ) RETURNS INTEGER (?) RETURN a + b 1 0 0 0
-statements_digest a70c0561b972d4e50638d5ecdeef2ff1 SELECT func (...) 2 0 0 0
-statements_digest eba7f0de7c0682908b1810e99b3194e8 DROP FUNCTION func 1 0 0 0
-statements_digest 3f4217b75adb295c507c21edda9a7880 CREATE TRIGGER trg BEFORE INSERT ON t12 FOR EACH ROW SET @ ? := ? 1 0 0 0
-statements_digest 9628bd16a7f019ca3046a449ec9e4e30 INSERT INTO t12 VALUES (?) 2 2 0 0
-statements_digest d62fc8c82ef777e9c3af4f47743e0c46 DROP TRIGGER trg 1 0 0 0
+statements_digest b7123a38bb99ce09f09d127df4e39b18 TRUNCATE TABLE performance_schema . events_statements_summary_by_digest 1 0 0 0
+statements_digest b84133205e24517207248a0eefded78a SELECT ? FROM t1 1 0 0 0
+statements_digest 88a673e6a76a2bd1ad72dddc7e9be037 SELECT ? FROM `t1` 1 0 0 0
+statements_digest a885b0a3ae7886d11bfdc7c51b3d7110 SELECT ?, ... FROM t1 2 0 0 0
+statements_digest e3a97cc772f0acebfe7ee5537dfcc881 SELECT ? FROM t2 1 0 0 0
+statements_digest 9ecf822210da222eae9b56a0017765fc SELECT ?, ... FROM t2 2 0 0 0
+statements_digest 98bbad9fba6ace6566d118333c00c67d INSERT INTO t1 VALUES (?) 1 1 0 0
+statements_digest 724ab5bcd5f11b3975a65331c89443c0 INSERT INTO t2 VALUES (?) 1 1 0 0
+statements_digest a351a420a8ef3b894177d2620be682ca INSERT INTO t3 VALUES (...) 4 4 0 0
+statements_digest f66804d1ba3de87895f9a82c6cef04d4 INSERT INTO t4 VALUES (...) 1 1 0 0
+statements_digest 797b00d27cc1a212f4f4d61d3ad11e66 INSERT INTO t5 VALUES (...) 1 1 0 0
+statements_digest 90427cb3f602eaa97b1cc97c0ef16d85 INSERT INTO t1 VALUES (?) /* , ... */ 2 7 0 0
+statements_digest 1691e787cfe88075cb3e9fd48ac4a52b INSERT INTO t3 VALUES (...) /* , ... */ 1 3 0 0
+statements_digest cf401a585c798da2f55f72b0a99ded18 INSERT INTO t5 VALUES (...) /* , ... */ 1 3 0 0
+statements_digest 1e25bc6303e3968077c586dab9c5562c INSERT INTO t1 VALUES ( NULL ) 1 1 0 0
+statements_digest 30f72e28c64b3e6ca888715a848cd085 INSERT INTO t6 VALUES (...) 5 5 0 0
+statements_digest 551dce993b267c981c5b3eb285c2fe57 SELECT ? + ? 3 0 0 0
+statements_digest d31e1af4dc7ed5fe3ff61c78db7b327e SELECT ? 1 0 0 0
+statements_digest 33003a7b4de282603814a057945694d3 CREATE SCHEMA statements_digest_temp 2 2 0 0
+statements_digest 6ce84f85f37b9996e3dcbed9d55b88dd DROP SCHEMA statements_digest_temp 2 0 0 0
+statements_digest 08c862f2422dd8464a3b7b96d9de1dfa SELECT ? FROM no_such_table 1 0 0 1
+statements_digest c41b789a3176e6dbd8157848c6ff4aaf CREATE TABLE dup_table ( c CHARACTER (?) ) 2 0 0 1
+statements_digest fe693f8cf543b249a89f9f76c363d9d5 DROP TABLE dup_table 1 0 0 0
+statements_digest 5a6a862982ca17eff9038f2d852d848f INSERT INTO t11 VALUES (?) 1 1 1 0
+statements_digest b72d811ed58c8f2ec01e110bcad3138b SHOW WARNINGS 1 0 0 0
+statements_digest 63e18c50006c39c70200e63e720a9f0a PREPARE stmt FROM ? 1 0 0 0
+statements_digest eac5a2c580910e14eb0894ef21a25353 EXECUTE stmt 2 0 0 0
+statements_digest 5f1eaa4567c93974669fc403159245db DEALLOCATE PREPARE stmt 1 0 0 0
+statements_digest acb8e84440f68ee053d486688dfc88b2 CREATE PROCEDURE p1 ( ) BEGIN SELECT * FROM t12 ; END 1 0 0 0
+statements_digest 44c11865a2c9cd9f884bca10564ac818 CALL p1 ( ) 2 0 0 0
+statements_digest fb004af2d0db6f35a97ccdbbc51343ef DROP PROCEDURE p1 1 0 0 0
+statements_digest 6566febd24d7b17c53f75785ce94936c CREATE FUNCTION `func` ( a INTEGER , b INTEGER ) RETURNS INTEGER (?) RETURN a + b 1 0 0 0
+statements_digest 5bc097b58c334afe0875d7b74d0eb86e SELECT func (...) 2 0 0 0
+statements_digest 183cce493d199f32fad2174aab485298 DROP FUNCTION func 1 0 0 0
+statements_digest b0f05e1bd191be18730e2e24801a448d CREATE TRIGGER trg BEFORE INSERT ON t12 FOR EACH ROW SET @ ? := ? 1 0 0 0
+statements_digest 4a20ca3773c57af8a3949b76f446505a INSERT INTO t12 VALUES (?) 2 2 0 0
+statements_digest b345f3bef14924fea5ce7129cd374576 DROP TRIGGER trg 1 0 0 0
####################################
# CLEANUP
####################################
diff --git a/mysql-test/suite/perfschema/r/statement_digest_consumers.result b/mysql-test/suite/perfschema/r/statement_digest_consumers.result
index 48f13c1ed01..62776d0dba5 100644
--- a/mysql-test/suite/perfschema/r/statement_digest_consumers.result
+++ b/mysql-test/suite/perfschema/r/statement_digest_consumers.result
@@ -125,43 +125,43 @@ DROP TRIGGER trg;
####################################
SELECT schema_name, digest, digest_text, count_star FROM performance_schema.events_statements_summary_by_digest;
schema_name digest digest_text count_star
-statements_digest 9dc10607f0c5f847fdec5021ec55454c TRUNCATE TABLE performance_schema . events_statements_summary_by_digest 1
-statements_digest 70dfef34974722c072dfde594161c53f SELECT ? FROM t1 1
-statements_digest 92afc5f0de28802cf90bac0d2ecc28ea SELECT ? FROM `t1` 1
-statements_digest a3c82be9beb57590c35da4ffdde342f4 SELECT ?, ... FROM t1 2
-statements_digest 4aa210c131d9f75ee7acfea2b4f84f8e SELECT ? FROM t2 1
-statements_digest 4160fd3e7a59e6a4fab0d5188e306d8f SELECT ?, ... FROM t2 2
-statements_digest 033964070b3cfe70ea0e6edf21ab5595 INSERT INTO t1 VALUES (?) 1
-statements_digest cd2843f72a1484fb137664855d268a23 INSERT INTO t2 VALUES (?) 1
-statements_digest 51de047a21efe42b07e3af030330af0e INSERT INTO t3 VALUES (...) 4
-statements_digest 159f7021cd6b766d3f8f98d6f49c26c7 INSERT INTO t4 VALUES (...) 1
-statements_digest 0d298ad85eb763cfed01450d45ed311f INSERT INTO t5 VALUES (...) 1
-statements_digest 2872669f092c0878080b93d8d55a9efa INSERT INTO t1 VALUES (?) /* , ... */ 2
-statements_digest f8b8ffcea0840f0b796c04be7c4586a7 INSERT INTO t3 VALUES (...) /* , ... */ 1
-statements_digest ba4e21efb9e3704e86b2036505d76608 INSERT INTO t5 VALUES (...) /* , ... */ 1
-statements_digest 7b81bc3a5cc54098b68df4297456ca42 INSERT INTO t1 VALUES ( NULL ) 1
-statements_digest dc50d0a03e9f4c25493bbb660614a979 INSERT INTO t6 VALUES (...) 5
-statements_digest 772f22c3ca1b998449570d41d677b739 SELECT ? + ? 3
-statements_digest 4077abb1c6924eb2e61a4e6b5832e610 SELECT ? 1
-statements_digest dec987af5d13596c58a0b3382bf4be0a CREATE SCHEMA statements_digest_temp 2
-statements_digest 310050e7e30e166116fd9b04b9a39cec DROP SCHEMA statements_digest_temp 2
-statements_digest 1de70c587e0cc8f72f18f5532ea9ce41 SELECT ? FROM no_such_table 1
-statements_digest a0fcbd276415d695cbbd0ff9ea988fd1 CREATE TABLE dup_table ( c CHARACTER (?) ) 2
-statements_digest 47eff8114ef3ff7156ea1f894792a780 DROP TABLE dup_table 1
-statements_digest ca7b7a57283e104af725938959e04acc INSERT INTO t11 VALUES (?) 1
-statements_digest 09d296fc41f9a0a8e3badbfb548e69d9 SHOW WARNINGS 1
-statements_digest daef8b2773ba22b629205ac8d37275ba PREPARE stmt FROM ? 1
-statements_digest 933bf6630b020a5215edb27059bd68cc EXECUTE stmt 2
-statements_digest 45459d112476cdf389c4c43b25fe1751 DEALLOCATE PREPARE stmt 1
-statements_digest 317d254d2506653e151e8ca097fc8f18 CREATE PROCEDURE p1 ( ) BEGIN SELECT * FROM t12 ; END 1
-statements_digest 554e43a1cba81ed9076c04f39721c94b CALL p1 ( ) 2
-statements_digest aa9d2a149756e50d528de565a14d1d18 DROP PROCEDURE p1 1
-statements_digest e5674b0df28911bcb4fb4a1902b6e30a CREATE FUNCTION `func` ( a INTEGER , b INTEGER ) RETURNS INTEGER (?) RETURN a + b 1
-statements_digest a70c0561b972d4e50638d5ecdeef2ff1 SELECT func (...) 2
-statements_digest eba7f0de7c0682908b1810e99b3194e8 DROP FUNCTION func 1
-statements_digest 3f4217b75adb295c507c21edda9a7880 CREATE TRIGGER trg BEFORE INSERT ON t12 FOR EACH ROW SET @ ? := ? 1
-statements_digest 9628bd16a7f019ca3046a449ec9e4e30 INSERT INTO t12 VALUES (?) 2
-statements_digest d62fc8c82ef777e9c3af4f47743e0c46 DROP TRIGGER trg 1
+statements_digest b7123a38bb99ce09f09d127df4e39b18 TRUNCATE TABLE performance_schema . events_statements_summary_by_digest 1
+statements_digest b84133205e24517207248a0eefded78a SELECT ? FROM t1 1
+statements_digest 88a673e6a76a2bd1ad72dddc7e9be037 SELECT ? FROM `t1` 1
+statements_digest a885b0a3ae7886d11bfdc7c51b3d7110 SELECT ?, ... FROM t1 2
+statements_digest e3a97cc772f0acebfe7ee5537dfcc881 SELECT ? FROM t2 1
+statements_digest 9ecf822210da222eae9b56a0017765fc SELECT ?, ... FROM t2 2
+statements_digest 98bbad9fba6ace6566d118333c00c67d INSERT INTO t1 VALUES (?) 1
+statements_digest 724ab5bcd5f11b3975a65331c89443c0 INSERT INTO t2 VALUES (?) 1
+statements_digest a351a420a8ef3b894177d2620be682ca INSERT INTO t3 VALUES (...) 4
+statements_digest f66804d1ba3de87895f9a82c6cef04d4 INSERT INTO t4 VALUES (...) 1
+statements_digest 797b00d27cc1a212f4f4d61d3ad11e66 INSERT INTO t5 VALUES (...) 1
+statements_digest 90427cb3f602eaa97b1cc97c0ef16d85 INSERT INTO t1 VALUES (?) /* , ... */ 2
+statements_digest 1691e787cfe88075cb3e9fd48ac4a52b INSERT INTO t3 VALUES (...) /* , ... */ 1
+statements_digest cf401a585c798da2f55f72b0a99ded18 INSERT INTO t5 VALUES (...) /* , ... */ 1
+statements_digest 1e25bc6303e3968077c586dab9c5562c INSERT INTO t1 VALUES ( NULL ) 1
+statements_digest 30f72e28c64b3e6ca888715a848cd085 INSERT INTO t6 VALUES (...) 5
+statements_digest 551dce993b267c981c5b3eb285c2fe57 SELECT ? + ? 3
+statements_digest d31e1af4dc7ed5fe3ff61c78db7b327e SELECT ? 1
+statements_digest 33003a7b4de282603814a057945694d3 CREATE SCHEMA statements_digest_temp 2
+statements_digest 6ce84f85f37b9996e3dcbed9d55b88dd DROP SCHEMA statements_digest_temp 2
+statements_digest 08c862f2422dd8464a3b7b96d9de1dfa SELECT ? FROM no_such_table 1
+statements_digest c41b789a3176e6dbd8157848c6ff4aaf CREATE TABLE dup_table ( c CHARACTER (?) ) 2
+statements_digest fe693f8cf543b249a89f9f76c363d9d5 DROP TABLE dup_table 1
+statements_digest 5a6a862982ca17eff9038f2d852d848f INSERT INTO t11 VALUES (?) 1
+statements_digest b72d811ed58c8f2ec01e110bcad3138b SHOW WARNINGS 1
+statements_digest 63e18c50006c39c70200e63e720a9f0a PREPARE stmt FROM ? 1
+statements_digest eac5a2c580910e14eb0894ef21a25353 EXECUTE stmt 2
+statements_digest 5f1eaa4567c93974669fc403159245db DEALLOCATE PREPARE stmt 1
+statements_digest acb8e84440f68ee053d486688dfc88b2 CREATE PROCEDURE p1 ( ) BEGIN SELECT * FROM t12 ; END 1
+statements_digest 44c11865a2c9cd9f884bca10564ac818 CALL p1 ( ) 2
+statements_digest fb004af2d0db6f35a97ccdbbc51343ef DROP PROCEDURE p1 1
+statements_digest 6566febd24d7b17c53f75785ce94936c CREATE FUNCTION `func` ( a INTEGER , b INTEGER ) RETURNS INTEGER (?) RETURN a + b 1
+statements_digest 5bc097b58c334afe0875d7b74d0eb86e SELECT func (...) 2
+statements_digest 183cce493d199f32fad2174aab485298 DROP FUNCTION func 1
+statements_digest b0f05e1bd191be18730e2e24801a448d CREATE TRIGGER trg BEFORE INSERT ON t12 FOR EACH ROW SET @ ? := ? 1
+statements_digest 4a20ca3773c57af8a3949b76f446505a INSERT INTO t12 VALUES (?) 2
+statements_digest b345f3bef14924fea5ce7129cd374576 DROP TRIGGER trg 1
SELECT digest, digest_text FROM performance_schema.events_statements_current;
digest digest_text
####################################
diff --git a/mysql-test/suite/perfschema/r/statement_digest_long_query.result b/mysql-test/suite/perfschema/r/statement_digest_long_query.result
index f304b0108de..611bbef4434 100644
--- a/mysql-test/suite/perfschema/r/statement_digest_long_query.result
+++ b/mysql-test/suite/perfschema/r/statement_digest_long_query.result
@@ -8,5 +8,5 @@ SELECT 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1
####################################
SELECT schema_name, digest, digest_text, count_star FROM events_statements_summary_by_digest;
schema_name digest digest_text count_star
-performance_schema f7e1956f59eac75d9274653d492191ad TRUNCATE TABLE events_statements_summary_by_digest 1
-performance_schema aa750e240d5b31fa3ca6ef4375863e28 SELECT ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ... 1
+performance_schema 85f61b5db68f69a59a90190e8077e4af TRUNCATE TABLE events_statements_summary_by_digest 1
+performance_schema 0cc3fae5d60042494d108e9075b594d3 SELECT ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ? + ... 1
diff --git a/mysql-test/suite/perfschema/r/table_aggregate_global_2u_2t.result b/mysql-test/suite/perfschema/r/table_aggregate_global_2u_2t.result
index a3a9316a7e7..b4af9d021ef 100644
--- a/mysql-test/suite/perfschema/r/table_aggregate_global_2u_2t.result
+++ b/mysql-test/suite/perfschema/r/table_aggregate_global_2u_2t.result
@@ -63,7 +63,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_aggregate_global_2u_3t.result b/mysql-test/suite/perfschema/r/table_aggregate_global_2u_3t.result
index 2ce2450e2f8..6ccc81c5581 100644
--- a/mysql-test/suite/perfschema/r/table_aggregate_global_2u_3t.result
+++ b/mysql-test/suite/perfschema/r/table_aggregate_global_2u_3t.result
@@ -62,7 +62,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_aggregate_global_4u_2t.result b/mysql-test/suite/perfschema/r/table_aggregate_global_4u_2t.result
index db2aa8e8a62..b7ed12e9f00 100644
--- a/mysql-test/suite/perfschema/r/table_aggregate_global_4u_2t.result
+++ b/mysql-test/suite/perfschema/r/table_aggregate_global_4u_2t.result
@@ -63,7 +63,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_aggregate_global_4u_3t.result b/mysql-test/suite/perfschema/r/table_aggregate_global_4u_3t.result
index 3c047ab38eb..4f45ae0fedd 100644
--- a/mysql-test/suite/perfschema/r/table_aggregate_global_4u_3t.result
+++ b/mysql-test/suite/perfschema/r/table_aggregate_global_4u_3t.result
@@ -62,7 +62,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_aggregate_hist_2u_2t.result b/mysql-test/suite/perfschema/r/table_aggregate_hist_2u_2t.result
index 433e1bf40e8..937a6bc11f4 100644
--- a/mysql-test/suite/perfschema/r/table_aggregate_hist_2u_2t.result
+++ b/mysql-test/suite/perfschema/r/table_aggregate_hist_2u_2t.result
@@ -61,7 +61,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_aggregate_hist_2u_3t.result b/mysql-test/suite/perfschema/r/table_aggregate_hist_2u_3t.result
index 10f72b0f2bf..d6436c2f4ac 100644
--- a/mysql-test/suite/perfschema/r/table_aggregate_hist_2u_3t.result
+++ b/mysql-test/suite/perfschema/r/table_aggregate_hist_2u_3t.result
@@ -60,7 +60,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_aggregate_hist_4u_2t.result b/mysql-test/suite/perfschema/r/table_aggregate_hist_4u_2t.result
index 210c2b503c9..fa805e36760 100644
--- a/mysql-test/suite/perfschema/r/table_aggregate_hist_4u_2t.result
+++ b/mysql-test/suite/perfschema/r/table_aggregate_hist_4u_2t.result
@@ -61,7 +61,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_aggregate_hist_4u_3t.result b/mysql-test/suite/perfschema/r/table_aggregate_hist_4u_3t.result
index ab89f7c24ea..5b0105ab137 100644
--- a/mysql-test/suite/perfschema/r/table_aggregate_hist_4u_3t.result
+++ b/mysql-test/suite/perfschema/r/table_aggregate_hist_4u_3t.result
@@ -60,7 +60,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_aggregate_off.result b/mysql-test/suite/perfschema/r/table_aggregate_off.result
index 57372963797..62d619854a8 100644
--- a/mysql-test/suite/perfschema/r/table_aggregate_off.result
+++ b/mysql-test/suite/perfschema/r/table_aggregate_off.result
@@ -62,7 +62,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_aggregate_thread_2u_2t.result b/mysql-test/suite/perfschema/r/table_aggregate_thread_2u_2t.result
index f46eb5ba228..7c91487477c 100644
--- a/mysql-test/suite/perfschema/r/table_aggregate_thread_2u_2t.result
+++ b/mysql-test/suite/perfschema/r/table_aggregate_thread_2u_2t.result
@@ -63,7 +63,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_aggregate_thread_2u_3t.result b/mysql-test/suite/perfschema/r/table_aggregate_thread_2u_3t.result
index a162949e54a..e4a3aabd51b 100644
--- a/mysql-test/suite/perfschema/r/table_aggregate_thread_2u_3t.result
+++ b/mysql-test/suite/perfschema/r/table_aggregate_thread_2u_3t.result
@@ -62,7 +62,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_aggregate_thread_4u_2t.result b/mysql-test/suite/perfschema/r/table_aggregate_thread_4u_2t.result
index 083e8ed7341..f6d2ac4fb19 100644
--- a/mysql-test/suite/perfschema/r/table_aggregate_thread_4u_2t.result
+++ b/mysql-test/suite/perfschema/r/table_aggregate_thread_4u_2t.result
@@ -63,7 +63,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_aggregate_thread_4u_3t.result b/mysql-test/suite/perfschema/r/table_aggregate_thread_4u_3t.result
index fccbaf0ad37..f76b83d2854 100644
--- a/mysql-test/suite/perfschema/r/table_aggregate_thread_4u_3t.result
+++ b/mysql-test/suite/perfschema/r/table_aggregate_thread_4u_3t.result
@@ -62,7 +62,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_io_aggregate_global_2u_2t.result b/mysql-test/suite/perfschema/r/table_io_aggregate_global_2u_2t.result
index 989e521cd75..a5c6f669eb6 100644
--- a/mysql-test/suite/perfschema/r/table_io_aggregate_global_2u_2t.result
+++ b/mysql-test/suite/perfschema/r/table_io_aggregate_global_2u_2t.result
@@ -65,7 +65,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_io_aggregate_global_2u_3t.result b/mysql-test/suite/perfschema/r/table_io_aggregate_global_2u_3t.result
index 268e9cccd9e..a327eadeb75 100644
--- a/mysql-test/suite/perfschema/r/table_io_aggregate_global_2u_3t.result
+++ b/mysql-test/suite/perfschema/r/table_io_aggregate_global_2u_3t.result
@@ -64,7 +64,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_io_aggregate_global_4u_2t.result b/mysql-test/suite/perfschema/r/table_io_aggregate_global_4u_2t.result
index 5fedb1a93fb..2cd3cd51329 100644
--- a/mysql-test/suite/perfschema/r/table_io_aggregate_global_4u_2t.result
+++ b/mysql-test/suite/perfschema/r/table_io_aggregate_global_4u_2t.result
@@ -65,7 +65,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_io_aggregate_global_4u_3t.result b/mysql-test/suite/perfschema/r/table_io_aggregate_global_4u_3t.result
index f7f18bafb64..40eb000ea84 100644
--- a/mysql-test/suite/perfschema/r/table_io_aggregate_global_4u_3t.result
+++ b/mysql-test/suite/perfschema/r/table_io_aggregate_global_4u_3t.result
@@ -64,7 +64,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_io_aggregate_hist_2u_2t.result b/mysql-test/suite/perfschema/r/table_io_aggregate_hist_2u_2t.result
index e3a0bc98d8c..bc348ffdd1c 100644
--- a/mysql-test/suite/perfschema/r/table_io_aggregate_hist_2u_2t.result
+++ b/mysql-test/suite/perfschema/r/table_io_aggregate_hist_2u_2t.result
@@ -63,7 +63,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_io_aggregate_hist_2u_3t.result b/mysql-test/suite/perfschema/r/table_io_aggregate_hist_2u_3t.result
index ca3565b5350..5f30bde9421 100644
--- a/mysql-test/suite/perfschema/r/table_io_aggregate_hist_2u_3t.result
+++ b/mysql-test/suite/perfschema/r/table_io_aggregate_hist_2u_3t.result
@@ -62,7 +62,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_io_aggregate_hist_4u_2t.result b/mysql-test/suite/perfschema/r/table_io_aggregate_hist_4u_2t.result
index 65662f5974c..9a147466a78 100644
--- a/mysql-test/suite/perfschema/r/table_io_aggregate_hist_4u_2t.result
+++ b/mysql-test/suite/perfschema/r/table_io_aggregate_hist_4u_2t.result
@@ -63,7 +63,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_io_aggregate_hist_4u_3t.result b/mysql-test/suite/perfschema/r/table_io_aggregate_hist_4u_3t.result
index 6b7bfc07953..63b1f709b72 100644
--- a/mysql-test/suite/perfschema/r/table_io_aggregate_hist_4u_3t.result
+++ b/mysql-test/suite/perfschema/r/table_io_aggregate_hist_4u_3t.result
@@ -62,7 +62,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_io_aggregate_thread_2u_2t.result b/mysql-test/suite/perfschema/r/table_io_aggregate_thread_2u_2t.result
index 8ffd9f4e922..ac740035848 100644
--- a/mysql-test/suite/perfschema/r/table_io_aggregate_thread_2u_2t.result
+++ b/mysql-test/suite/perfschema/r/table_io_aggregate_thread_2u_2t.result
@@ -65,7 +65,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_io_aggregate_thread_2u_3t.result b/mysql-test/suite/perfschema/r/table_io_aggregate_thread_2u_3t.result
index 3de3432fbc4..8fa0ff230e3 100644
--- a/mysql-test/suite/perfschema/r/table_io_aggregate_thread_2u_3t.result
+++ b/mysql-test/suite/perfschema/r/table_io_aggregate_thread_2u_3t.result
@@ -64,7 +64,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_io_aggregate_thread_4u_2t.result b/mysql-test/suite/perfschema/r/table_io_aggregate_thread_4u_2t.result
index ec748384b8f..83d70261823 100644
--- a/mysql-test/suite/perfschema/r/table_io_aggregate_thread_4u_2t.result
+++ b/mysql-test/suite/perfschema/r/table_io_aggregate_thread_4u_2t.result
@@ -65,7 +65,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_io_aggregate_thread_4u_3t.result b/mysql-test/suite/perfschema/r/table_io_aggregate_thread_4u_3t.result
index 30b234f89be..5dac5f5b83a 100644
--- a/mysql-test/suite/perfschema/r/table_io_aggregate_thread_4u_3t.result
+++ b/mysql-test/suite/perfschema/r/table_io_aggregate_thread_4u_3t.result
@@ -64,7 +64,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_lock_aggregate_global_2u_2t.result b/mysql-test/suite/perfschema/r/table_lock_aggregate_global_2u_2t.result
index bda8c1587d8..f702ebadfc0 100644
--- a/mysql-test/suite/perfschema/r/table_lock_aggregate_global_2u_2t.result
+++ b/mysql-test/suite/perfschema/r/table_lock_aggregate_global_2u_2t.result
@@ -65,7 +65,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_lock_aggregate_global_2u_3t.result b/mysql-test/suite/perfschema/r/table_lock_aggregate_global_2u_3t.result
index f1be16ee937..693723f6538 100644
--- a/mysql-test/suite/perfschema/r/table_lock_aggregate_global_2u_3t.result
+++ b/mysql-test/suite/perfschema/r/table_lock_aggregate_global_2u_3t.result
@@ -64,7 +64,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_lock_aggregate_global_4u_2t.result b/mysql-test/suite/perfschema/r/table_lock_aggregate_global_4u_2t.result
index c0293a03175..274b9c22b9f 100644
--- a/mysql-test/suite/perfschema/r/table_lock_aggregate_global_4u_2t.result
+++ b/mysql-test/suite/perfschema/r/table_lock_aggregate_global_4u_2t.result
@@ -65,7 +65,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_lock_aggregate_global_4u_3t.result b/mysql-test/suite/perfschema/r/table_lock_aggregate_global_4u_3t.result
index 56317b28bf1..2cf43f9b47a 100644
--- a/mysql-test/suite/perfschema/r/table_lock_aggregate_global_4u_3t.result
+++ b/mysql-test/suite/perfschema/r/table_lock_aggregate_global_4u_3t.result
@@ -64,7 +64,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_lock_aggregate_hist_2u_2t.result b/mysql-test/suite/perfschema/r/table_lock_aggregate_hist_2u_2t.result
index 47b5f2757b9..412353b73dd 100644
--- a/mysql-test/suite/perfschema/r/table_lock_aggregate_hist_2u_2t.result
+++ b/mysql-test/suite/perfschema/r/table_lock_aggregate_hist_2u_2t.result
@@ -63,7 +63,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_lock_aggregate_hist_2u_3t.result b/mysql-test/suite/perfschema/r/table_lock_aggregate_hist_2u_3t.result
index e9febe20037..487bc91e784 100644
--- a/mysql-test/suite/perfschema/r/table_lock_aggregate_hist_2u_3t.result
+++ b/mysql-test/suite/perfschema/r/table_lock_aggregate_hist_2u_3t.result
@@ -62,7 +62,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_lock_aggregate_hist_4u_2t.result b/mysql-test/suite/perfschema/r/table_lock_aggregate_hist_4u_2t.result
index 677cb692f36..1fe09b3008f 100644
--- a/mysql-test/suite/perfschema/r/table_lock_aggregate_hist_4u_2t.result
+++ b/mysql-test/suite/perfschema/r/table_lock_aggregate_hist_4u_2t.result
@@ -63,7 +63,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_lock_aggregate_hist_4u_3t.result b/mysql-test/suite/perfschema/r/table_lock_aggregate_hist_4u_3t.result
index ea9d23307cc..5bbd3ca022c 100644
--- a/mysql-test/suite/perfschema/r/table_lock_aggregate_hist_4u_3t.result
+++ b/mysql-test/suite/perfschema/r/table_lock_aggregate_hist_4u_3t.result
@@ -62,7 +62,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_lock_aggregate_thread_2u_2t.result b/mysql-test/suite/perfschema/r/table_lock_aggregate_thread_2u_2t.result
index 45d463f28b1..4fd4f505454 100644
--- a/mysql-test/suite/perfschema/r/table_lock_aggregate_thread_2u_2t.result
+++ b/mysql-test/suite/perfschema/r/table_lock_aggregate_thread_2u_2t.result
@@ -65,7 +65,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_lock_aggregate_thread_2u_3t.result b/mysql-test/suite/perfschema/r/table_lock_aggregate_thread_2u_3t.result
index 17365d80f8a..a3e0129bb70 100644
--- a/mysql-test/suite/perfschema/r/table_lock_aggregate_thread_2u_3t.result
+++ b/mysql-test/suite/perfschema/r/table_lock_aggregate_thread_2u_3t.result
@@ -64,7 +64,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_lock_aggregate_thread_4u_2t.result b/mysql-test/suite/perfschema/r/table_lock_aggregate_thread_4u_2t.result
index c35dedb0871..3edba653026 100644
--- a/mysql-test/suite/perfschema/r/table_lock_aggregate_thread_4u_2t.result
+++ b/mysql-test/suite/perfschema/r/table_lock_aggregate_thread_4u_2t.result
@@ -65,7 +65,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
diff --git a/mysql-test/suite/perfschema/r/table_lock_aggregate_thread_4u_3t.result b/mysql-test/suite/perfschema/r/table_lock_aggregate_thread_4u_3t.result
index 5b02767819c..461ff5da3dd 100644
--- a/mysql-test/suite/perfschema/r/table_lock_aggregate_thread_4u_3t.result
+++ b/mysql-test/suite/perfschema/r/table_lock_aggregate_thread_4u_3t.result
@@ -64,7 +64,7 @@ performance_schema_max_rwlock_instances 5000
performance_schema_max_socket_classes 10
performance_schema_max_socket_instances 1000
performance_schema_max_stage_classes 150
-performance_schema_max_statement_classes 175
+performance_schema_max_statement_classes 179
performance_schema_max_table_handles 1000
performance_schema_max_table_instances 500
performance_schema_max_thread_classes 50
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 00000000000..6f6688f446e
--- /dev/null
+++ b/mysql-test/suite/roles/acl_load_mutex-5170.result
@@ -0,0 +1,8 @@
+create user user1@localhost;
+create role r1 with admin user1@localhost;
+grant all on test.* to r1;
+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 00000000000..e77d191ea16
--- /dev/null
+++ b/mysql-test/suite/roles/acl_load_mutex-5170.test
@@ -0,0 +1,24 @@
+--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;
+
+--append_file $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+wait
+EOF
+--shutdown_server 60
+--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/admin.result b/mysql-test/suite/roles/admin.result
new file mode 100644
index 00000000000..ad3d4125252
--- /dev/null
+++ b/mysql-test/suite/roles/admin.result
@@ -0,0 +1,152 @@
+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;
+create role role5 with admin root@localhost;
+ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation
+create role role5 with admin role3;
+ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation
+create role role5 with admin foo@localhost;
+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
+role1 role2 NO
+role1 role3 YES
+role3 role4 YES
+root@localhost role1 YES
+root@localhost role2 YES
+root@localhost role4 YES
+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
+role1 role2 YES
+role1 role3 YES
+role3 role4 NO
+root@localhost role1 NO
+root@localhost role2 YES
+root@localhost role4 YES
+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 00000000000..03f3449200b
--- /dev/null
+++ b/mysql-test/suite/roles/admin.test
@@ -0,0 +1,102 @@
+source include/not_embedded.inc;
+
+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_role.result b/mysql-test/suite/roles/create_and_drop_role.result
new file mode 100644
index 00000000000..d4eac2756ec
--- /dev/null
+++ b/mysql-test/suite/roles/create_and_drop_role.result
@@ -0,0 +1,36 @@
+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
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 00000000000..3491986e666
--- /dev/null
+++ b/mysql-test/suite/roles/create_and_drop_role.test
@@ -0,0 +1,47 @@
+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;
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 00000000000..d0254fc2c44
--- /dev/null
+++ b/mysql-test/suite/roles/create_and_drop_role_invalid_user_table.result
@@ -0,0 +1,14 @@
+use mysql;
+alter table user drop column is_role;
+flush privileges;
+create role test_role;
+ERROR HY000: Column count of mysql.user is wrong. Expected 44, found 43. Created with MariaDB 100004, now running 100004. Please use mysql_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;
+update user set is_role='N';
+flush privileges;
+create role test_role;
+drop role test_role;
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 00000000000..e83e2d3b278
--- /dev/null
+++ b/mysql-test/suite/roles/create_and_drop_role_invalid_user_table.test
@@ -0,0 +1,25 @@
+source include/not_embedded.inc;
+
+connect (mysql, localhost, root,,);
+use mysql;
+
+alter table user drop column is_role;
+
+flush privileges;
+
+--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;
+
+update user set is_role='N';
+
+flush privileges;
+create role test_role;
+drop role test_role;
+
+
+
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 00000000000..883ae44397d
--- /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 00000000000..5b32150389e
--- /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/default_create_user_not_role.result b/mysql-test/suite/roles/default_create_user_not_role.result
new file mode 100644
index 00000000000..1ddb054c092
--- /dev/null
+++ b/mysql-test/suite/roles/default_create_user_not_role.result
@@ -0,0 +1,6 @@
+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';
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 00000000000..d3a432892db
--- /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 00000000000..0010853be78
--- /dev/null
+++ b/mysql-test/suite/roles/definer.result
@@ -0,0 +1,625 @@
+create database mysqltest1;
+use mysqltest1;
+create table t1 (a int, b int, c int);
+insert t1 values (1,10,100),(2,20,200);
+create role role1;
+grant select (a) on mysqltest1.t1 to role1;
+grant event,execute,trigger on mysqltest1.* to role1;
+grant role1 to current_user;
+create role role2;
+grant insert,select on mysqltest1.t1 to role2;
+grant event,execute,trigger on mysqltest1.* to role2;
+grant create view on mysqltest1.* to foo@localhost;
+create role role4;
+grant select on mysqltest1.t1 to role4;
+grant role4 to foo@localhost;
+create definer=current_role view test.v1 as select a+b,c from t1;
+ERROR 0L000: Invalid definer
+set role role1;
+create definer=current_role view test.v1 as select a+b,c from t1;
+show create view test.v1;
+View Create View character_set_client collation_connection
+v1 CREATE ALGORITHM=UNDEFINED DEFINER=`role1` SQL SECURITY DEFINER VIEW `test`.`v1` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1` latin1 latin1_swedish_ci
+set role none;
+create definer=role2 view test.v2 as select a+b,c from t1;
+show create view test.v2;
+View Create View character_set_client collation_connection
+v2 CREATE ALGORITHM=UNDEFINED DEFINER=`role2` SQL SECURITY DEFINER VIEW `test`.`v2` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1` latin1 latin1_swedish_ci
+create definer=role3 view test.v3 as select a+b,c from t1;
+Warnings:
+Note 1449 The user specified as a definer ('role3'@'%') does not exist
+show create view test.v3;
+View Create View character_set_client collation_connection
+v3 CREATE ALGORITHM=UNDEFINED DEFINER=`role3`@`%` SQL SECURITY DEFINER VIEW `test`.`v3` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1` latin1 latin1_swedish_ci
+Warnings:
+Note 1449 The user specified as a definer ('role3'@'%') does not exist
+show grants;
+Grants for foo@localhost
+GRANT role4 TO 'foo'@'localhost'
+GRANT USAGE ON *.* TO 'foo'@'localhost'
+GRANT CREATE VIEW ON `mysqltest1`.* TO 'foo'@'localhost'
+select * from test.v1;
+ERROR HY000: View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
+select * from test.v2;
+a+b c
+11 100
+22 200
+select * from test.v3;
+ERROR 28000: Access denied for user 'foo'@'localhost' (using password: NO)
+create definer=role4 view test.v4 as select a+b,c from t1;
+ERROR 42000: ANY command denied to user 'foo'@'localhost' for table 't1'
+select * from t1;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table 't1'
+set role role4;
+select * from t1;
+a b c
+1 10 100
+2 20 200
+create view test.v4 as select a+b,c from t1;
+create definer=role4 view test.v5 as select a+b,c from t1;
+select * from test.v4;
+ERROR HY000: View 'test.v4' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
+select * from test.v5;
+a+b c
+11 100
+22 200
+set role none;
+select * from test.v4;
+ERROR HY000: View 'test.v4' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
+select * from test.v5;
+a+b c
+11 100
+22 200
+drop role role4;
+show create view test.v5;
+View Create View character_set_client collation_connection
+v5 CREATE ALGORITHM=UNDEFINED DEFINER=`role4` SQL SECURITY DEFINER VIEW `test`.`v5` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1` latin1 latin1_swedish_ci
+Warnings:
+Note 1449 The user specified as a definer ('role4'@'') does not exist
+select * from test.v5;
+ERROR HY000: The user specified as a definer ('role4'@'') does not exist
+grant select on mysqltest1.t1 to role4;
+show create view test.v5;
+View Create View character_set_client collation_connection
+v5 CREATE ALGORITHM=UNDEFINED DEFINER=`role4` SQL SECURITY DEFINER VIEW `test`.`v5` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1` latin1 latin1_swedish_ci
+Warnings:
+Note 1449 The user specified as a definer ('role4'@'') does not exist
+select * from test.v5;
+ERROR HY000: The user specified as a definer ('role4'@'') does not exist
+show create view test.v5;
+View Create View character_set_client collation_connection
+v5 CREATE ALGORITHM=UNDEFINED DEFINER=`role4`@`%` SQL SECURITY DEFINER VIEW `test`.`v5` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1` latin1 latin1_swedish_ci
+select * from test.v5;
+a+b c
+11 100
+22 200
+drop user role4;
+create table t2 select * from t1;
+create definer=current_role trigger tr1 before insert on t2 for each row
+insert t1 values (111, 222, 333);
+ERROR 0L000: Invalid definer
+set role role1;
+create definer=current_role trigger tr1 before insert on t2 for each row
+insert t1 values (111, 222, 333);
+show create trigger tr1;
+Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation
+tr1 CREATE DEFINER=`role1` trigger tr1 before insert on t2 for each row
+insert t1 values (111, 222, 333) latin1 latin1_swedish_ci latin1_swedish_ci
+set role none;
+insert t2 values (11,22,33);
+ERROR 42000: INSERT command denied to user 'role1'@'' for table 't1'
+select * from t1;
+a b c
+1 10 100
+2 20 200
+select * from t2;
+a b c
+1 10 100
+2 20 200
+create definer=role2 trigger tr2 before delete on t2 for each row
+insert t1 values (111, 222, 333);
+show create trigger tr2;
+Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation
+tr2 CREATE DEFINER=`role2` trigger tr2 before delete on t2 for each row
+insert t1 values (111, 222, 333) latin1 latin1_swedish_ci latin1_swedish_ci
+delete from t2 where a=1;
+select * from t1;
+a b c
+1 10 100
+2 20 200
+111 222 333
+select * from t2;
+a b c
+2 20 200
+delete from t1 where a=111;
+create definer=role3 trigger tr3 before update on t2 for each row
+insert t1 values (111, 222, 333);
+Warnings:
+Note 1449 The user specified as a definer ('role3'@'%') does not exist
+show create trigger tr3;
+Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation
+tr3 CREATE DEFINER=`role3`@`%` trigger tr3 before update on t2 for each row
+insert t1 values (111, 222, 333) latin1 latin1_swedish_ci latin1_swedish_ci
+update t2 set b=2 where a=2;
+ERROR HY000: The user specified as a definer ('role3'@'%') does not exist
+select * from t1;
+a b c
+1 10 100
+2 20 200
+select * from t2;
+a b c
+2 20 200
+flush tables;
+show create trigger tr2;
+Trigger sql_mode SQL Original Statement character_set_client collation_connection Database Collation
+tr2 CREATE DEFINER=`role2`@`` trigger tr2 before delete on t2 for each row
+insert t1 values (111, 222, 333) latin1 latin1_swedish_ci latin1_swedish_ci
+delete from t2 where a=2;
+ERROR HY000: The user specified as a definer ('role2'@'%') does not exist
+select * from t1;
+a b c
+1 10 100
+2 20 200
+select * from t2;
+a b c
+2 20 200
+create definer=current_role procedure pr1() insert t1 values (111, 222, 333);
+ERROR 0L000: Invalid definer
+set role role1;
+create definer=current_role procedure pr1() insert t1 values (111, 222, 333);
+show create procedure pr1;
+Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation
+pr1 CREATE DEFINER=`role1` PROCEDURE `pr1`()
+insert t1 values (111, 222, 333) latin1 latin1_swedish_ci latin1_swedish_ci
+set role none;
+call pr1();
+ERROR 42000: INSERT command denied to user 'role1'@'' for table 't1'
+select * from t1;
+a b c
+1 10 100
+2 20 200
+create definer=role2 procedure pr2() insert t1 values (111, 222, 333);
+show create procedure pr2;
+Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation
+pr2 CREATE DEFINER=`role2` PROCEDURE `pr2`()
+insert t1 values (111, 222, 333) latin1 latin1_swedish_ci latin1_swedish_ci
+call pr2();
+select * from t1;
+a b c
+1 10 100
+2 20 200
+111 222 333
+delete from t1 where a=111;
+create definer=role3 procedure pr3() insert t1 values (111, 222, 333);
+Warnings:
+Note 1449 The user specified as a definer ('role3'@'%') does not exist
+show create procedure pr3;
+Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation
+pr3 CREATE DEFINER=`role3`@`%` PROCEDURE `pr3`()
+insert t1 values (111, 222, 333) latin1 latin1_swedish_ci latin1_swedish_ci
+call pr3();
+ERROR HY000: The user specified as a definer ('role3'@'%') does not exist
+select * from t1;
+a b c
+1 10 100
+2 20 200
+update mysql.proc set definer='role2@' where definer='role2';
+call pr2();
+ERROR HY000: The user specified as a definer ('role2'@'%') does not exist
+create definer=current_role function fn1() returns int return (select sum(a+b) from t1);
+ERROR 0L000: Invalid definer
+set role role1;
+create definer=current_role function fn1() returns int return (select sum(a+b) from t1);
+show create function fn1;
+Function sql_mode Create Function character_set_client collation_connection Database Collation
+fn1 CREATE DEFINER=`role1` FUNCTION `fn1`() RETURNS int(11)
+return (select sum(a+b) from t1) latin1 latin1_swedish_ci latin1_swedish_ci
+set role none;
+select fn1();
+ERROR 42000: SELECT command denied to user 'role1'@'' for column 'b' in table 't1'
+select * from t1;
+a b c
+1 10 100
+2 20 200
+create definer=role2 function fn2() returns int return (select sum(a+b) from t1);
+show create function fn2;
+Function sql_mode Create Function character_set_client collation_connection Database Collation
+fn2 CREATE DEFINER=`role2` FUNCTION `fn2`() RETURNS int(11)
+return (select sum(a+b) from t1) latin1 latin1_swedish_ci latin1_swedish_ci
+select fn2();
+fn2()
+33
+create definer=role3 function fn3() returns int return (select sum(a+b) from t1);
+Warnings:
+Note 1449 The user specified as a definer ('role3'@'%') does not exist
+show create function fn3;
+Function sql_mode Create Function character_set_client collation_connection Database Collation
+fn3 CREATE DEFINER=`role3`@`%` FUNCTION `fn3`() RETURNS int(11)
+return (select sum(a+b) from t1) latin1 latin1_swedish_ci latin1_swedish_ci
+select fn3();
+ERROR HY000: The user specified as a definer ('role3'@'%') does not exist
+set global event_scheduler=on;
+create definer=current_role event e1 on schedule every 1 second starts '2000-01-01' do
+insert t1 values (111, 1, 0);
+ERROR 0L000: Invalid definer
+set role role1;
+create definer=current_role event e1 on schedule every 1 second starts '2000-01-01' do
+insert t1 values (111, 2, 0);
+show create event e1;
+Event sql_mode time_zone Create Event character_set_client collation_connection Database Collation
+e1 SYSTEM CREATE DEFINER=`role1` EVENT `e1` ON SCHEDULE EVERY 1 SECOND STARTS '2000-01-01 00:00:00' ON COMPLETION NOT PRESERVE ENABLE DO insert t1 values (111, 2, 0) latin1 latin1_swedish_ci latin1_swedish_ci
+set role none;
+create definer=role3 event e3 on schedule every 1 second starts '2000-01-01' do
+insert t1 values (111, 3, 0);
+Warnings:
+Note 1449 The user specified as a definer ('role3'@'%') does not exist
+show create event e3;
+Event sql_mode time_zone Create Event character_set_client collation_connection Database Collation
+e3 SYSTEM CREATE DEFINER=`role3`@`%` EVENT `e3` ON SCHEDULE EVERY 1 SECOND STARTS '2000-01-01 00:00:00' ON COMPLETION NOT PRESERVE ENABLE DO insert t1 values (111, 3, 0) latin1 latin1_swedish_ci latin1_swedish_ci
+create definer=role2 event e2 on schedule every 1 second starts '2000-01-01' do
+insert t1 values (111, 4, 0);
+show create event e2;
+Event sql_mode time_zone Create Event character_set_client collation_connection Database Collation
+e2 SYSTEM CREATE DEFINER=`role2` EVENT `e2` ON SCHEDULE EVERY 1 SECOND STARTS '2000-01-01 00:00:00' ON COMPLETION NOT PRESERVE ENABLE DO insert t1 values (111, 4, 0) latin1 latin1_swedish_ci latin1_swedish_ci
+set global event_scheduler=off;
+select distinct * from t1;
+a b c
+1 10 100
+111 4 0
+2 20 200
+delete from t1 where a=111;
+
+CREATE DATABASE /*!32312 IF NOT EXISTS*/ `test` /*!40100 DEFAULT CHARACTER SET latin1 */;
+
+USE `test`;
+SET @saved_cs_client = @@character_set_client;
+SET character_set_client = utf8;
+/*!50001 CREATE TABLE `v1` (
+ `a+b` tinyint NOT NULL,
+ `c` tinyint NOT NULL
+) ENGINE=MyISAM */;
+SET character_set_client = @saved_cs_client;
+SET @saved_cs_client = @@character_set_client;
+SET character_set_client = utf8;
+/*!50001 CREATE TABLE `v2` (
+ `a+b` tinyint NOT NULL,
+ `c` tinyint NOT NULL
+) ENGINE=MyISAM */;
+SET character_set_client = @saved_cs_client;
+SET @saved_cs_client = @@character_set_client;
+SET character_set_client = utf8;
+/*!50001 CREATE TABLE `v3` (
+ `a+b` tinyint NOT NULL,
+ `c` tinyint NOT NULL
+) ENGINE=MyISAM */;
+SET character_set_client = @saved_cs_client;
+SET @saved_cs_client = @@character_set_client;
+SET character_set_client = utf8;
+/*!50001 CREATE TABLE `v4` (
+ `a+b` tinyint NOT NULL,
+ `c` tinyint NOT NULL
+) ENGINE=MyISAM */;
+SET character_set_client = @saved_cs_client;
+SET @saved_cs_client = @@character_set_client;
+SET character_set_client = utf8;
+/*!50001 CREATE TABLE `v5` (
+ `a+b` tinyint NOT NULL,
+ `c` tinyint NOT NULL
+) ENGINE=MyISAM */;
+SET character_set_client = @saved_cs_client;
+
+CREATE DATABASE /*!32312 IF NOT EXISTS*/ `mysqltest1` /*!40100 DEFAULT CHARACTER SET latin1 */;
+
+USE `mysqltest1`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `t1` (
+ `a` int(11) DEFAULT NULL,
+ `b` int(11) DEFAULT NULL,
+ `c` int(11) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+INSERT INTO `t1` VALUES (1,10,100),(2,20,200);
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `t2` (
+ `a` int(11) DEFAULT NULL,
+ `b` int(11) DEFAULT NULL,
+ `c` int(11) DEFAULT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=latin1;
+/*!40101 SET character_set_client = @saved_cs_client */;
+INSERT INTO `t2` VALUES (2,20,200);
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = latin1 */ ;
+/*!50003 SET character_set_results = latin1 */ ;
+/*!50003 SET collation_connection = latin1_swedish_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+/*!50003 CREATE*/ /*!50017 DEFINER=`role1`*/ /*!50003 trigger tr1 before insert on t2 for each row
+insert t1 values (111, 222, 333) */;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = latin1 */ ;
+/*!50003 SET character_set_results = latin1 */ ;
+/*!50003 SET collation_connection = latin1_swedish_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+/*!50003 CREATE*/ /*!50017 DEFINER=`role3`@`%`*/ /*!50003 trigger tr3 before update on t2 for each row
+insert t1 values (111, 222, 333) */;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = latin1 */ ;
+/*!50003 SET character_set_results = latin1 */ ;
+/*!50003 SET collation_connection = latin1_swedish_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+/*!50003 CREATE*/ /*!50017 DEFINER=`role2`@``*/ /*!50003 trigger tr2 before delete on t2 for each row
+insert t1 values (111, 222, 333) */;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50106 SET @save_time_zone= @@TIME_ZONE */ ;
+DELIMITER ;;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;;
+/*!50003 SET character_set_client = latin1 */ ;;
+/*!50003 SET character_set_results = latin1 */ ;;
+/*!50003 SET collation_connection = latin1_swedish_ci */ ;;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;;
+/*!50003 SET sql_mode = '' */ ;;
+/*!50003 SET @saved_time_zone = @@time_zone */ ;;
+/*!50003 SET time_zone = 'SYSTEM' */ ;;
+/*!50106 CREATE*/ /*!50117 DEFINER=`role1`*/ /*!50106 EVENT `e1` ON SCHEDULE EVERY 1 SECOND STARTS '2000-01-01 00:00:00' ON COMPLETION NOT PRESERVE ENABLE DO insert t1 values (111, 2, 0) */ ;;
+/*!50003 SET time_zone = @saved_time_zone */ ;;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;;
+/*!50003 SET character_set_client = @saved_cs_client */ ;;
+/*!50003 SET character_set_results = @saved_cs_results */ ;;
+/*!50003 SET collation_connection = @saved_col_connection */ ;;
+DELIMITER ;;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;;
+/*!50003 SET character_set_client = latin1 */ ;;
+/*!50003 SET character_set_results = latin1 */ ;;
+/*!50003 SET collation_connection = latin1_swedish_ci */ ;;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;;
+/*!50003 SET sql_mode = '' */ ;;
+/*!50003 SET @saved_time_zone = @@time_zone */ ;;
+/*!50003 SET time_zone = 'SYSTEM' */ ;;
+/*!50106 CREATE*/ /*!50117 DEFINER=`role2`*/ /*!50106 EVENT `e2` ON SCHEDULE EVERY 1 SECOND STARTS '2000-01-01 00:00:00' ON COMPLETION NOT PRESERVE ENABLE DO insert t1 values (111, 4, 0) */ ;;
+/*!50003 SET time_zone = @saved_time_zone */ ;;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;;
+/*!50003 SET character_set_client = @saved_cs_client */ ;;
+/*!50003 SET character_set_results = @saved_cs_results */ ;;
+/*!50003 SET collation_connection = @saved_col_connection */ ;;
+DELIMITER ;;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;;
+/*!50003 SET character_set_client = latin1 */ ;;
+/*!50003 SET character_set_results = latin1 */ ;;
+/*!50003 SET collation_connection = latin1_swedish_ci */ ;;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;;
+/*!50003 SET sql_mode = '' */ ;;
+/*!50003 SET @saved_time_zone = @@time_zone */ ;;
+/*!50003 SET time_zone = 'SYSTEM' */ ;;
+/*!50106 CREATE*/ /*!50117 DEFINER=`role3`@`%`*/ /*!50106 EVENT `e3` ON SCHEDULE EVERY 1 SECOND STARTS '2000-01-01 00:00:00' ON COMPLETION NOT PRESERVE ENABLE DO insert t1 values (111, 3, 0) */ ;;
+/*!50003 SET time_zone = @saved_time_zone */ ;;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;;
+/*!50003 SET character_set_client = @saved_cs_client */ ;;
+/*!50003 SET character_set_results = @saved_cs_results */ ;;
+/*!50003 SET collation_connection = @saved_col_connection */ ;;
+DELIMITER ;
+/*!50106 SET TIME_ZONE= @save_time_zone */ ;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = latin1 */ ;
+/*!50003 SET character_set_results = latin1 */ ;
+/*!50003 SET collation_connection = latin1_swedish_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+CREATE DEFINER=`role1` FUNCTION `fn1`() RETURNS int(11)
+return (select sum(a+b) from t1) ;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = latin1 */ ;
+/*!50003 SET character_set_results = latin1 */ ;
+/*!50003 SET collation_connection = latin1_swedish_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+CREATE DEFINER=`role2` FUNCTION `fn2`() RETURNS int(11)
+return (select sum(a+b) from t1) ;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = latin1 */ ;
+/*!50003 SET character_set_results = latin1 */ ;
+/*!50003 SET collation_connection = latin1_swedish_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+CREATE DEFINER=`role3`@`%` FUNCTION `fn3`() RETURNS int(11)
+return (select sum(a+b) from t1) ;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = latin1 */ ;
+/*!50003 SET character_set_results = latin1 */ ;
+/*!50003 SET collation_connection = latin1_swedish_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+CREATE DEFINER=`role1` PROCEDURE `pr1`()
+insert t1 values (111, 222, 333) ;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = latin1 */ ;
+/*!50003 SET character_set_results = latin1 */ ;
+/*!50003 SET collation_connection = latin1_swedish_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+CREATE DEFINER=`role2`@`%` PROCEDURE `pr2`()
+insert t1 values (111, 222, 333) ;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+/*!50003 SET @saved_cs_client = @@character_set_client */ ;
+/*!50003 SET @saved_cs_results = @@character_set_results */ ;
+/*!50003 SET @saved_col_connection = @@collation_connection */ ;
+/*!50003 SET character_set_client = latin1 */ ;
+/*!50003 SET character_set_results = latin1 */ ;
+/*!50003 SET collation_connection = latin1_swedish_ci */ ;
+/*!50003 SET @saved_sql_mode = @@sql_mode */ ;
+/*!50003 SET sql_mode = '' */ ;
+DELIMITER ;;
+CREATE DEFINER=`role3`@`%` PROCEDURE `pr3`()
+insert t1 values (111, 222, 333) ;;
+DELIMITER ;
+/*!50003 SET sql_mode = @saved_sql_mode */ ;
+/*!50003 SET character_set_client = @saved_cs_client */ ;
+/*!50003 SET character_set_results = @saved_cs_results */ ;
+/*!50003 SET collation_connection = @saved_col_connection */ ;
+
+USE `test`;
+/*!50001 DROP TABLE IF EXISTS `v1`*/;
+/*!50001 SET @saved_cs_client = @@character_set_client */;
+/*!50001 SET @saved_cs_results = @@character_set_results */;
+/*!50001 SET @saved_col_connection = @@collation_connection */;
+/*!50001 SET character_set_client = latin1 */;
+/*!50001 SET character_set_results = latin1 */;
+/*!50001 SET collation_connection = latin1_swedish_ci */;
+/*!50001 CREATE ALGORITHM=UNDEFINED DEFINER=`role1` SQL SECURITY DEFINER VIEW `v1` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1` */;
+/*!50001 SET character_set_client = @saved_cs_client */;
+/*!50001 SET character_set_results = @saved_cs_results */;
+/*!50001 SET collation_connection = @saved_col_connection */;
+/*!50001 DROP TABLE IF EXISTS `v2`*/;
+/*!50001 SET @saved_cs_client = @@character_set_client */;
+/*!50001 SET @saved_cs_results = @@character_set_results */;
+/*!50001 SET @saved_col_connection = @@collation_connection */;
+/*!50001 SET character_set_client = latin1 */;
+/*!50001 SET character_set_results = latin1 */;
+/*!50001 SET collation_connection = latin1_swedish_ci */;
+/*!50001 CREATE ALGORITHM=UNDEFINED DEFINER=`role2` SQL SECURITY DEFINER VIEW `v2` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1` */;
+/*!50001 SET character_set_client = @saved_cs_client */;
+/*!50001 SET character_set_results = @saved_cs_results */;
+/*!50001 SET collation_connection = @saved_col_connection */;
+/*!50001 DROP TABLE IF EXISTS `v3`*/;
+/*!50001 SET @saved_cs_client = @@character_set_client */;
+/*!50001 SET @saved_cs_results = @@character_set_results */;
+/*!50001 SET @saved_col_connection = @@collation_connection */;
+/*!50001 SET character_set_client = latin1 */;
+/*!50001 SET character_set_results = latin1 */;
+/*!50001 SET collation_connection = latin1_swedish_ci */;
+/*!50001 CREATE ALGORITHM=UNDEFINED */
+/*!50013 DEFINER=`role3`@`%` SQL SECURITY DEFINER */
+/*!50001 VIEW `v3` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1` */;
+/*!50001 SET character_set_client = @saved_cs_client */;
+/*!50001 SET character_set_results = @saved_cs_results */;
+/*!50001 SET collation_connection = @saved_col_connection */;
+/*!50001 DROP TABLE IF EXISTS `v4`*/;
+/*!50001 SET @saved_cs_client = @@character_set_client */;
+/*!50001 SET @saved_cs_results = @@character_set_results */;
+/*!50001 SET @saved_col_connection = @@collation_connection */;
+/*!50001 SET character_set_client = latin1 */;
+/*!50001 SET character_set_results = latin1 */;
+/*!50001 SET collation_connection = latin1_swedish_ci */;
+/*!50001 CREATE ALGORITHM=UNDEFINED */
+/*!50013 DEFINER=`foo`@`localhost` SQL SECURITY DEFINER */
+/*!50001 VIEW `v4` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1` */;
+/*!50001 SET character_set_client = @saved_cs_client */;
+/*!50001 SET character_set_results = @saved_cs_results */;
+/*!50001 SET collation_connection = @saved_col_connection */;
+/*!50001 DROP TABLE IF EXISTS `v5`*/;
+/*!50001 SET @saved_cs_client = @@character_set_client */;
+/*!50001 SET @saved_cs_results = @@character_set_results */;
+/*!50001 SET @saved_col_connection = @@collation_connection */;
+/*!50001 SET character_set_client = latin1 */;
+/*!50001 SET character_set_results = latin1 */;
+/*!50001 SET collation_connection = latin1_swedish_ci */;
+/*!50001 CREATE ALGORITHM=UNDEFINED */
+/*!50013 DEFINER=`role4`@`%` SQL SECURITY DEFINER */
+/*!50001 VIEW `v5` AS select (`mysqltest1`.`t1`.`a` + `mysqltest1`.`t1`.`b`) AS `a+b`,`mysqltest1`.`t1`.`c` AS `c` from `mysqltest1`.`t1` */;
+/*!50001 SET character_set_client = @saved_cs_client */;
+/*!50001 SET character_set_results = @saved_cs_results */;
+/*!50001 SET collation_connection = @saved_col_connection */;
+
+USE `mysqltest1`;
+drop trigger tr1;
+drop trigger tr2;
+drop trigger tr3;
+drop procedure pr1;
+drop procedure pr2;
+drop procedure pr3;
+drop function fn1;
+drop function fn2;
+drop function fn3;
+drop event e1;
+drop event e2;
+drop event e3;
+drop view test.v1, test.v2, test.v3, test.v4, test.v5;
+drop table t1, t2;
+drop role role1, role2;
+drop user foo@localhost;
+drop database mysqltest1;
+use test;
+create user utest;
+prepare stmt1 from 'grant select on *.* to utest';
+execute stmt1;
+show grants for utest;
+Grants for utest@%
+GRANT SELECT ON *.* TO 'utest'@'%'
+drop user utest;
+create role utest;
+execute stmt1;
+show grants for utest;
+Grants for utest
+GRANT SELECT ON *.* TO 'utest'
+drop role utest;
diff --git a/mysql-test/suite/roles/definer.test b/mysql-test/suite/roles/definer.test
new file mode 100644
index 00000000000..3c069105c8c
--- /dev/null
+++ b/mysql-test/suite/roles/definer.test
@@ -0,0 +1,331 @@
+# create view
+# create trigger
+# create procedure
+# create event
+# mysqldump dumping the definer
+
+source include/not_embedded.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 role1 to current_user;
+
+# priv role
+create role role2;
+grant insert,select on mysqltest1.t1 to role2;
+grant event,execute,trigger on mysqltest1.* to role2;
+
+# create a non-priv user and a priv role granted to him
+grant create view on mysqltest1.* to foo@localhost;
+create role role4;
+grant select on mysqltest1.t1 to role4;
+grant role4 to foo@localhost;
+
+##################################################
+# views
+##################################################
+
+# no curent role = error
+--error ER_MALFORMED_DEFINER
+create definer=current_role view test.v1 as select a+b,c from t1;
+
+# definer=current_role, but it has doesn't have enough privileges
+set role role1;
+create definer=current_role view test.v1 as select a+b,c from t1;
+show create view test.v1;
+set role none;
+
+# definer=role_name, privileges ok
+create definer=role2 view test.v2 as select a+b,c from t1;
+show create view test.v2;
+
+# definer=non_existent_role
+create definer=role3 view test.v3 as select a+b,c from t1;
+show create view test.v3;
+
+connect (c1, localhost, foo,,mysqltest1);
+connection c1;
+show grants;
+
+# role1 doesn't have enough privileges for v1 to work
+--error ER_VIEW_INVALID
+select * from test.v1;
+
+# role2 is ok, v2 is ok
+select * from test.v2;
+
+# role3 is treated as a user name role3@%, doesn't exist, v3 fails
+--error ER_ACCESS_DENIED_ERROR
+select * from test.v3;
+
+# fails, no SUPER - cannot specify a definer arbitrarily
+--error ER_TABLEACCESS_DENIED_ERROR
+create definer=role4 view test.v4 as select a+b,c from t1;
+
+--error ER_TABLEACCESS_DENIED_ERROR
+select * from t1;
+set role role4;
+select * from t1;
+
+# can select from t1, but the view won't work, by default definer=current_user
+create view test.v4 as select a+b,c from t1;
+
+# now role4 is the current_role, can be specified as a definer
+create definer=role4 view test.v5 as select a+b,c from t1;
+
+--error ER_VIEW_INVALID
+select * from test.v4;
+select * from test.v5;
+set role none;
+--error ER_VIEW_INVALID
+select * from test.v4;
+select * from test.v5;
+
+connection default;
+
+drop role role4;
+
+show create view test.v5;
+--error ER_NO_SUCH_USER
+select * from test.v5;
+
+grant select on mysqltest1.t1 to role4;
+show create view test.v5;
+--error ER_NO_SUCH_USER
+select * from test.v5;
+
+# pretend it's an old view from before 10.0.5
+perl;
+local $/;
+my $f= "$ENV{MYSQLD_DATADIR}/test/v5.frm";
+open(F, '<', $f) or die "open(<$f): $!";
+$_=<F>;
+s/create-version=2/create-version=1/;
+open(F, '>', $f) or die "open(>$f): $!";
+syswrite F, $_ or die "syswrite($f): $!"
+EOF
+
+show create view test.v5;
+select * from test.v5;
+drop user role4;
+
+
+##################################################
+# trigger
+##################################################
+
+create table t2 select * from t1;
+
+# no curent role = error
+--error ER_MALFORMED_DEFINER
+create definer=current_role trigger tr1 before insert on t2 for each row
+ insert t1 values (111, 222, 333);
+
+# definer=current_role, but it has doesn't have enough privileges
+set role role1;
+create definer=current_role trigger tr1 before insert on t2 for each row
+ insert t1 values (111, 222, 333);
+show create trigger tr1;
+set role none;
+
+--error ER_TABLEACCESS_DENIED_ERROR
+insert t2 values (11,22,33);
+select * from t1;
+select * from t2;
+
+# definer=role_name, privileges ok
+create definer=role2 trigger tr2 before delete on t2 for each row
+ insert t1 values (111, 222, 333);
+show create trigger tr2;
+delete from t2 where a=1;
+select * from t1;
+select * from t2;
+delete from t1 where a=111;
+
+# definer=non_existent_role
+create definer=role3 trigger tr3 before update on t2 for each row
+ insert t1 values (111, 222, 333);
+show create trigger tr3;
+--error ER_NO_SUCH_USER
+update t2 set b=2 where a=2;
+select * from t1;
+select * from t2;
+
+flush tables;
+
+# change triggers to use pre-10.0.5 definer with an empty hostname
+perl;
+local $/;
+my $f= "$ENV{MYSQLD_DATADIR}/mysqltest1/t2.TRG";
+open(F, '<', $f) or die "open(<$f): $!";
+$_=<F>;
+s/'role2'/'role2\@'/;
+s/`role2`/$&\@``/;
+open(F, '>', $f) or die "open(>$f): $!";
+syswrite F, $_ or die "syswrite($f): $!"
+EOF
+
+show create trigger tr2;
+--error ER_NO_SUCH_USER
+delete from t2 where a=2;
+select * from t1;
+select * from t2;
+
+##################################################
+# stored procedures
+##################################################
+
+# no curent role = error
+--error ER_MALFORMED_DEFINER
+create definer=current_role procedure pr1() insert t1 values (111, 222, 333);
+
+# definer=current_role, but it has doesn't have enough privileges
+set role role1;
+create definer=current_role procedure pr1() insert t1 values (111, 222, 333);
+show create procedure pr1;
+set role none;
+
+--error ER_TABLEACCESS_DENIED_ERROR
+call pr1();
+select * from t1;
+
+# definer=role_name, privileges ok
+create definer=role2 procedure pr2() insert t1 values (111, 222, 333);
+show create procedure pr2;
+call pr2();
+select * from t1;
+delete from t1 where a=111;
+
+# definer=non_existent_role
+create definer=role3 procedure pr3() insert t1 values (111, 222, 333);
+show create procedure pr3;
+--error ER_NO_SUCH_USER
+call pr3();
+select * from t1;
+
+# change a procedure to use pre-10.0.5 definer with an empty hostname
+update mysql.proc set definer='role2@' where definer='role2';
+--error ER_NO_SUCH_USER
+call pr2();
+
+##################################################
+# stored functions
+##################################################
+
+# no curent role = error
+--error ER_MALFORMED_DEFINER
+create definer=current_role function fn1() returns int return (select sum(a+b) from t1);
+
+# definer=current_role, but it has doesn't have enough privileges
+set role role1;
+create definer=current_role function fn1() returns int return (select sum(a+b) from t1);
+show create function fn1;
+set role none;
+
+--error ER_COLUMNACCESS_DENIED_ERROR
+select fn1();
+select * from t1;
+
+# definer=role_name, privileges ok
+create definer=role2 function fn2() returns int return (select sum(a+b) from t1);
+show create function fn2;
+select fn2();
+
+# definer=non_existent_role
+create definer=role3 function fn3() returns int return (select sum(a+b) from t1);
+show create function fn3;
+--error ER_NO_SUCH_USER
+select fn3();
+
+##################################################
+# events
+##################################################
+
+set global event_scheduler=on;
+
+# no curent role = error
+--error ER_MALFORMED_DEFINER
+create definer=current_role event e1 on schedule every 1 second starts '2000-01-01' do
+ insert t1 values (111, 1, 0);
+
+# definer=current_role, but it has doesn't have enough privileges
+set role role1;
+create definer=current_role event e1 on schedule every 1 second starts '2000-01-01' do
+ insert t1 values (111, 2, 0);
+show create event e1;
+set role none;
+
+# definer=non_existent_role
+create definer=role3 event e3 on schedule every 1 second starts '2000-01-01' do
+ insert t1 values (111, 3, 0);
+show create event e3;
+
+# definer=role_name, privileges ok
+create definer=role2 event e2 on schedule every 1 second starts '2000-01-01' do
+ insert t1 values (111, 4, 0);
+show create event e2;
+
+let $wait_condition=select count(*) >= 4 from t1;
+--source include/wait_condition.inc
+
+set global event_scheduler=off;
+
+--sorted_result
+select distinct * from t1;
+delete from t1 where a=111;
+
+##################################################
+# mysqldump
+##################################################
+
+# note that LOCK TABLES won't work because v3 has invalid definer
+
+--exec $MYSQL_DUMP --compact --events --routines --skip-lock-tables --databases test mysqltest1
+
+##################################################
+# cleanup
+##################################################
+
+drop trigger tr1;
+drop trigger tr2;
+drop trigger tr3;
+drop procedure pr1;
+drop procedure pr2;
+drop procedure pr3;
+drop function fn1;
+drop function fn2;
+drop function fn3;
+drop event e1;
+drop event e2;
+drop event e3;
+drop view test.v1, test.v2, test.v3, test.v4, test.v5;
+drop table t1, t2;
+drop role role1, role2;
+drop user foo@localhost;
+drop database mysqltest1;
+use test;
+
+##################################################
+# reexecution
+##################################################
+
+create user utest;
+prepare stmt1 from 'grant select on *.* to utest';
+execute stmt1;
+show grants for utest;
+drop user utest;
+create role utest;
+execute stmt1;
+show grants for utest;
+drop role utest;
+
diff --git a/mysql-test/suite/roles/drop_current_user-5176.result b/mysql-test/suite/roles/drop_current_user-5176.result
new file mode 100644
index 00000000000..8e01bb10bd5
--- /dev/null
+++ b/mysql-test/suite/roles/drop_current_user-5176.result
@@ -0,0 +1,9 @@
+grant create user on *.* to foo@localhost;
+drop user foo@localhost;
+select * from information_schema.applicable_roles;
+GRANTEE ROLE_NAME IS_GRANTABLE
+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 00000000000..e022d3e8a86
--- /dev/null
+++ b/mysql-test/suite/roles/drop_current_user-5176.test
@@ -0,0 +1,13 @@
+#
+# MDEV-5176 Server crashes in fill_schema_applicable_roles on select from APPLICABLE_ROLES after a suicide
+#
+--source include/not_embedded.inc
+
+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 00000000000..ad2a82c37c5
--- /dev/null
+++ b/mysql-test/suite/roles/drop_routines.result
@@ -0,0 +1,79 @@
+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'
+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'
+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 00000000000..b5972d8d9f9
--- /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/none_public.result b/mysql-test/suite/roles/none_public.result
new file mode 100644
index 00000000000..8b92e5bfcfc
--- /dev/null
+++ b/mysql-test/suite/roles/none_public.result
@@ -0,0 +1,56 @@
+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'
+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;
+ERROR OP000: Invalid role specification `public`.
+grant select on *.* to public;
+ERROR OP000: Invalid role specification `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;
+ERROR OP000: Invalid role specification `public`.
+revoke select on *.* from public;
+ERROR OP000: Invalid role specification `public`.
+show grants for none;
+ERROR OP000: Invalid role specification `none`.
+show grants for public;
+ERROR OP000: Invalid role specification `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;
+optimize table mysql.user;
+Table Op Msg_type Msg_text
+mysql.user optimize status OK
+insert mysql.user (user, is_role) values ('none', 'Y'), ('public', 'Y');
+Warnings:
+Warning 1364 Field 'ssl_cipher' doesn't have a default value
+Warning 1364 Field 'x509_issuer' doesn't have a default value
+Warning 1364 Field 'x509_subject' doesn't have a default value
+Warning 1364 Field 'authentication_string' doesn't have a default value
+flush privileges;
+Warnings:
+Error 1959 Invalid role specification `none`.
+Error 1959 Invalid role specification `public`.
+delete from mysql.user where is_role='Y';
diff --git a/mysql-test/suite/roles/none_public.test b/mysql-test/suite/roles/none_public.test
new file mode 100644
index 00000000000..8e370d80007
--- /dev/null
+++ b/mysql-test/suite/roles/none_public.test
@@ -0,0 +1,57 @@
+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_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;
+--error ER_INVALID_ROLE
+grant role1 to public;
+--error ER_INVALID_ROLE
+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;
+--error ER_INVALID_ROLE
+revoke role1 from public;
+--error ER_INVALID_ROLE
+revoke select on *.* from public;
+
+--error ER_INVALID_ROLE
+show grants for none;
+--error ER_INVALID_ROLE
+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;
+
+optimize table mysql.user; # to remove deleted rows and have stable row order
+insert mysql.user (user, is_role) values ('none', 'Y'), ('public', 'Y');
+flush privileges;
+delete from mysql.user where is_role='Y';
+
diff --git a/mysql-test/suite/roles/password.result b/mysql-test/suite/roles/password.result
new file mode 100644
index 00000000000..e41816bdef7
--- /dev/null
+++ b/mysql-test/suite/roles/password.result
@@ -0,0 +1,34 @@
+create role r1;
+grant select on *.* to r1 identified by 'foobar';
+ERROR 28000: Can't find any matching row in the user table
+grant select on *.* to r1 identified by '';
+ERROR 28000: Can't find any matching row in the user table
+grant select on mysql.user to r1 identified by password '00000000000000000000000000000000000000000';
+ERROR 28000: Can't find any matching row in the user table
+grant select on *.* to r1 identified via plugin;
+ERROR 28000: Can't find any matching row in the user table
+grant select on mysql.user to r1 identified via plugin using 'param';
+ERROR 28000: Can't find any matching row in the user table
+grant select on *.* to r1 require subject 'foobar';
+ERROR 28000: Can't find any matching row in the user table
+grant select on mysql.user to r1 require issuer 'foobar';
+ERROR 28000: Can't find any matching row in the user table
+grant select on *.* to r1 require cipher 'foobar';
+ERROR 28000: Can't find any matching row in the user table
+grant select on mysql.user to r1 require ssl;
+ERROR 28000: Can't find any matching row in the user table
+grant select on *.* to r1 require x509;
+ERROR 28000: Can't find any matching row in the user table
+grant select on mysql.user to r1 require none;
+ERROR 28000: Can't find any matching row in the user table
+grant select on *.* to r1 with max_queries_per_hour 10;
+ERROR 28000: Can't find any matching row in the user table
+grant select on mysql.user to r1 with max_updates_per_hour 10;
+ERROR 28000: Can't find any matching row in the user table
+grant select on *.* to r1 with max_connections_per_hour 10;
+ERROR 28000: Can't find any matching row in the user table
+grant select on mysql.user to r1 with max_user_connections 10;
+ERROR 28000: Can't find any matching row in the user table
+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 00000000000..6b5073fae43
--- /dev/null
+++ b/mysql-test/suite/roles/password.test
@@ -0,0 +1,48 @@
+#
+# 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 ... = ...
+
+create role r1;
+
+--error ER_PASSWORD_NO_MATCH
+grant select on *.* to r1 identified by 'foobar';
+--error ER_PASSWORD_NO_MATCH
+grant select on *.* to r1 identified by '';
+--error ER_PASSWORD_NO_MATCH
+grant select on mysql.user to r1 identified by password '00000000000000000000000000000000000000000';
+--error ER_PASSWORD_NO_MATCH
+grant select on *.* to r1 identified via plugin;
+--error ER_PASSWORD_NO_MATCH
+grant select on mysql.user to r1 identified via plugin using 'param';
+--error ER_PASSWORD_NO_MATCH
+grant select on *.* to r1 require subject 'foobar';
+--error ER_PASSWORD_NO_MATCH
+grant select on mysql.user to r1 require issuer 'foobar';
+--error ER_PASSWORD_NO_MATCH
+grant select on *.* to r1 require cipher 'foobar';
+--error ER_PASSWORD_NO_MATCH
+grant select on mysql.user to r1 require ssl;
+--error ER_PASSWORD_NO_MATCH
+grant select on *.* to r1 require x509;
+--error ER_PASSWORD_NO_MATCH
+grant select on mysql.user to r1 require none;
+--error ER_PASSWORD_NO_MATCH
+grant select on *.* to r1 with max_queries_per_hour 10;
+--error ER_PASSWORD_NO_MATCH
+grant select on mysql.user to r1 with max_updates_per_hour 10;
+--error ER_PASSWORD_NO_MATCH
+grant select on *.* to r1 with max_connections_per_hour 10;
+--error ER_PASSWORD_NO_MATCH
+grant select on mysql.user to r1 with max_user_connections 10;
+--error ER_PASSWORD_NO_MATCH
+set password for r1 = '00000000000000000000000000000000000000000';
+
+drop role r1;
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 00000000000..2817c046ae9
--- /dev/null
+++ b/mysql-test/suite/roles/rebuild_role_grants.result
@@ -0,0 +1,58 @@
+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;
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 00000000000..3ec3577317a
--- /dev/null
+++ b/mysql-test/suite/roles/rebuild_role_grants.test
@@ -0,0 +1,56 @@
+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;
diff --git a/mysql-test/suite/roles/recursive.inc b/mysql-test/suite/roles/recursive.inc
new file mode 100644
index 00000000000..940a3c9e0db
--- /dev/null
+++ b/mysql-test/suite/roles/recursive.inc
@@ -0,0 +1,258 @@
+#
+# 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;
+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 00000000000..ef40c97865f
--- /dev/null
+++ b/mysql-test/suite/roles/recursive.result
@@ -0,0 +1,328 @@
+create user 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'.
+show grants;
+Grants for foo@localhost
+GRANT USAGE ON *.* TO 'foo'@'localhost'
+GRANT role10 TO 'foo'@'localhost'
+select * from information_schema.applicable_roles;
+GRANTEE ROLE_NAME IS_GRANTABLE
+foo@localhost role10 NO
+role10 role9 NO
+role2 role1 NO
+role4 role2 NO
+role5 role2 NO
+role5 role3 NO
+role6 role4 NO
+role6 role5 NO
+role7 role5 NO
+role9 role6 NO
+role9 role7 NO
+show status like 'debug%';
+Variable_name Value
+grant select on *.* to role1;
+show status like 'debug%';
+Variable_name Value
+select count(*) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table '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 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 role1 TO 'role2'
+GRANT role10 TO 'foo'@'localhost'
+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
+revoke select on *.* from role1;
+show status like 'debug%';
+Variable_name Value
+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 'roles_mapping'
+set role none;
+grant select on mysql.* to role1;
+show status like 'debug%';
+Variable_name Value
+select count(*) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table '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 USAGE ON *.* TO 'foo'@'localhost'
+GRANT USAGE ON *.* TO 'role1'
+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 role1 TO 'role2'
+GRANT role10 TO 'foo'@'localhost'
+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'
+revoke select on mysql.* from role1;
+show status like 'debug%';
+Variable_name Value
+select count(*) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table 'roles_mapping'
+set role none;
+grant select on mysql.roles_mapping to role1;
+show status like 'debug%';
+Variable_name Value
+select count(*) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table '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 USAGE ON *.* TO 'foo'@'localhost'
+GRANT USAGE ON *.* TO 'role1'
+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 role1 TO 'role2'
+GRANT role10 TO 'foo'@'localhost'
+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'
+revoke select on mysql.roles_mapping from role1;
+show status like 'debug%';
+Variable_name Value
+select count(*) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table 'roles_mapping'
+set role none;
+grant select(User) on mysql.roles_mapping to role1;
+show status like 'debug%';
+Variable_name Value
+select count(*) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table '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 USAGE ON *.* TO 'foo'@'localhost'
+GRANT USAGE ON *.* TO 'role1'
+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 role1 TO 'role2'
+GRANT role10 TO 'foo'@'localhost'
+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'
+grant select(Host) on mysql.roles_mapping to role3;
+show status like 'debug%';
+Variable_name Value
+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 USAGE ON *.* TO 'foo'@'localhost'
+GRANT USAGE ON *.* TO 'role1'
+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 role1 TO 'role2'
+GRANT role10 TO 'foo'@'localhost'
+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'
+revoke select(User) on mysql.roles_mapping from role1;
+show status like 'debug%';
+Variable_name Value
+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
+revoke select(Host) on mysql.roles_mapping from role3;
+show status like 'debug%';
+Variable_name Value
+select count(concat(Host)) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table 'roles_mapping'
+set role none;
+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
+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'
+grant execute on function test.fn1 to role5;
+show status like 'debug%';
+Variable_name Value
+select fn1();
+fn1()
+fn1
+revoke execute on procedure test.pr1 from role1;
+show status like 'debug%';
+Variable_name Value
+call pr1();
+ERROR 42000: execute command denied to user 'foo'@'localhost' for routine 'test.pr1'
+select fn1();
+fn1()
+fn1
+revoke execute on function test.fn1 from role5;
+show status like 'debug%';
+Variable_name Value
+select fn1();
+ERROR 42000: execute command denied to user 'foo'@'localhost' for routine 'test.fn1'
+set role none;
+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
+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 00000000000..0858f868d42
--- /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 00000000000..268ef47232e
--- /dev/null
+++ b/mysql-test/suite/roles/recursive_dbug.result
@@ -0,0 +1,448 @@
+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;
+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'.
+show grants;
+Grants for foo@localhost
+GRANT USAGE ON *.* TO 'foo'@'localhost'
+GRANT role10 TO 'foo'@'localhost'
+select * from information_schema.applicable_roles;
+GRANTEE ROLE_NAME IS_GRANTABLE
+foo@localhost role10 NO
+role10 role9 NO
+role2 role1 NO
+role4 role2 NO
+role5 role2 NO
+role5 role3 NO
+role6 role4 NO
+role6 role5 NO
+role7 role5 NO
+role9 role6 NO
+role9 role7 NO
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 0
+Debug_role_merges_db 0
+Debug_role_merges_table 0
+Debug_role_merges_column 0
+Debug_role_merges_routine 0
+grant select on *.* to role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 8
+Debug_role_merges_db 0
+Debug_role_merges_table 0
+Debug_role_merges_column 0
+Debug_role_merges_routine 0
+select count(*) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table '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 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 role1 TO 'role2'
+GRANT role10 TO 'foo'@'localhost'
+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
+revoke select on *.* from role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 16
+Debug_role_merges_db 0
+Debug_role_merges_table 0
+Debug_role_merges_column 0
+Debug_role_merges_routine 0
+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 'roles_mapping'
+set role none;
+grant select on mysql.* to role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 16
+Debug_role_merges_db 8
+Debug_role_merges_table 0
+Debug_role_merges_column 0
+Debug_role_merges_routine 0
+select count(*) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table '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 USAGE ON *.* TO 'foo'@'localhost'
+GRANT USAGE ON *.* TO 'role1'
+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 role1 TO 'role2'
+GRANT role10 TO 'foo'@'localhost'
+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'
+revoke select on mysql.* from role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 16
+Debug_role_merges_db 16
+Debug_role_merges_table 0
+Debug_role_merges_column 0
+Debug_role_merges_routine 0
+select count(*) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table 'roles_mapping'
+set role none;
+grant select on mysql.roles_mapping to role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 16
+Debug_role_merges_db 16
+Debug_role_merges_table 8
+Debug_role_merges_column 0
+Debug_role_merges_routine 0
+select count(*) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table '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 USAGE ON *.* TO 'foo'@'localhost'
+GRANT USAGE ON *.* TO 'role1'
+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 role1 TO 'role2'
+GRANT role10 TO 'foo'@'localhost'
+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'
+revoke select on mysql.roles_mapping from role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 16
+Debug_role_merges_db 16
+Debug_role_merges_table 16
+Debug_role_merges_column 0
+Debug_role_merges_routine 0
+select count(*) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table 'roles_mapping'
+set role none;
+grant select(User) on mysql.roles_mapping to role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 16
+Debug_role_merges_db 16
+Debug_role_merges_table 24
+Debug_role_merges_column 8
+Debug_role_merges_routine 0
+select count(*) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table '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 USAGE ON *.* TO 'foo'@'localhost'
+GRANT USAGE ON *.* TO 'role1'
+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 role1 TO 'role2'
+GRANT role10 TO 'foo'@'localhost'
+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'
+grant select(Host) on mysql.roles_mapping to role3;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 16
+Debug_role_merges_db 16
+Debug_role_merges_table 30
+Debug_role_merges_column 14
+Debug_role_merges_routine 0
+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 USAGE ON *.* TO 'foo'@'localhost'
+GRANT USAGE ON *.* TO 'role1'
+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 role1 TO 'role2'
+GRANT role10 TO 'foo'@'localhost'
+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'
+revoke select(User) on mysql.roles_mapping from role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 16
+Debug_role_merges_db 16
+Debug_role_merges_table 38
+Debug_role_merges_column 22
+Debug_role_merges_routine 0
+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
+revoke select(Host) on mysql.roles_mapping from role3;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 16
+Debug_role_merges_db 16
+Debug_role_merges_table 44
+Debug_role_merges_column 28
+Debug_role_merges_routine 0
+select count(concat(Host)) from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table 'roles_mapping'
+set role none;
+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 16
+Debug_role_merges_db 16
+Debug_role_merges_table 44
+Debug_role_merges_column 28
+Debug_role_merges_routine 8
+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'
+grant execute on function test.fn1 to role5;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 16
+Debug_role_merges_db 16
+Debug_role_merges_table 44
+Debug_role_merges_column 28
+Debug_role_merges_routine 13
+select fn1();
+fn1()
+fn1
+revoke execute on procedure test.pr1 from role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 16
+Debug_role_merges_db 16
+Debug_role_merges_table 44
+Debug_role_merges_column 28
+Debug_role_merges_routine 21
+call pr1();
+ERROR 42000: execute command denied to user 'foo'@'localhost' for routine 'test.pr1'
+select fn1();
+fn1()
+fn1
+revoke execute on function test.fn1 from role5;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 16
+Debug_role_merges_db 16
+Debug_role_merges_table 44
+Debug_role_merges_column 28
+Debug_role_merges_routine 26
+select fn1();
+ERROR 42000: execute command denied to user 'foo'@'localhost' for routine 'test.fn1'
+set role none;
+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 16
+Debug_role_merges_db 16
+Debug_role_merges_table 50
+Debug_role_merges_column 28
+Debug_role_merges_routine 26
+grant select on mysql.roles_mapping to role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 16
+Debug_role_merges_db 16
+Debug_role_merges_table 53
+Debug_role_merges_column 28
+Debug_role_merges_routine 26
+revoke select on mysql.roles_mapping from role3;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 16
+Debug_role_merges_db 16
+Debug_role_merges_table 54
+Debug_role_merges_column 28
+Debug_role_merges_routine 26
+revoke select on mysql.roles_mapping from role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 16
+Debug_role_merges_db 16
+Debug_role_merges_table 62
+Debug_role_merges_column 28
+Debug_role_merges_routine 26
+grant select on mysql.* to role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 16
+Debug_role_merges_db 24
+Debug_role_merges_table 62
+Debug_role_merges_column 28
+Debug_role_merges_routine 26
+grant select on test.* to role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 16
+Debug_role_merges_db 32
+Debug_role_merges_table 62
+Debug_role_merges_column 28
+Debug_role_merges_routine 26
+revoke select on mysql.* from role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 16
+Debug_role_merges_db 40
+Debug_role_merges_table 62
+Debug_role_merges_column 28
+Debug_role_merges_routine 26
+revoke select on test.* from role1;
+show status like 'debug%';
+Variable_name Value
+Debug_role_merges_global 16
+Debug_role_merges_db 48
+Debug_role_merges_table 62
+Debug_role_merges_column 28
+Debug_role_merges_routine 26
+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 00000000000..811695994e0
--- /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 00000000000..987d90a5820
--- /dev/null
+++ b/mysql-test/suite/roles/rename_user.result
@@ -0,0 +1,27 @@
+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;
diff --git a/mysql-test/suite/roles/rename_user.test b/mysql-test/suite/roles/rename_user.test
new file mode 100644
index 00000000000..45429b76666
--- /dev/null
+++ b/mysql-test/suite/roles/rename_user.test
@@ -0,0 +1,38 @@
+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;
+
diff --git a/mysql-test/suite/roles/revoke_all.result b/mysql-test/suite/roles/revoke_all.result
new file mode 100644
index 00000000000..7e72b5bc766
--- /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 00000000000..a3dee981c40
--- /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/rpl_definer.result b/mysql-test/suite/roles/rpl_definer.result
new file mode 100644
index 00000000000..eadb6315d9b
--- /dev/null
+++ b/mysql-test/suite/roles/rpl_definer.result
@@ -0,0 +1,72 @@
+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 CREATE DEFINER=`root`@`localhost` PROCEDURE `pcu`()
+select current_user latin1 latin1_swedish_ci latin1_swedish_ci
+show create procedure pu;
+Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation
+pu CREATE DEFINER=`root`@`localhost` PROCEDURE `pu`()
+select "root@localhost" latin1 latin1_swedish_ci latin1_swedish_ci
+show create procedure pcr;
+Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation
+pcr CREATE DEFINER=`role1` PROCEDURE `pcr`()
+select current_role latin1 latin1_swedish_ci latin1_swedish_ci
+show create procedure pr;
+Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation
+pr CREATE DEFINER=`role1` PROCEDURE `pr`()
+select "role1" latin1 latin1_swedish_ci latin1_swedish_ci
+[connection slave]
+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 CREATE DEFINER=`root`@`localhost` PROCEDURE `pcu`()
+select current_user latin1 latin1_swedish_ci latin1_swedish_ci
+show create procedure pu;
+Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation
+pu CREATE DEFINER=`root`@`localhost` PROCEDURE `pu`()
+select "root@localhost" latin1 latin1_swedish_ci latin1_swedish_ci
+show create procedure pcr;
+Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation
+pcr CREATE DEFINER=`role1` PROCEDURE `pcr`()
+select current_role latin1 latin1_swedish_ci latin1_swedish_ci
+show create procedure pr;
+Procedure sql_mode Create Procedure character_set_client collation_connection Database Collation
+pr CREATE DEFINER=`role1` PROCEDURE `pr`()
+select "role1" latin1 latin1_swedish_ci latin1_swedish_ci
+drop procedure pcu;
+drop procedure pu;
+drop procedure pcr;
+drop procedure pr;
+drop role role1;
+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 00000000000..86d69de86aa
--- /dev/null
+++ b/mysql-test/suite/roles/rpl_definer.test
@@ -0,0 +1,48 @@
+#
+# 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;
+connection slave;
+echo [connection slave];
+
+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/set_and_drop.result b/mysql-test/suite/roles/set_and_drop.result
new file mode 100644
index 00000000000..1814c19cc97
--- /dev/null
+++ b/mysql-test/suite/roles/set_and_drop.result
@@ -0,0 +1,117 @@
+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;
+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 '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 't2'
+select a from mysqltest1.t2;
+a
+5
+7
+call mysqltest1.pr1();
+pr1
+pr1
+revoke execute on procedure mysqltest1.pr1 from role2;
+call mysqltest1.pr1();
+ERROR 42000: execute command denied to user 'foo'@'localhost' for routine 'mysqltest1.pr1'
+drop role role2;
+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 'roles_mapping'
+select * from mysqltest1.t1;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table 't1'
+select a from mysqltest1.t2;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table 't2'
+set role none;
+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;
+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 't2'
+select a from mysqltest1.t2;
+a
+5
+7
+call mysqltest1.pr1();
+pr1
+pr1
+drop role role1;
+flush tables;
+select * from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table 'roles_mapping'
+select * from mysqltest1.t1;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table 't1'
+select a from mysqltest1.t2;
+ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table '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
+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 00000000000..852e79fd109
--- /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_role-database-recursive.result b/mysql-test/suite/roles/set_role-database-recursive.result
new file mode 100644
index 00000000000..479e553c3d1
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-database-recursive.result
@@ -0,0 +1,83 @@
+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
+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 '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 '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 '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 00000000000..17c93d4571f
--- /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 00000000000..e21a55edf2e
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-database-simple.result
@@ -0,0 +1,52 @@
+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
+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 '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 '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 'roles_mapping'
+insert into mysql.user (user, host) values ('Dummy', 'Dummy');
+ERROR 42000: INSERT command denied to user 'test_user'@'localhost' for table '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 'roles_mapping'
+delete from mysql.roles_mapping where Role='test_role2';
+ERROR 42000: DELETE command denied to user 'test_user'@'localhost' for table '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 00000000000..daac13a095d
--- /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 00000000000..fca53b4d645
--- /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 '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 '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 '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 00000000000..ecfe8869c12
--- /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 00000000000..0bec8026ab5
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-recursive.result
@@ -0,0 +1,118 @@
+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
+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 ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections plugin authentication_string password_expired is_role
+ test_role1 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 N Y
+select * from mysql.user where user like 'test_role2';
+Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections plugin authentication_string password_expired is_role
+ test_role2 Y N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 N Y
+select * from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table '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 '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: Invalid role specification `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 '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 '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 00000000000..23d623e1966
--- /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 00000000000..3e17a78ad77
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-routine-simple.result
@@ -0,0 +1,103 @@
+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
+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 00000000000..0e808d190d6
--- /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 00000000000..ea416835017
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-simple.result
@@ -0,0 +1,47 @@
+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
+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 ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections plugin authentication_string password_expired is_role
+ 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 0 0 0 0 N Y
+select * from mysql.roles_mapping;
+ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table '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 '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';
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 00000000000..152a1d0eb25
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-simple.test
@@ -0,0 +1,39 @@
+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';
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 00000000000..721bd3039a3
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-table-column-priv.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
+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 '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 'roles_mapping'
+drop user 'test_user'@'localhost';
+select * from mysql.tables_priv;
+Host Db User Table_name Grantor Timestamp Table_priv Column_priv
+ 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 00000000000..e154b259b04
--- /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 00000000000..f5688dbe62e
--- /dev/null
+++ b/mysql-test/suite/roles/set_role-table-simple.result
@@ -0,0 +1,67 @@
+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
+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 '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 'roles_mapping'
+drop user 'test_user'@'localhost';
+select * from mysql.tables_priv;
+Host Db User Table_name Grantor Timestamp Table_priv Column_priv
+ 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 00000000000..8de7781927a
--- /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_grants.result b/mysql-test/suite/roles/show_grants.result
new file mode 100644
index 00000000000..e7b7860c74f
--- /dev/null
+++ b/mysql-test/suite/roles/show_grants.result
@@ -0,0 +1,148 @@
+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
+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
+root@localhost test_role1 YES
+root@localhost test_role2 YES
+test_role1 test_role2 NO
+select * from information_schema.applicable_roles;
+GRANTEE ROLE_NAME IS_GRANTABLE
+test_role1 test_role2 NO
+test_user@localhost test_role1 NO
+test_user@localhost test_role2 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;
diff --git a/mysql-test/suite/roles/show_grants.test b/mysql-test/suite/roles/show_grants.test
new file mode 100644
index 00000000000..9c15d8b8b2b
--- /dev/null
+++ b/mysql-test/suite/roles/show_grants.test
@@ -0,0 +1,90 @@
+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;
diff --git a/mysql-test/suite/rpl/r/rpl_do_grant.result b/mysql-test/suite/rpl/r/rpl_do_grant.result
index f1e994c660b..4cbc2d7d9e9 100644
--- a/mysql-test/suite/rpl/r/rpl_do_grant.result
+++ b/mysql-test/suite/rpl/r/rpl_do_grant.result
@@ -190,7 +190,7 @@ GRANT EXECUTE ON PROCEDURE `test`.`p1` TO 'user49119'@'localhost'
SHOW GRANTS FOR CURRENT_USER;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
-GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
##############################################################
##############################################################
### Showing grants for both users: root and user49119 (master)
@@ -201,7 +201,7 @@ GRANT EXECUTE ON PROCEDURE `test`.`p1` TO 'user49119'@'localhost'
SHOW GRANTS FOR CURRENT_USER;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
-GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
##############################################################
## This statement will make the revoke fail because root has no
## execute grant. However, it will still revoke the grant for
@@ -217,7 +217,7 @@ GRANT USAGE ON *.* TO 'user49119'@'localhost'
SHOW GRANTS FOR CURRENT_USER;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
-GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
##############################################################
#############################################################
### Showing grants for both users: root and user49119 (slave)
@@ -228,7 +228,7 @@ GRANT USAGE ON *.* TO 'user49119'@'localhost'
SHOW GRANTS FOR CURRENT_USER;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
-GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'%' TO 'root'@'localhost' WITH GRANT OPTION
##############################################################
DROP TABLE t1;
DROP PROCEDURE p1;
diff --git a/mysql-test/t/grant.test b/mysql-test/t/grant.test
index 20284ec9ae9..1fa4101bfe3 100644
--- a/mysql-test/t/grant.test
+++ b/mysql-test/t/grant.test
@@ -587,7 +587,6 @@ flush privileges;
# Create some users with different hostnames
create user mysqltest_8@'';
-create user mysqltest_8;
create user mysqltest_8@host8;
# Try to create them again
@@ -614,7 +613,6 @@ select * from t1;
disconnect conn3;
connection master;
revoke select on mysqltest.* from mysqltest_8@'';
-revoke select on mysqltest.* from mysqltest_8;
show grants for mysqltest_8@'';
show grants for mysqltest_8;
select * from information_schema.schema_privileges
@@ -642,7 +640,6 @@ select * from t1;
disconnect conn4;
connection master;
revoke update (a) on t1 from mysqltest_8@'';
-revoke update (a) on t1 from mysqltest_8;
show grants for mysqltest_8@'';
show grants for mysqltest_8;
select * from information_schema.column_privileges;
@@ -664,7 +661,6 @@ select * from t1;
disconnect conn5;
connection master;
revoke update on t1 from mysqltest_8@'';
-revoke update on t1 from mysqltest_8;
show grants for mysqltest_8@'';
show grants for mysqltest_8;
select * from information_schema.table_privileges;
@@ -692,10 +688,6 @@ show grants for mysqltest_8;
drop user mysqltest_8@'';
--error ER_NONEXISTING_GRANT
show grants for mysqltest_8@'';
-show grants for mysqltest_8;
-select * from information_schema.user_privileges
-where grantee like "'mysqltest_8'%";
-drop user mysqltest_8;
--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT
--error ER_ACCESS_DENIED_ERROR
connect (conn6,localhost,mysqltest_8,,);
@@ -1669,7 +1661,6 @@ use test;
#
--echo FLUSH PRIVILEGES without procs_priv table.
RENAME TABLE mysql.procs_priv TO mysql.procs_gone;
---error ER_NO_SUCH_TABLE
FLUSH PRIVILEGES;
--echo Assigning privileges without procs_priv table.
CREATE DATABASE mysqltest1;
diff --git a/mysql-test/t/grant2.test b/mysql-test/t/grant2.test
index cde3d12b0ce..8590dccd1d1 100644
--- a/mysql-test/t/grant2.test
+++ b/mysql-test/t/grant2.test
@@ -405,7 +405,7 @@ drop database mysqltest_1;
# But anonymous users can't change their password
connect (n5,localhost,test,,test,$MASTER_MYPORT,$MASTER_MYSOCK);
connection n5;
---error ER_PASSWORD_NO_MATCH
+--error ER_PASSWORD_ANONYMOUS_USER
set password = password("changed");
disconnect n5;
connection default;
diff --git a/mysql-test/t/join_cache.test b/mysql-test/t/join_cache.test
index 2d06c3e2a30..7859822bcad 100644
--- a/mysql-test/t/join_cache.test
+++ b/mysql-test/t/join_cache.test
@@ -3636,5 +3636,49 @@ set optimizer_switch=@tmp_optimizer_switch;
DROP table t1,t2,t3;
+--echo #
+--echo # MDEV-5123 Remove duplicated conditions pushed both to join_tab->select_cond and join_tab->cache_select->cond for blocked joins.
+--echo #
+
+set join_cache_level=default;
+set expensive_subquery_limit=0;
+
+create table t1 (c1 int);
+create table t2 (c2 int);
+create table t3 (c3 int);
+
+insert into t1 values (1), (2);
+insert into t2 values (1), (2);
+insert into t3 values (2);
+
+explain
+select count(*) from t1 straight_join t2
+where c1 = c2-0 and c2 <= (select max(c3) from t3 where c3 = 2 and @counter:=@counter+1);
+
+set @counter=0;
+
+select count(*) from t1 straight_join t2
+where c1 = c2-0 and c2 <= (select max(c3) from t3 where c3 = 2 and @counter:=@counter+1);
+
+select @counter;
+
+explain
+select count(*) from t1 straight_join t2
+where c1 = c2-0 and
+ c2 <= (select max(c3) from t3 where c3 = 2 and @counter:=@counter+1) and
+ c2 / 2 = 1;
+
+set @counter=0;
+
+select count(*) from t1 straight_join t2
+where c1 = c2-0 and
+ c2 <= (select max(c3) from t3 where c3 = 2 and @counter:=@counter+1) and
+ c2 / 2 = 1;
+
+select @counter;
+
+drop table t1,t2,t3;
+set expensive_subquery_limit=default;
+
# this must be the last command in the file
set @@optimizer_switch=@save_optimizer_switch;
diff --git a/mysql-test/t/limit_rows_examined.test b/mysql-test/t/limit_rows_examined.test
index ef28a37108f..45ee483c7aa 100644
--- a/mysql-test/t/limit_rows_examined.test
+++ b/mysql-test/t/limit_rows_examined.test
@@ -502,7 +502,7 @@ SELECT a AS field1, alias2.d AS field2, alias2.f AS field3, alias2.e AS field4,
FROM t1, t2 AS alias2, t2 AS alias3
WHERE alias3.c IN ( SELECT 1 UNION SELECT 6 )
GROUP BY field1, field2, field3, field4, field5
-LIMIT ROWS EXAMINED 250;
+LIMIT ROWS EXAMINED 124;
SHOW STATUS LIKE 'Handler_read%';
SHOW STATUS LIKE 'Handler_tmp%';
diff --git a/mysql-test/t/plugin_auth.test b/mysql-test/t/plugin_auth.test
index 366dc75c425..be07913137e 100644
--- a/mysql-test/t/plugin_auth.test
+++ b/mysql-test/t/plugin_auth.test
@@ -169,7 +169,7 @@ connection grant_plug_dest_con;
--echo ## testing what an ordinary user can grant
--echo this should fail : no rights to grant all
--error ER_ACCESS_DENIED_NO_PASSWORD_ERROR
-GRANT PROXY ON ''@'' TO grant_plug;
+GRANT PROXY ON ''@'%%' TO grant_plug;
--echo this should fail : not the same user
--error ER_ACCESS_DENIED_NO_PASSWORD_ERROR
@@ -212,11 +212,11 @@ disconnect grant_plug_dest_con;
--echo # test what root can grant
--echo should work : root has PROXY to all users
-GRANT PROXY ON ''@'' TO grant_plug;
-REVOKE PROXY ON ''@'' FROM grant_plug;
+GRANT PROXY ON ''@'%%' TO grant_plug;
+REVOKE PROXY ON ''@'%%' FROM grant_plug;
--echo should work : root has PROXY to all users
-GRANT PROXY ON ''@'' TO proxy_admin IDENTIFIED BY 'test'
+GRANT PROXY ON ''@'%%' TO proxy_admin IDENTIFIED BY 'test'
WITH GRANT OPTION;
--echo need USAGE : PROXY doesn't contain it.
@@ -226,7 +226,7 @@ connect (proxy_admin_con,localhost,proxy_admin,test);
connection proxy_admin_con;
--echo in proxy_admin_con;
---echo should work : proxy_admin has proxy to ''@''
+--echo should work : proxy_admin has proxy to ''@'%%'
GRANT PROXY ON future_user TO grant_plug;
connection default;
@@ -318,9 +318,9 @@ DROP USER plug_dest;
--echo #
GRANT ALL PRIVILEGES ON *.* TO power_user;
-GRANT USAGE ON anonymous_db.* TO ''@''
+GRANT USAGE ON anonymous_db.* TO ''@'%%'
IDENTIFIED WITH 'test_plugin_server' AS 'power_user';
-GRANT PROXY ON power_user TO ''@'';
+GRANT PROXY ON power_user TO ''@'%%';
CREATE DATABASE confidential_db;
connect(plug_con,localhost, test_login_user, power_user, confidential_db);
@@ -330,24 +330,23 @@ connection default;
disconnect plug_con;
DROP USER power_user;
-DROP USER ''@'';
+DROP USER ''@'%%';
DROP DATABASE confidential_db;
--echo # Test case #2 (crash with double grant proxy)
-CREATE USER ''@'' IDENTIFIED WITH 'test_plugin_server' AS 'standard_user';
+CREATE USER ''@'%%' IDENTIFIED WITH 'test_plugin_server' AS 'standard_user';
CREATE USER standard_user;
CREATE DATABASE shared;
GRANT ALL PRIVILEGES ON shared.* TO standard_user;
-GRANT PROXY ON standard_user TO ''@'';
+GRANT PROXY ON standard_user TO ''@'%%';
--echo #should not crash
-GRANT PROXY ON standard_user TO ''@'';
+GRANT PROXY ON standard_user TO ''@'%%';
-DROP USER ''@'';
+DROP USER ''@'%%';
DROP USER standard_user;
DROP DATABASE shared;
-
--echo #
--echo # Bug #57551 : Live upgrade fails between 5.1.52 -> 5.5.7-rc
--echo #
diff --git a/mysql-test/t/plugin_auth_qa_1.test b/mysql-test/t/plugin_auth_qa_1.test
index 93e471da25d..55fef4254e1 100644
--- a/mysql-test/t/plugin_auth_qa_1.test
+++ b/mysql-test/t/plugin_auth_qa_1.test
@@ -184,7 +184,7 @@ DROP USER new_user,new_dest,plug_dest;
--echo ========== test 2, 2.1, 2.2 ================================
-CREATE USER ''@'' IDENTIFIED WITH test_plugin_server AS 'proxied_user';
+CREATE USER ''@'%%' IDENTIFIED WITH test_plugin_server AS 'proxied_user';
CREATE USER proxied_user IDENTIFIED BY 'proxied_user_passwd';
--sorted_result
SELECT user,plugin,authentication_string FROM mysql.user WHERE user != 'root';
@@ -202,7 +202,7 @@ disconnect proxy_con;
--error ER_ACCESS_DENIED_ERROR : this should fail : no grant
connect(proxy_con,localhost,proxy_user,proxied_user);
--enable_query_log
-GRANT PROXY ON proxied_user TO ''@'';
+GRANT PROXY ON proxied_user TO ''@'%%';
--echo connect(proxy_con,localhost,proxied_user,proxied_user_passwd);
connect(proxy_con,localhost,proxied_user,proxied_user_passwd);
SELECT USER(),CURRENT_USER();
@@ -219,9 +219,9 @@ SELECT @@proxy_user;
connection default;
--echo disconnect proxy_con;
disconnect proxy_con;
-DROP USER ''@'',proxied_user;
+DROP USER ''@'%%',proxied_user;
#
-GRANT ALL PRIVILEGES ON test_user_db.* TO ''@''
+GRANT ALL PRIVILEGES ON test_user_db.* TO ''@'%%'
IDENTIFIED WITH test_plugin_server AS 'proxied_user';
CREATE USER proxied_user IDENTIFIED BY 'proxied_user_passwd';
--sorted_result
@@ -239,7 +239,7 @@ disconnect proxy_con;
--error ER_ACCESS_DENIED_ERROR : this should fail : no grant
connect(proxy_con,localhost,proxy_user,proxied_user);
--enable_query_log
-GRANT PROXY ON proxied_user TO ''@'';
+GRANT PROXY ON proxied_user TO ''@'%%';
--echo connect(proxy_con,localhost,proxied_user,proxied_user_passwd);
connect(proxy_con,localhost,proxied_user,proxied_user_passwd);
SELECT USER(),CURRENT_USER();
@@ -255,19 +255,19 @@ SELECT @@proxy_user;
connection default;
--echo disconnect proxy_con;
disconnect proxy_con;
-DROP USER ''@'',proxied_user;
+DROP USER ''@'%%',proxied_user;
#
-CREATE USER ''@'' IDENTIFIED WITH test_plugin_server AS 'proxied_user';
+CREATE USER ''@'%%' IDENTIFIED WITH test_plugin_server AS 'proxied_user';
CREATE USER proxied_user_1 IDENTIFIED BY 'proxied_user_1_pwd';
CREATE USER proxied_user_2 IDENTIFIED BY 'proxied_user_2_pwd';
CREATE USER proxied_user_3 IDENTIFIED BY 'proxied_user_3_pwd';
CREATE USER proxied_user_4 IDENTIFIED BY 'proxied_user_4_pwd';
CREATE USER proxied_user_5 IDENTIFIED BY 'proxied_user_5_pwd';
-GRANT PROXY ON proxied_user_1 TO ''@'';
-GRANT PROXY ON proxied_user_2 TO ''@'';
-GRANT PROXY ON proxied_user_3 TO ''@'';
-GRANT PROXY ON proxied_user_4 TO ''@'';
-GRANT PROXY ON proxied_user_5 TO ''@'';
+GRANT PROXY ON proxied_user_1 TO ''@'%%';
+GRANT PROXY ON proxied_user_2 TO ''@'%%';
+GRANT PROXY ON proxied_user_3 TO ''@'%%';
+GRANT PROXY ON proxied_user_4 TO ''@'%%';
+GRANT PROXY ON proxied_user_5 TO ''@'%%';
--sorted_result
SELECT user,plugin,authentication_string FROM mysql.user WHERE user != 'root';
--echo connect(proxy_con_1,localhost,proxied_user_1,'proxied_user_1_pwd');
@@ -312,7 +312,7 @@ disconnect proxy_con_3;
disconnect proxy_con_4;
--echo disconnect proxy_con_5;
disconnect proxy_con_5;
-DROP USER ''@'',proxied_user_1,proxied_user_2,proxied_user_3,proxied_user_4,proxied_user_5;
+DROP USER ''@'%%',proxied_user_1,proxied_user_2,proxied_user_3,proxied_user_4,proxied_user_5;
--echo ========== test 3 ==========================================
diff --git a/mysql-test/t/system_mysql_db_fix40123.test b/mysql-test/t/system_mysql_db_fix40123.test
index 291da0e7a70..25d977deeba 100644
--- a/mysql-test/t/system_mysql_db_fix40123.test
+++ b/mysql-test/t/system_mysql_db_fix40123.test
@@ -37,7 +37,7 @@ CREATE TABLE host ( Host char(60) binary DEFAULT '' NOT NULL, Db char(64) bina
CREATE TABLE user ( Host char(60) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL, Password char(41) binary DEFAULT '' NOT NULL, Select_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Insert_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Update_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Delete_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Drop_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Reload_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Shutdown_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Process_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, File_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Grant_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, References_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Index_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_db_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Super_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tmp_table_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Lock_tables_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Execute_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_slave_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_client_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, ssl_type enum('','ANY','X509', 'SPECIFIED') COLLATE utf8_general_ci DEFAULT '' NOT NULL, ssl_cipher BLOB NOT NULL, x509_issuer BLOB NOT NULL, x509_subject BLOB NOT NULL, max_questions int(11) unsigned DEFAULT 0 NOT NULL, max_updates int(11) unsigned DEFAULT 0 NOT NULL, max_connections int(11) unsigned DEFAULT 0 NOT NULL, PRIMARY KEY Host (Host,User) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Users and global privileges';
INSERT INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);
- INSERT INTO user VALUES ('localhost','','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);
+INSERT INTO user VALUES ('localhost','','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);
CREATE TABLE func ( name char(64) binary DEFAULT '' NOT NULL, ret tinyint(1) DEFAULT '0' NOT NULL, dl char(128) DEFAULT '' NOT NULL, type enum ('function','aggregate') COLLATE utf8_general_ci NOT NULL, PRIMARY KEY (name) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='User defined functions';
@@ -78,8 +78,7 @@ CREATE TABLE index_stats (db_name varchar(64) NOT NULL, table_name varchar(64) N
-- disable_query_log
# Drop all tables created by this test
-DROP TABLE db, host, user, func, plugin, tables_priv, columns_priv, procs_priv, servers, help_category, help_keyword, help_relation, help_topic, proc, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type, general_log, slow_log, event, proxies_priv, innodb_index_stats, innodb_table_stats, table_stats, column_stats, index_stats,
- gtid_slave_pos;
+DROP TABLE db, host, user, func, plugin, tables_priv, columns_priv, procs_priv, servers, help_category, help_keyword, help_relation, help_topic, proc, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type, general_log, slow_log, event, proxies_priv, innodb_index_stats, innodb_table_stats, table_stats, column_stats, index_stats, roles_mapping, gtid_slave_pos;
-- enable_query_log
diff --git a/mysql-test/t/system_mysql_db_fix50030.test b/mysql-test/t/system_mysql_db_fix50030.test
index b034087bf0e..1ab6ac3c7b9 100644
--- a/mysql-test/t/system_mysql_db_fix50030.test
+++ b/mysql-test/t/system_mysql_db_fix50030.test
@@ -85,8 +85,8 @@ CREATE TABLE index_stats (db_name varchar(64) NOT NULL, table_name varchar(64) N
-- disable_query_log
# Drop all tables created by this test
-DROP TABLE db, host, user, func, plugin, tables_priv, columns_priv, procs_priv, servers, help_category, help_keyword, help_relation, help_topic, proc, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type, general_log, slow_log, event, proxies_priv, innodb_index_stats, innodb_table_stats, table_stats, column_stats, index_stats,
- gtid_slave_pos;
+DROP TABLE db, host, user, func, plugin, tables_priv, columns_priv, procs_priv, servers, help_category, help_keyword, help_relation, help_topic, proc, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type, general_log, slow_log, event, proxies_priv, innodb_index_stats, innodb_table_stats, table_stats, column_stats, index_stats, roles_mapping, gtid_slave_pos;
+
-- enable_query_log
# check that we dropped all system tables
diff --git a/mysql-test/t/system_mysql_db_fix50117.test b/mysql-test/t/system_mysql_db_fix50117.test
index 915d6a85c26..2f9ba5477d0 100644
--- a/mysql-test/t/system_mysql_db_fix50117.test
+++ b/mysql-test/t/system_mysql_db_fix50117.test
@@ -105,8 +105,8 @@ CREATE TABLE index_stats (db_name varchar(64) NOT NULL, table_name varchar(64) N
-- disable_query_log
# Drop all tables created by this test
-DROP TABLE db, host, user, func, plugin, tables_priv, columns_priv, procs_priv, servers, help_category, help_keyword, help_relation, help_topic, proc, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type, general_log, slow_log, event, proxies_priv, innodb_index_stats, innodb_table_stats, table_stats, column_stats, index_stats,
- gtid_slave_pos;
+DROP TABLE db, host, user, func, plugin, tables_priv, columns_priv, procs_priv, servers, help_category, help_keyword, help_relation, help_topic, proc, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type, general_log, slow_log, event, proxies_priv, innodb_index_stats, innodb_table_stats, table_stats, column_stats, index_stats, roles_mapping, gtid_slave_pos;
+
-- enable_query_log
# check that we dropped all system tables
diff --git a/mysys/array.c b/mysys/array.c
index cf377f77676..e92c5b45e67 100644
--- a/mysys/array.c
+++ b/mysys/array.c
@@ -321,7 +321,24 @@ void delete_dynamic_element(DYNAMIC_ARRAY *array, uint idx)
(array->elements-idx)*array->size_of_element);
}
+/*
+ Wrapper around delete_dynamic, calling a FREE function on every
+ element, before releasing the memory
+ SYNOPSIS
+ delete_dynamic_with_callback()
+ array
+ f The function to be called on every element before
+ deleting the array;
+*/
+void delete_dynamic_with_callback(DYNAMIC_ARRAY *array, FREE_FUNC f) {
+ uint i;
+ char *ptr= (char*) array->buffer;
+ for (i= 0; i < array->elements; i++, ptr+= array->size_of_element) {
+ f(ptr);
+ }
+ delete_dynamic(array);
+}
/*
Free unused memory
diff --git a/scripts/mysql_system_tables.sql b/scripts/mysql_system_tables.sql
index 69d3c608f80..219d909ff24 100644
--- a/scripts/mysql_system_tables.sql
+++ b/scripts/mysql_system_tables.sql
@@ -35,11 +35,12 @@ set @had_db_table= @@warning_count != 0;
CREATE TABLE IF NOT EXISTS host ( Host char(60) binary DEFAULT '' NOT NULL, Db char(64) binary DEFAULT '' NOT NULL, Select_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Insert_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Update_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Delete_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Drop_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Grant_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, References_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Index_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tmp_table_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Lock_tables_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Execute_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Trigger_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, PRIMARY KEY Host (Host,Db) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Host privileges; Merged with database privileges';
-CREATE TABLE IF NOT EXISTS user ( Host char(60) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL, Password char(41) character set latin1 collate latin1_bin DEFAULT '' NOT NULL, Select_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Insert_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Update_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Delete_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Drop_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Reload_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Shutdown_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Process_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, File_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Grant_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, References_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Index_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_db_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Super_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tmp_table_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Lock_tables_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Execute_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_slave_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_client_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_user_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Event_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Trigger_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tablespace_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, ssl_type enum('','ANY','X509', 'SPECIFIED') COLLATE utf8_general_ci DEFAULT '' NOT NULL, ssl_cipher BLOB NOT NULL, x509_issuer BLOB NOT NULL, x509_subject BLOB NOT NULL, max_questions int(11) unsigned DEFAULT 0 NOT NULL, max_updates int(11) unsigned DEFAULT 0 NOT NULL, max_connections int(11) unsigned DEFAULT 0 NOT NULL, max_user_connections int(11) DEFAULT 0 NOT NULL, plugin char(64) CHARACTER SET latin1 DEFAULT '' NOT NULL, authentication_string TEXT NOT NULL, password_expired ENUM('N', 'Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, PRIMARY KEY Host (Host,User) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Users and global privileges';
+CREATE TABLE IF NOT EXISTS user ( Host char(60) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL, Password char(41) character set latin1 collate latin1_bin DEFAULT '' NOT NULL, Select_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Insert_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Update_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Delete_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Drop_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Reload_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Shutdown_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Process_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, File_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Grant_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, References_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Index_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_db_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Super_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tmp_table_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Lock_tables_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Execute_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_slave_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_client_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_user_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Event_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Trigger_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tablespace_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, ssl_type enum('','ANY','X509', 'SPECIFIED') COLLATE utf8_general_ci DEFAULT '' NOT NULL, ssl_cipher BLOB NOT NULL, x509_issuer BLOB NOT NULL, x509_subject BLOB NOT NULL, max_questions int(11) unsigned DEFAULT 0 NOT NULL, max_updates int(11) unsigned DEFAULT 0 NOT NULL, max_connections int(11) unsigned DEFAULT 0 NOT NULL, max_user_connections int(11) DEFAULT 0 NOT NULL, plugin char(64) CHARACTER SET latin1 DEFAULT '' NOT NULL, authentication_string TEXT NOT NULL, password_expired ENUM('N', 'Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, is_role enum('N', 'Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, PRIMARY KEY Host (Host,User) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Users and global privileges';
-- Remember for later if user table already existed
set @had_user_table= @@warning_count != 0;
+CREATE TABLE IF NOT EXISTS roles_mapping ( Host char(60) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL, Role char(16) binary DEFAULT '' NOT NULL, Admin_option enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, UNIQUE (Host, User, Role)) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Granted roles';
CREATE TABLE IF NOT EXISTS func ( name char(64) binary DEFAULT '' NOT NULL, ret tinyint(1) DEFAULT '0' NOT NULL, dl char(128) DEFAULT '' NOT NULL, type enum ('function','aggregate') COLLATE utf8_general_ci NOT NULL, PRIMARY KEY (name) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='User defined functions';
diff --git a/scripts/mysql_system_tables_data.sql b/scripts/mysql_system_tables_data.sql
index 334c4adacc6..beb99af8e90 100644
--- a/scripts/mysql_system_tables_data.sql
+++ b/scripts/mysql_system_tables_data.sql
@@ -40,10 +40,10 @@ DROP TABLE tmp_db;
-- Fill "user" table with default users allowing root access
-- from local machine if "user" table didn't exist before
CREATE TEMPORARY TABLE tmp_user LIKE user;
-INSERT INTO tmp_user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','','');
-REPLACE INTO tmp_user SELECT @current_hostname,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','','' FROM dual WHERE LOWER( @current_hostname) != 'localhost';
-REPLACE INTO tmp_user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','','');
-REPLACE INTO tmp_user VALUES ('::1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','','');
+INSERT INTO tmp_user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','','N', 'N');
+REPLACE INTO tmp_user SELECT @current_hostname,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','','N','N' FROM dual WHERE LOWER( @current_hostname) != 'localhost';
+REPLACE INTO tmp_user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','','N','N');
+REPLACE INTO tmp_user VALUES ('::1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','','N','N');
INSERT INTO tmp_user (host,user) VALUES ('localhost','');
INSERT INTO tmp_user (host,user) SELECT @current_hostname,'' FROM dual WHERE LOWER(@current_hostname ) != 'localhost';
INSERT INTO user SELECT * FROM tmp_user WHERE @had_user_table=0;
diff --git a/scripts/mysql_system_tables_fix.sql b/scripts/mysql_system_tables_fix.sql
index 24ded350d5f..0a7f08ca914 100644
--- a/scripts/mysql_system_tables_fix.sql
+++ b/scripts/mysql_system_tables_fix.sql
@@ -54,6 +54,11 @@ ADD x509_subject BLOB NOT NULL;
ALTER TABLE user MODIFY ssl_type enum('','ANY','X509', 'SPECIFIED') NOT NULL;
#
+# Add roles to the user table
+ALTER TABLE user
+ADD is_role enum('N', 'Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL;
+
+#
# tables_priv
#
ALTER TABLE tables_priv
@@ -186,7 +191,8 @@ ALTER TABLE user
MODIFY Execute_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL,
MODIFY Repl_slave_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL,
MODIFY Repl_client_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL,
- MODIFY ssl_type enum('','ANY','X509', 'SPECIFIED') COLLATE utf8_general_ci DEFAULT '' NOT NULL;
+ MODIFY ssl_type enum('','ANY','X509', 'SPECIFIED') COLLATE utf8_general_ci DEFAULT '' NOT NULL,
+ MODIFY is_role enum('N', 'Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL;
ALTER TABLE db
MODIFY Host char(60) NOT NULL default '',
diff --git a/sql-common/my_user.c b/sql-common/my_user.c
index 8d717ea7131..a486f77bc1e 100644
--- a/sql-common/my_user.c
+++ b/sql-common/my_user.c
@@ -30,34 +30,40 @@
host_name_str [OUT] Buffer to store host name part.
Must be not less than HOSTNAME_LENGTH + 1.
host_name_len [OUT] A place to store length of the host name part.
+
+ RETURN
+ 0 - if only a user was set, no '@' was found
+ 1 - if both user and host were set
*/
-void parse_user(const char *user_id_str, size_t user_id_len,
- char *user_name_str, size_t *user_name_len,
- char *host_name_str, size_t *host_name_len)
+int parse_user(const char *user_id_str, size_t user_id_len,
+ char *user_name_str, size_t *user_name_len,
+ char *host_name_str, size_t *host_name_len)
{
char *p= strrchr(user_id_str, '@');
if (!p)
{
- *user_name_len= 0;
+ *user_name_len= user_id_len;
*host_name_len= 0;
}
else
{
*user_name_len= (uint) (p - user_id_str);
*host_name_len= (uint) (user_id_len - *user_name_len - 1);
+ }
- if (*user_name_len > USERNAME_LENGTH)
- *user_name_len= USERNAME_LENGTH;
+ if (*user_name_len > USERNAME_LENGTH)
+ *user_name_len= USERNAME_LENGTH;
- if (*host_name_len > HOSTNAME_LENGTH)
- *host_name_len= HOSTNAME_LENGTH;
+ if (*host_name_len > HOSTNAME_LENGTH)
+ *host_name_len= HOSTNAME_LENGTH;
- memcpy(user_name_str, user_id_str, *user_name_len);
- memcpy(host_name_str, p + 1, *host_name_len);
- }
+ memcpy(user_name_str, user_id_str, *user_name_len);
+ memcpy(host_name_str, p + 1, *host_name_len);
user_name_str[*user_name_len]= 0;
host_name_str[*host_name_len]= 0;
+
+ return p != NULL;
}
diff --git a/sql/handler.h b/sql/handler.h
index a7f00f55076..ee961010b2c 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -712,12 +712,15 @@ struct TABLE;
*/
enum enum_schema_tables
{
- SCH_CHARSETS= 0,
+ SCH_ALL_PLUGINS,
+ SCH_APPLICABLE_ROLES,
+ SCH_CHARSETS,
SCH_CLIENT_STATS,
SCH_COLLATIONS,
SCH_COLLATION_CHARACTER_SET_APPLICABILITY,
SCH_COLUMNS,
SCH_COLUMN_PRIVILEGES,
+ SCH_ENABLED_ROLES,
SCH_ENGINES,
SCH_EVENTS,
SCH_EXPLAIN,
@@ -731,7 +734,6 @@ enum enum_schema_tables
SCH_PARAMETERS,
SCH_PARTITIONS,
SCH_PLUGINS,
- SCH_ALL_PLUGINS,
SCH_PROCESSLIST,
SCH_PROFILES,
SCH_REFERENTIAL_CONSTRAINTS,
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index ec1c795c100..4c83334ccd9 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -2515,16 +2515,32 @@ bool Item_func_current_user::fix_fields(THD *thd, Item **ref)
if (Item_func_sysconst::fix_fields(thd, ref))
return TRUE;
- Security_context *ctx=
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- (context->security_ctx
- ? context->security_ctx : thd->security_ctx);
-#else
- thd->security_ctx;
-#endif /*NO_EMBEDDED_ACCESS_CHECKS*/
+ Security_context *ctx= context->security_ctx
+ ? context->security_ctx : thd->security_ctx;
return init(ctx->priv_user, ctx->priv_host);
}
+bool Item_func_current_role::fix_fields(THD *thd, Item **ref)
+{
+ if (Item_func_sysconst::fix_fields(thd, ref))
+ return 1;
+
+ Security_context *ctx= context->security_ctx
+ ? context->security_ctx : thd->security_ctx;
+
+ if (ctx->priv_role[0])
+ {
+ if (str_value.copy(ctx->priv_role, strlen(ctx->priv_role),
+ system_charset_info))
+ return 1;
+
+ str_value.mark_as_const();
+ return 0;
+ }
+ null_value= maybe_null= 1;
+ return 0;
+}
+
void Item_func_soundex::fix_length_and_dec()
{
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
index 6bce7da7d52..6709b4b64c6 100644
--- a/sql/item_strfunc.h
+++ b/sql/item_strfunc.h
@@ -607,6 +607,28 @@ public:
};
+class Item_func_current_role :public Item_func_sysconst
+{
+ Name_resolution_context *context;
+
+public:
+ Item_func_current_role(Name_resolution_context *context_arg)
+ : context(context_arg) {}
+ bool fix_fields(THD *thd, Item **ref);
+ void fix_length_and_dec()
+ { max_length= username_char_length * SYSTEM_CHARSET_MBMAXLEN; }
+ int save_in_field(Field *field, bool no_conversions)
+ { return save_str_value_in_field(field, &str_value); }
+ const char *func_name() const { return "current_role"; }
+ const char *fully_qualified_func_name() const { return "current_role()"; }
+ String *val_str(String *)
+ {
+ DBUG_ASSERT(fixed == 1);
+ return (null_value ? 0 : &str_value);
+ }
+};
+
+
class Item_func_soundex :public Item_str_func
{
String tmp_value;
diff --git a/sql/lex.h b/sql/lex.h
index 62a579944bb..88d73b9b169 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -59,6 +59,7 @@ static SYMBOL symbols[] = {
{ "ACCESSIBLE", SYM(ACCESSIBLE_SYM)},
{ "ACTION", SYM(ACTION)},
{ "ADD", SYM(ADD)},
+ { "ADMIN", SYM(ADMIN_SYM)},
{ "AFTER", SYM(AFTER_SYM)},
{ "AGAINST", SYM(AGAINST)},
{ "AGGREGATE", SYM(AGGREGATE_SYM)},
@@ -154,6 +155,7 @@ static SYMBOL symbols[] = {
{ "CURRENT", SYM(CURRENT_SYM)},
{ "CURRENT_DATE", SYM(CURDATE)},
{ "CURRENT_POS", SYM(CURRENT_POS_SYM)},
+ { "CURRENT_ROLE", SYM(CURRENT_ROLE)},
{ "CURRENT_TIME", SYM(CURTIME)},
{ "CURRENT_TIMESTAMP", SYM(NOW_SYM)},
{ "CURRENT_USER", SYM(CURRENT_USER)},
@@ -498,6 +500,7 @@ static SYMBOL symbols[] = {
{ "REVOKE", SYM(REVOKE)},
{ "RIGHT", SYM(RIGHT)},
{ "RLIKE", SYM(REGEXP)}, /* Like in mSQL2 */
+ { "ROLE", SYM(ROLE_SYM)},
{ "ROLLBACK", SYM(ROLLBACK_SYM)},
{ "ROLLUP", SYM(ROLLUP_SYM)},
{ "ROUTINE", SYM(ROUTINE_SYM)},
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 6f75e3b9dba..d042e4e588e 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -2898,17 +2898,22 @@ bool Query_log_event::write(IO_CACHE* file)
user= thd->get_invoker_user();
host= thd->get_invoker_host();
}
- else if (thd->security_ctx->priv_user)
+ else
{
Security_context *ctx= thd->security_ctx;
- user.length= strlen(ctx->priv_user);
- user.str= ctx->priv_user;
- if (ctx->priv_host[0] != '\0')
+ if (thd->need_binlog_invoker() == THD::INVOKER_USER)
{
+ user.str= ctx->priv_user;
host.str= ctx->priv_host;
- host.length= strlen(ctx->priv_host);
+ host.length= strlen(host.str);
+ }
+ else
+ {
+ user.str= ctx->priv_role;
+ host= empty_lex_str;
}
+ user.length= strlen(user.str);
}
if (user.length > 0)
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index b12f60a031b..c397da69037 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -2825,7 +2825,7 @@ bool one_thread_per_connection_end(THD *thd, bool put_in_cache)
mysql_mutex_unlock(&LOCK_thread_count);
}
DBUG_LEAVE; // Must match DBUG_ENTER()
-#ifndef EMBEDDED_LIBRARY
+#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
ERR_remove_state(0);
#endif
my_thread_end();
@@ -3561,6 +3561,7 @@ SHOW_VAR com_status_vars[]= {
{"create_function", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_SPFUNCTION]), SHOW_LONG_STATUS},
{"create_index", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_INDEX]), SHOW_LONG_STATUS},
{"create_procedure", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_PROCEDURE]), SHOW_LONG_STATUS},
+ {"create_role", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_ROLE]), SHOW_LONG_STATUS},
{"create_server", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_SERVER]), SHOW_LONG_STATUS},
{"create_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_TABLE]), SHOW_LONG_STATUS},
{"create_trigger", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_TRIGGER]), SHOW_LONG_STATUS},
@@ -3576,6 +3577,7 @@ SHOW_VAR com_status_vars[]= {
{"drop_function", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_FUNCTION]), SHOW_LONG_STATUS},
{"drop_index", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_INDEX]), SHOW_LONG_STATUS},
{"drop_procedure", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_PROCEDURE]), SHOW_LONG_STATUS},
+ {"drop_role", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_ROLE]), SHOW_LONG_STATUS},
{"drop_server", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_SERVER]), SHOW_LONG_STATUS},
{"drop_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_TABLE]), SHOW_LONG_STATUS},
{"drop_trigger", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_TRIGGER]), SHOW_LONG_STATUS},
@@ -3586,6 +3588,7 @@ SHOW_VAR com_status_vars[]= {
{"flush", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_FLUSH]), SHOW_LONG_STATUS},
{"get_diagnostics", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_GET_DIAGNOSTICS]), SHOW_LONG_STATUS},
{"grant", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_GRANT]), SHOW_LONG_STATUS},
+ {"grant_role", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_GRANT_ROLE]), SHOW_LONG_STATUS},
{"ha_close", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_HA_CLOSE]), SHOW_LONG_STATUS},
{"ha_open", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_HA_OPEN]), SHOW_LONG_STATUS},
{"ha_read", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_HA_READ]), SHOW_LONG_STATUS},
@@ -3611,6 +3614,7 @@ SHOW_VAR com_status_vars[]= {
{"resignal", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_RESIGNAL]), SHOW_LONG_STATUS},
{"revoke", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_REVOKE]), SHOW_LONG_STATUS},
{"revoke_all", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_REVOKE_ALL]), SHOW_LONG_STATUS},
+ {"revoke_role", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_REVOKE_ROLE]), SHOW_LONG_STATUS},
{"rollback", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ROLLBACK]), SHOW_LONG_STATUS},
{"rollback_to_savepoint",(char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ROLLBACK_TO_SAVEPOINT]), SHOW_LONG_STATUS},
{"savepoint", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SAVEPOINT]), SHOW_LONG_STATUS},
@@ -7567,6 +7571,42 @@ static int show_default_keycache(THD *thd, SHOW_VAR *var, char *buff)
return 0;
}
+#ifndef DBUG_OFF
+static int debug_status_func(THD *thd, SHOW_VAR *var, char *buff)
+{
+#define add_var(X,Y,Z) \
+ v->name= X; \
+ v->value= (char*)Y; \
+ v->type= Z; \
+ v++;
+
+ var->type= SHOW_ARRAY;
+ var->value= buff;
+
+ SHOW_VAR *v= (SHOW_VAR *)buff;
+
+ if (_db_keyword_(0, "role_merge_stats", 1))
+ {
+ static SHOW_VAR roles[]= {
+ {"global", (char*) &role_global_merges, SHOW_ULONG},
+ {"db", (char*) &role_db_merges, SHOW_ULONG},
+ {"table", (char*) &role_table_merges, SHOW_ULONG},
+ {"column", (char*) &role_column_merges, SHOW_ULONG},
+ {"routine", (char*) &role_routine_merges, SHOW_ULONG},
+ {NullS, NullS, SHOW_LONG}
+ };
+
+ add_var("role_merges", roles, SHOW_ARRAY);
+ }
+
+ v->name= 0;
+
+#undef add_var
+
+ return 0;
+}
+#endif
+
#ifdef HAVE_POOL_OF_THREADS
int show_threadpool_idle_threads(THD *thd, SHOW_VAR *var, char *buff)
{
@@ -7606,6 +7646,9 @@ SHOW_VAR status_vars[]= {
{"Created_tmp_disk_tables", (char*) offsetof(STATUS_VAR, created_tmp_disk_tables_), SHOW_LONG_STATUS},
{"Created_tmp_files", (char*) &my_tmp_file_created, SHOW_LONG},
{"Created_tmp_tables", (char*) offsetof(STATUS_VAR, created_tmp_tables_), SHOW_LONG_STATUS},
+#ifndef DBUG_OFF
+ {"Debug", (char*) &debug_status_func, SHOW_FUNC},
+#endif
{"Delayed_errors", (char*) &delayed_insert_errors, SHOW_LONG},
{"Delayed_insert_threads", (char*) &delayed_insert_threads, SHOW_LONG_NOFLUSH},
{"Delayed_writes", (char*) &delayed_insert_writes, SHOW_LONG},
diff --git a/sql/set_var.cc b/sql/set_var.cc
index db74d8f0d9d..8ae29e01a20 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -829,26 +829,7 @@ int set_var_user::update(THD *thd)
int set_var_password::check(THD *thd)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (!user->host.str)
- {
- DBUG_ASSERT(thd->security_ctx->priv_host);
- if (*thd->security_ctx->priv_host != 0)
- {
- user->host.str= (char *) thd->security_ctx->priv_host;
- user->host.length= strlen(thd->security_ctx->priv_host);
- }
- else
- {
- user->host.str= (char *)"%";
- user->host.length= 1;
- }
- }
- if (!user->user.str)
- {
- DBUG_ASSERT(thd->security_ctx->user);
- user->user.str= (char *) thd->security_ctx->user;
- user->user.length= strlen(thd->security_ctx->user);
- }
+ user= get_current_user(thd, user);
/* Returns 1 as the function sends error to client */
return check_change_password(thd, user->host.str, user->user.str,
password, strlen(password)) ? 1 : 0;
@@ -869,6 +850,31 @@ int set_var_password::update(THD *thd)
}
/*****************************************************************************
+ Functions to handle SET ROLE
+*****************************************************************************/
+int set_var_role::check(THD *thd)
+{
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ ulonglong access;
+ int status= acl_check_setrole(thd, base.str, &access);
+ save_result.ulonglong_value= access;
+ return status;
+#else
+ return 0;
+#endif
+}
+
+int set_var_role::update(THD *thd)
+{
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ return acl_setrole(thd, base.str, save_result.ulonglong_value);
+#else
+ return 0;
+#endif
+}
+
+
+/*****************************************************************************
Functions to handle SET NAMES and SET CHARACTER SET
*****************************************************************************/
diff --git a/sql/set_var.h b/sql/set_var.h
index f248dc2894f..11501c4212a 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -276,6 +276,17 @@ public:
int update(THD *thd);
};
+/* For SET ROLE */
+
+class set_var_role: public set_var
+{
+public:
+ set_var_role(LEX_STRING role_arg) :
+ set_var(OPT_SESSION, NULL, &role_arg, NULL){};
+ int check(THD *thd);
+ int update(THD *thd);
+};
+
/* For SET NAMES and SET CHARACTER SET */
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index 05693693bc9..1d27156f00e 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -2998,7 +2998,7 @@ ER_PASSWORD_NOT_ALLOWED 42000
spa "Tu debes de tener permiso para actualizar tablas en la base de datos mysql para cambiar las claves para otros"
swe "För att ändra lösenord för andra måste du ha rättigheter att uppdatera mysql-databasen"
ukr "Ви повині мати право на Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†ÑŒ у базі данних mysql, аби мати можливіÑÑ‚ÑŒ змінювати пароль іншим"
-ER_PASSWORD_NO_MATCH 42000
+ER_PASSWORD_NO_MATCH 28000
cze "V tabulce user není žádný odpovídající řádek"
dan "Kan ikke finde nogen tilsvarende poster i bruger tabellen"
nla "Kan geen enkele passende rij vinden in de gebruikers tabel"
@@ -5558,9 +5558,8 @@ ER_PS_NO_RECURSION
ER_SP_CANT_SET_AUTOCOMMIT
eng "Not allowed to set autocommit from a stored function or trigger"
ger "Es ist nicht erlaubt, innerhalb einer gespeicherten Funktion oder eines Triggers AUTOCOMMIT zu setzen"
-ER_MALFORMED_DEFINER
- eng "Definer is not fully qualified"
- ger "Definierer des View ist nicht vollständig spezifiziert"
+ER_MALFORMED_DEFINER 0L000
+ eng "Invalid definer"
ER_VIEW_FRM_NO_USER
eng "View '%-.192s'.'%-.192s' has no definer information (old table format). Current user is used as definer. Please recreate the view!"
ger "View '%-.192s'.'%-.192s' hat keine Definierer-Information (altes Tabellenformat). Der aktuelle Benutzer wird als Definierer verwendet. Bitte erstellen Sie den View neu"
@@ -7060,3 +7059,15 @@ ER_NO_SUCH_QUERY
rus "ÐеизвеÑтный номер запроÑа: %lld"
ER_BAD_BASE64_DATA
eng "Bad base64 data as position %u"
+ER_INVALID_ROLE OP000
+ eng "Invalid role specification %`s."
+ rum "Rolul %`s este invalid."
+ER_INVALID_CURRENT_USER 0L000
+ eng "The current user is invalid."
+ rum "Utilizatorul curent este invalid."
+ER_CANNOT_GRANT_ROLE
+ eng "Cannot grant role '%s' to: %s."
+ rum "Rolul '%s' nu poate fi acordat catre: %s."
+ER_CANNOT_REVOKE_ROLE
+ eng "Cannot revoke role '%s' from: %s."
+ rum "Rolul '%s' nu poate fi revocat de la: %s."
diff --git a/sql/slave.cc b/sql/slave.cc
index 52a8250e23f..c0608ad5cb7 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -3987,7 +3987,9 @@ err_during_init:
DBUG_LEAVE; // Must match DBUG_ENTER()
my_thread_end();
+#ifdef HAVE_OPENSSL
ERR_remove_state(0);
+#endif
pthread_exit(0);
return 0; // Avoid compiler warnings
}
@@ -4476,7 +4478,9 @@ err_during_init:
DBUG_LEAVE; // Must match DBUG_ENTER()
my_thread_end();
+#ifdef HAVE_OPENSSL
ERR_remove_state(0);
+#endif
pthread_exit(0);
return 0; // Avoid compiler warnings
}
diff --git a/sql/sp.cc b/sql/sp.cc
index c1c162267a8..1b8bc9b36ae 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -50,7 +50,8 @@ db_load_routine(THD *thd, stored_procedure_type type, sp_name *name,
sp_head **sphp,
ulonglong sql_mode, const char *params, const char *returns,
const char *body, st_sp_chistics &chistics,
- const char *definer, longlong created, longlong modified,
+ LEX_STRING *definer_user_name, LEX_STRING *definer_host_name,
+ longlong created, longlong modified,
Stored_program_creation_ctx *creation_ctx);
static const
@@ -548,6 +549,10 @@ db_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
ulonglong sql_mode, saved_mode= thd->variables.sql_mode;
Open_tables_backup open_tables_state_backup;
Stored_program_creation_ctx *creation_ctx;
+ char definer_user_name_holder[USERNAME_LENGTH + 1];
+ LEX_STRING definer_user_name= { definer_user_name_holder, USERNAME_LENGTH };
+ char definer_host_name_holder[HOSTNAME_LENGTH + 1];
+ LEX_STRING definer_host_name= { definer_host_name_holder, HOSTNAME_LENGTH };
DBUG_ENTER("db_find_routine");
DBUG_PRINT("enter", ("type: %d name: %.*s",
@@ -657,9 +662,19 @@ db_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
close_system_tables(thd, &open_tables_state_backup);
table= 0;
+ if (parse_user(definer, strlen(definer),
+ definer_user_name.str, &definer_user_name.length,
+ definer_host_name.str, &definer_host_name.length) &&
+ definer_user_name.length && !definer_host_name.length)
+ {
+ // 'user@' -> 'user@%'
+ definer_host_name= host_not_specified;
+ }
+
ret= db_load_routine(thd, type, name, sphp,
sql_mode, params, returns, body, chistics,
- definer, created, modified, creation_ctx);
+ &definer_user_name, &definer_host_name,
+ created, modified, creation_ctx);
done:
/*
Restore the time zone flag as the timezone usage in proc table
@@ -804,7 +819,8 @@ db_load_routine(THD *thd, stored_procedure_type type,
sp_name *name, sp_head **sphp,
ulonglong sql_mode, const char *params, const char *returns,
const char *body, st_sp_chistics &chistics,
- const char *definer, longlong created, longlong modified,
+ LEX_STRING *definer_user_name, LEX_STRING *definer_host_name,
+ longlong created, longlong modified,
Stored_program_creation_ctx *creation_ctx)
{
LEX *old_lex= thd->lex, newlex;
@@ -814,22 +830,12 @@ db_load_routine(THD *thd, stored_procedure_type type,
{ saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) };
bool cur_db_changed;
Bad_db_error_handler db_not_exists_handler;
- char definer_user_name_holder[USERNAME_LENGTH + 1];
- LEX_STRING definer_user_name= { definer_user_name_holder,
- USERNAME_LENGTH };
-
- char definer_host_name_holder[HOSTNAME_LENGTH + 1];
- LEX_STRING definer_host_name= { definer_host_name_holder, HOSTNAME_LENGTH };
int ret= 0;
thd->lex= &newlex;
newlex.current_select= NULL;
- parse_user(definer, strlen(definer),
- definer_user_name.str, &definer_user_name.length,
- definer_host_name.str, &definer_host_name.length);
-
defstr.set_charset(creation_ctx->get_client_cs());
/*
@@ -845,7 +851,7 @@ db_load_routine(THD *thd, stored_procedure_type type,
params, strlen(params),
returns, strlen(returns),
body, strlen(body),
- &chistics, &definer_user_name, &definer_host_name,
+ &chistics, definer_user_name, definer_host_name,
sql_mode))
{
ret= SP_INTERNAL_ERROR;
@@ -895,7 +901,7 @@ db_load_routine(THD *thd, stored_procedure_type type,
goto end;
}
- (*sphp)->set_definer(&definer_user_name, &definer_host_name);
+ (*sphp)->set_definer(definer_user_name, definer_host_name);
(*sphp)->set_info(created, modified, &chistics, sql_mode);
(*sphp)->set_creation_ctx(creation_ctx);
(*sphp)->optimize();
@@ -974,7 +980,8 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
{
int ret;
TABLE *table;
- char definer[USER_HOST_BUFF_SIZE];
+ char definer_buf[USER_HOST_BUFF_SIZE];
+ LEX_STRING definer;
ulonglong saved_mode= thd->variables.sql_mode;
MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ?
MDL_key::FUNCTION : MDL_key::PROCEDURE;
@@ -1011,8 +1018,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
restore_record(table, s->default_values); // Get default values for fields
/* NOTE: all needed privilege checks have been already done. */
- strxnmov(definer, sizeof(definer)-1, thd->lex->definer->user.str, "@",
- thd->lex->definer->host.str, NullS);
+ thd->lex->definer->set_lex_string(&definer, definer_buf);
if (table->s->fields < MYSQL_PROC_FIELD_COUNT)
{
@@ -1087,7 +1093,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp)
store_failed= store_failed ||
table->field[MYSQL_PROC_FIELD_DEFINER]->
- store(definer, (uint)strlen(definer), system_charset_info);
+ store(definer.str, definer.length, system_charset_info);
((Field_timestamp *)table->field[MYSQL_PROC_FIELD_CREATED])->set_time();
((Field_timestamp *)table->field[MYSQL_PROC_FIELD_MODIFIED])->set_time();
@@ -1648,7 +1654,6 @@ sp_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
ulong level;
sp_head *new_sp;
const char *returns= "";
- char definer[USER_HOST_BUFF_SIZE];
/*
String buffer for RETURNS data type must have system charset;
@@ -1685,8 +1690,6 @@ sp_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
DBUG_RETURN(0);
}
- strxmov(definer, sp->m_definer_user.str, "@",
- sp->m_definer_host.str, NullS);
if (type == TYPE_ENUM_FUNCTION)
{
sp_returns_type(thd, retstr, sp);
@@ -1694,7 +1697,8 @@ sp_find_routine(THD *thd, stored_procedure_type type, sp_name *name,
}
if (db_load_routine(thd, type, name, &new_sp,
sp->m_sql_mode, sp->m_params.str, returns,
- sp->m_body.str, *sp->m_chistics, definer,
+ sp->m_body.str, *sp->m_chistics,
+ &sp->m_definer_user, &sp->m_definer_host,
sp->m_created, sp->m_modified,
sp->get_creation_ctx()) == SP_OK)
{
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 9e7d908f8a3..4bf6c13a7d8 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -282,9 +282,12 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_CREATE_VIEW:
case SQLCOM_CREATE_TRIGGER:
case SQLCOM_CREATE_USER:
+ case SQLCOM_CREATE_ROLE:
case SQLCOM_ALTER_TABLE:
case SQLCOM_GRANT:
+ case SQLCOM_GRANT_ROLE:
case SQLCOM_REVOKE:
+ case SQLCOM_REVOKE_ROLE:
case SQLCOM_BEGIN:
case SQLCOM_RENAME_TABLE:
case SQLCOM_RENAME_USER:
@@ -292,6 +295,7 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_DROP_DB:
case SQLCOM_REVOKE_ALL:
case SQLCOM_DROP_USER:
+ case SQLCOM_DROP_ROLE:
case SQLCOM_DROP_VIEW:
case SQLCOM_DROP_TRIGGER:
case SQLCOM_TRUNCATE:
@@ -2468,8 +2472,13 @@ sp_head::set_definer(const char *definer, uint definerlen)
char host_name_holder[HOSTNAME_LENGTH + 1];
LEX_STRING host_name= { host_name_holder, HOSTNAME_LENGTH };
- parse_user(definer, definerlen, user_name.str, &user_name.length,
- host_name.str, &host_name.length);
+ if (parse_user(definer, definerlen, user_name.str, &user_name.length,
+ host_name.str, &host_name.length) &&
+ user_name.length && !host_name.length)
+ {
+ // 'user@' -> 'user@%'
+ host_name= host_not_specified;
+ }
set_definer(&user_name, &host_name);
}
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 3e7564a1f25..1643161d5d6 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -44,13 +44,13 @@
#include "sp.h"
#include "transaction.h"
#include "lock.h" // MYSQL_LOCK_IGNORE_TIMEOUT
-#include "records.h" // init_read_record, end_read_record
#include <sql_common.h>
#include <mysql/plugin_auth.h>
#include "sql_connect.h"
#include "hostname.h"
#include "sql_db.h"
#include "sql_array.h"
+#include "sql_hset.h"
#include "sql_plugin_compat.h"
@@ -59,15 +59,15 @@ bool mysql_user_table_is_in_short_password_format= false;
static const
TABLE_FIELD_TYPE mysql_db_table_fields[MYSQL_DB_FIELD_COUNT] = {
{
- { C_STRING_WITH_LEN("Host") },
+ { C_STRING_WITH_LEN("Host") },
{ C_STRING_WITH_LEN("char(60)") },
{NULL, 0}
- },
+ },
{
- { C_STRING_WITH_LEN("Db") },
+ { C_STRING_WITH_LEN("Db") },
{ C_STRING_WITH_LEN("char(64)") },
{NULL, 0}
- },
+ },
{
{ C_STRING_WITH_LEN("User") },
{ C_STRING_WITH_LEN("char(") },
@@ -176,19 +176,40 @@ mysql_db_table_def= {MYSQL_DB_FIELD_COUNT, mysql_db_table_fields, 0, (uint*) 0 }
static LEX_STRING native_password_plugin_name= {
C_STRING_WITH_LEN("mysql_native_password")
};
-
+
static LEX_STRING old_password_plugin_name= {
C_STRING_WITH_LEN("mysql_old_password")
};
-
+
/// @todo make it configurable
LEX_STRING *default_auth_plugin_name= &native_password_plugin_name;
+/*
+ Wildcard host, matches any hostname
+*/
+LEX_STRING host_not_specified= { C_STRING_WITH_LEN("%") };
+
+/*
+ Constants, used in the SHOW GRANTS command.
+ Their actual string values are irrelevant, they're always compared
+ as pointers to these string constants.
+*/
+LEX_STRING current_user= { C_STRING_WITH_LEN("*current_user") };
+LEX_STRING current_role= { C_STRING_WITH_LEN("*current_role") };
+LEX_STRING current_user_and_current_role= { C_STRING_WITH_LEN("*current_user_and_current_role") };
+
+
#ifndef NO_EMBEDDED_ACCESS_CHECKS
static plugin_ref old_password_plugin;
#endif
static plugin_ref native_password_plugin;
+static char *safe_str(char *str)
+{ return str ? str : const_cast<char*>(""); }
+
+static const char *safe_str(const char *str)
+{ return str ? str : ""; }
+
/* Classes */
struct acl_host_and_ip
@@ -197,6 +218,12 @@ struct acl_host_and_ip
long ip, ip_mask; // Used with masked ip:s
};
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+static bool compare_hostname(const acl_host_and_ip *, const char *, const char *);
+#else
+#define compare_hostname(X,Y,Z) 0
+#endif
+
class ACL_ACCESS {
public:
ulong sort;
@@ -212,15 +239,27 @@ public:
char *db;
};
-class ACL_USER :public ACL_ACCESS
+class ACL_USER_BASE :public ACL_ACCESS
+{
+
+public:
+ static void *operator new(size_t size, MEM_ROOT *mem_root)
+ { return (void*) alloc_root(mem_root, size); }
+
+ uchar flags; // field used to store various state information
+ LEX_STRING user;
+ /* list to hold references to granted roles (ACL_ROLE instances) */
+ DYNAMIC_ARRAY role_grants;
+};
+
+class ACL_USER :public ACL_USER_BASE
{
public:
acl_host_and_ip host;
uint hostname_length;
USER_RESOURCES user_resource;
- char *user;
uint8 salt[SCRAMBLE_LENGTH + 1]; // scrambled password in binary form
- uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 4.0, 20 - 4.1.1
+ uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 4.0, 20 - 4.1.1
enum SSL_type ssl_type;
const char *ssl_cipher, *x509_issuer, *x509_subject;
LEX_STRING plugin;
@@ -232,7 +271,8 @@ public:
if (!dst)
return 0;
*dst= *this;
- dst->user= safe_strdup_root(root, user);
+ dst->user.str= safe_strdup_root(root, user.str);
+ dst->user.length= user.length;
dst->ssl_cipher= safe_strdup_root(root, ssl_cipher);
dst->x509_issuer= safe_strdup_root(root, x509_issuer);
dst->x509_subject= safe_strdup_root(root, x509_subject);
@@ -243,8 +283,55 @@ public:
dst->plugin.str= strmake_root(root, plugin.str, plugin.length);
dst->auth_string.str= safe_strdup_root(root, auth_string.str);
dst->host.hostname= safe_strdup_root(root, host.hostname);
+ bzero(&dst->role_grants, sizeof(role_grants));
return dst;
}
+
+ int cmp(const char *user2, const char *host2)
+ {
+ CHARSET_INFO *cs= system_charset_info;
+ int res;
+ res= strcmp(safe_str(user.str), safe_str(user2));
+ if (!res)
+ res= my_strcasecmp(cs, host.hostname, host2);
+ return res;
+ }
+
+ bool eq(const char *user2, const char *host2) { return !cmp(user2, host2); }
+
+ bool wild_eq(const char *user2, const char *host2, const char *ip2 = 0)
+ {
+ if (strcmp(safe_str(user.str), safe_str(user2)))
+ return false;
+
+ return compare_hostname(&host, host2, ip2 ? ip2 : host2);
+ }
+};
+
+class ACL_ROLE :public ACL_USER_BASE
+{
+public:
+ /*
+ In case of granting a role to a role, the access bits are merged together
+ via a bit OR operation and placed in the ACL_USER::access field.
+
+ When rebuilding role_grants via the rebuild_role_grant function,
+ the ACL_USER::access field needs to be reset first. The field
+ initial_role_access holds initial grants, as granted directly to the role
+ */
+ ulong initial_role_access;
+ /*
+ In subgraph traversal, when we need to traverse only a part of the graph
+ (e.g. all direct and indirect grantees of a role X), the counter holds the
+ number of affected neighbour nodes.
+ See also propagate_role_grants()
+ */
+ uint counter;
+ DYNAMIC_ARRAY parent_grantee; // array of backlinks to elements granted
+
+ ACL_ROLE(ACL_USER * user, MEM_ROOT *mem);
+ ACL_ROLE(const char * rolename, ulong privileges, MEM_ROOT *mem);
+
};
class ACL_DB :public ACL_ACCESS
@@ -252,16 +339,30 @@ class ACL_DB :public ACL_ACCESS
public:
acl_host_and_ip host;
char *user,*db;
+ ulong initial_access; /* access bits present in the table */
};
+#ifndef DBUG_OFF
+/* status variables, only visible in SHOW STATUS after -#d,role_merge_stats */
+ulong role_global_merges= 0, role_db_merges= 0, role_table_merges= 0,
+ role_column_merges= 0, role_routine_merges= 0;
+#endif
#ifndef NO_EMBEDDED_ACCESS_CHECKS
static void update_hostname(acl_host_and_ip *host, const char *hostname);
static ulong get_sort(uint count,...);
-static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
- const char *ip);
-static bool show_proxy_grants (THD *thd, LEX_USER *user,
- char *buff, size_t buffsize);
+static bool show_proxy_grants (THD *, const char *, const char *,
+ char *, size_t);
+static bool show_role_grants(THD *, const char *, const char *,
+ ACL_USER_BASE *, char *, size_t);
+static bool show_global_privileges(THD *, ACL_USER_BASE *,
+ bool, char *, size_t);
+static bool show_database_privileges(THD *, const char *, const char *,
+ char *, size_t);
+static bool show_table_and_column_privileges(THD *, const char *, const char *,
+ char *, size_t);
+static int show_routine_grants(THD *, const char *, const char *, HASH *,
+ const char *, int, char *, int);
class ACL_PROXY_USER :public ACL_ACCESS
{
@@ -271,11 +372,11 @@ class ACL_PROXY_USER :public ACL_ACCESS
const char *proxied_user;
bool with_grant;
- typedef enum {
- MYSQL_PROXIES_PRIV_HOST,
- MYSQL_PROXIES_PRIV_USER,
+ typedef enum {
+ MYSQL_PROXIES_PRIV_HOST,
+ MYSQL_PROXIES_PRIV_USER,
MYSQL_PROXIES_PRIV_PROXIED_HOST,
- MYSQL_PROXIES_PRIV_PROXIED_USER,
+ MYSQL_PROXIES_PRIV_PROXIED_USER,
MYSQL_PROXIES_PRIV_WITH_GRANT,
MYSQL_PROXIES_PRIV_GRANTOR,
MYSQL_PROXIES_PRIV_TIMESTAMP } old_acl_proxy_users;
@@ -287,16 +388,14 @@ public:
bool with_grant_arg)
{
user= (user_arg && *user_arg) ? user_arg : NULL;
- update_hostname (&host,
- (host_arg && *host_arg) ? host_arg : NULL);
- proxied_user= (proxied_user_arg && *proxied_user_arg) ?
+ update_hostname (&host, (host_arg && *host_arg) ? host_arg : NULL);
+ proxied_user= (proxied_user_arg && *proxied_user_arg) ?
proxied_user_arg : NULL;
- update_hostname (&proxied_host,
+ update_hostname (&proxied_host,
(proxied_host_arg && *proxied_host_arg) ?
proxied_host_arg : NULL);
with_grant= with_grant_arg;
- sort= get_sort(4, host.hostname, user,
- proxied_host.hostname, proxied_user);
+ sort= get_sort(4, host.hostname, user, proxied_host.hostname, proxied_user);
}
void init(MEM_ROOT *mem, const char *host_arg, const char *user_arg,
@@ -305,9 +404,9 @@ public:
{
init ((host_arg && *host_arg) ? strdup_root (mem, host_arg) : NULL,
(user_arg && *user_arg) ? strdup_root (mem, user_arg) : NULL,
- (proxied_host_arg && *proxied_host_arg) ?
+ (proxied_host_arg && *proxied_host_arg) ?
strdup_root (mem, proxied_host_arg) : NULL,
- (proxied_user_arg && *proxied_user_arg) ?
+ (proxied_user_arg && *proxied_user_arg) ?
strdup_root (mem, proxied_user_arg) : NULL,
with_grant_arg);
}
@@ -326,29 +425,27 @@ public:
const char *get_host() { return host.hostname; }
const char *get_proxied_user() { return proxied_user; }
const char *get_proxied_host() { return proxied_host.hostname; }
- void set_user(MEM_ROOT *mem, const char *user_arg)
- {
+ void set_user(MEM_ROOT *mem, const char *user_arg)
+ {
user= user_arg && *user_arg ? strdup_root(mem, user_arg) : NULL;
}
- void set_host(MEM_ROOT *mem, const char *host_arg)
- {
- update_hostname(&host,
- (host_arg && *host_arg) ?
- strdup_root(mem, host_arg) : NULL);
+ void set_host(MEM_ROOT *mem, const char *host_arg)
+ {
+ update_hostname(&host, safe_strdup_root(mem, host_arg));
}
bool check_validity(bool check_no_resolve)
{
- if (check_no_resolve &&
+ if (check_no_resolve &&
(hostname_requires_resolving(host.hostname) ||
hostname_requires_resolving(proxied_host.hostname)))
{
sql_print_warning("'proxies_priv' entry '%s@%s %s@%s' "
"ignored in --skip-name-resolve mode.",
- proxied_user ? proxied_user : "",
- proxied_host.hostname ? proxied_host.hostname : "",
- user ? user : "",
- host.hostname ? host.hostname : "");
+ safe_str(proxied_user),
+ safe_str(proxied_host.hostname),
+ safe_str(user),
+ safe_str(host.hostname));
return TRUE;
}
return FALSE;
@@ -362,22 +459,15 @@ public:
"compare_hostname(%s,%s,%s) &&"
"wild_compare (%s,%s) &&"
"wild_compare (%s,%s)",
- host.hostname ? host.hostname : "<NULL>",
- host_arg ? host_arg : "<NULL>",
- ip_arg ? ip_arg : "<NULL>",
- proxied_host.hostname ? proxied_host.hostname : "<NULL>",
- host_arg ? host_arg : "<NULL>",
- ip_arg ? ip_arg : "<NULL>",
- user_arg ? user_arg : "<NULL>",
- user ? user : "<NULL>",
- proxied_user_arg ? proxied_user_arg : "<NULL>",
- proxied_user ? proxied_user : "<NULL>"));
+ host.hostname, host_arg, ip_arg, proxied_host.hostname,
+ host_arg, ip_arg, user_arg, user,
+ proxied_user_arg, proxied_user));
DBUG_RETURN(compare_hostname(&host, host_arg, ip_arg) &&
compare_hostname(&proxied_host, host_arg, ip_arg) &&
(!user ||
(user_arg && !wild_compare(user_arg, user, TRUE))) &&
- (!proxied_user ||
- (proxied_user && !wild_compare(proxied_user_arg,
+ (!proxied_user ||
+ (proxied_user && !wild_compare(proxied_user_arg,
proxied_user, TRUE))));
}
@@ -395,21 +485,16 @@ public:
"strcmp(%s,%s) &&"
"wild_compare (%s,%s) &&"
"wild_compare (%s,%s)",
- user ? user : "<NULL>",
- grant->user ? grant->user : "<NULL>",
- proxied_user ? proxied_user : "<NULL>",
- grant->proxied_user ? grant->proxied_user : "<NULL>",
- host.hostname ? host.hostname : "<NULL>",
- grant->host.hostname ? grant->host.hostname : "<NULL>",
- proxied_host.hostname ? proxied_host.hostname : "<NULL>",
- grant->proxied_host.hostname ?
- grant->proxied_host.hostname : "<NULL>"));
+ user, grant->user, proxied_user, grant->proxied_user,
+ host.hostname, grant->host.hostname,
+ proxied_host.hostname, grant->proxied_host.hostname));
- DBUG_RETURN(auth_element_equals(user, grant->user) &&
- auth_element_equals(proxied_user, grant->proxied_user) &&
- auth_element_equals(host.hostname, grant->host.hostname) &&
- auth_element_equals(proxied_host.hostname,
- grant->proxied_host.hostname));
+ bool res= auth_element_equals(user, grant->user) &&
+ auth_element_equals(proxied_user, grant->proxied_user) &&
+ auth_element_equals(host.hostname, grant->host.hostname) &&
+ auth_element_equals(proxied_host.hostname,
+ grant->proxied_host.hostname);
+ DBUG_RETURN(res);
}
@@ -446,23 +531,21 @@ public:
with_grant= grant->with_grant;
}
- static int store_pk(TABLE *table,
- const LEX_STRING *host,
+ static int store_pk(TABLE *table,
+ const LEX_STRING *host,
const LEX_STRING *user,
- const LEX_STRING *proxied_host,
+ const LEX_STRING *proxied_host,
const LEX_STRING *proxied_user)
{
DBUG_ENTER("ACL_PROXY_USER::store_pk");
DBUG_PRINT("info", ("host=%s, user=%s, proxied_host=%s, proxied_user=%s",
- host->str ? host->str : "<NULL>",
- user->str ? user->str : "<NULL>",
- proxied_host->str ? proxied_host->str : "<NULL>",
- proxied_user->str ? proxied_user->str : "<NULL>"));
- if (table->field[MYSQL_PROXIES_PRIV_HOST]->store(host->str,
+ host->str, user->str,
+ proxied_host->str, proxied_user->str));
+ if (table->field[MYSQL_PROXIES_PRIV_HOST]->store(host->str,
host->length,
system_charset_info))
DBUG_RETURN(TRUE);
- if (table->field[MYSQL_PROXIES_PRIV_USER]->store(user->str,
+ if (table->field[MYSQL_PROXIES_PRIV_USER]->store(user->str,
user->length,
system_charset_info))
DBUG_RETURN(TRUE);
@@ -490,10 +573,10 @@ public:
if (store_pk(table, host, user, proxied_host, proxied_user))
DBUG_RETURN(TRUE);
DBUG_PRINT("info", ("with_grant=%s", with_grant ? "TRUE" : "FALSE"));
- if (table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->store(with_grant ? 1 : 0,
- TRUE))
+ if (table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->store(with_grant ? 1 : 0,
+ TRUE))
DBUG_RETURN(TRUE);
- if (table->field[MYSQL_PROXIES_PRIV_GRANTOR]->store(grantor,
+ if (table->field[MYSQL_PROXIES_PRIV_GRANTOR]->store(grantor,
strlen(grantor),
system_charset_info))
DBUG_RETURN(TRUE);
@@ -520,6 +603,81 @@ static uchar* acl_entry_get_key(acl_entry *entry, size_t *length,
return (uchar*) entry->key;
}
+static uchar* acl_role_get_key(ACL_ROLE *entry, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length=(uint) entry->user.length;
+ return (uchar*) entry->user.str;
+}
+
+struct ROLE_GRANT_PAIR : public Sql_alloc
+{
+ char *u_uname;
+ char *u_hname;
+ char *r_uname;
+ LEX_STRING hashkey;
+ bool with_admin;
+
+ bool init(MEM_ROOT *mem, char *username, char *hostname, char *rolename,
+ bool with_admin_option);
+};
+
+static uchar* acl_role_map_get_key(ROLE_GRANT_PAIR *entry, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length=(uint) entry->hashkey.length;
+ return (uchar*) entry->hashkey.str;
+}
+
+bool ROLE_GRANT_PAIR::init(MEM_ROOT *mem, char *username,
+ char *hostname, char *rolename,
+ bool with_admin_option)
+{
+ if (!this)
+ return true;
+
+ size_t uname_l = username ? strlen(username) : 0;
+ size_t hname_l = hostname ? strlen(hostname) : 0;
+ size_t rname_l = rolename ? strlen(rolename) : 0;
+ /*
+ Create a buffer that holds all 3 NULL terminated strings in succession
+ To save memory space, the same buffer is used as the hashkey
+ */
+ size_t bufflen = uname_l + hname_l + rname_l + 3; //add the '\0' aswell
+ char *buff= (char *)alloc_root(mem, bufflen);
+ if (!buff)
+ return true;
+
+ /*
+ Offsets in the buffer for all 3 strings
+ */
+ char *username_pos= buff;
+ char *hostname_pos= buff + uname_l + 1;
+ char *rolename_pos= buff + uname_l + hname_l + 2;
+
+ if (username) //prevent undefined behaviour
+ memcpy(username_pos, username, uname_l);
+ username_pos[uname_l]= '\0'; //#1 string terminator
+ u_uname= username_pos;
+
+ if (hostname) //prevent undefined behaviour
+ memcpy(hostname_pos, hostname, hname_l);
+ hostname_pos[hname_l]= '\0'; //#2 string terminator
+ u_hname= hostname_pos;
+
+ if (rolename) //prevent undefined behaviour
+ memcpy(rolename_pos, rolename, rname_l);
+ rolename_pos[rname_l]= '\0'; //#3 string terminator
+ r_uname= rolename_pos;
+
+ hashkey.str = buff;
+ hashkey.length = bufflen;
+
+ with_admin= with_admin_option;
+
+ return false;
+}
+
#define IP_ADDR_STRLEN (3 + 1 + 3 + 1 + 3 + 1 + 3)
#define ACL_KEY_LENGTH (IP_ADDR_STRLEN + 1 + NAME_LEN + \
1 + USERNAME_LENGTH + 1)
@@ -542,7 +700,28 @@ static uchar* acl_entry_get_key(acl_entry *entry, size_t *length,
#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
#define NORMAL_HANDSHAKE_SIZE 6
+#define ROLE_ASSIGN_COLUMN_IDX 43
+/* various flags valid for ACL_USER */
+#define IS_ROLE (1L << 0)
+/* Flag to mark that a ROLE is on the recursive DEPTH_FIRST_SEARCH stack */
+#define ROLE_ON_STACK (1L << 1)
+/*
+ Flag to mark that a ROLE and all it's neighbours have
+ been visited
+*/
+#define ROLE_EXPLORED (1L << 2)
+/* Flag to mark that on_node was already called for this role */
+#define ROLE_OPENED (1L << 3)
+
static DYNAMIC_ARRAY acl_hosts, acl_users, acl_dbs, acl_proxy_users;
+static HASH acl_roles;
+/*
+ An hash containing mappings user <--> role
+
+ A hash is used so as to make updates quickly
+ The hashkey used represents all the entries combined
+*/
+static HASH acl_roles_mappings;
static MEM_ROOT mem, memex;
static bool initialized=0;
static bool allow_all_hosts=1;
@@ -551,38 +730,101 @@ static DYNAMIC_ARRAY acl_wild_hosts;
static hash_filo *acl_cache;
static uint grant_version=0; /* Version of priv tables. incremented by acl_load */
static ulong get_access(TABLE *form,uint fieldnr, uint *next_field=0);
+static bool check_is_role(TABLE *form);
static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b);
static ulong get_sort(uint count,...);
static void init_check_host(void);
static void rebuild_check_host(void);
-static ACL_USER *find_acl_user(const char *host, const char *user,
- my_bool exact);
+static void rebuild_role_grants(void);
+static ACL_USER *find_user_exact(const char *host, const char *user);
+static ACL_USER *find_user_wild(const char *host, const char *user, const char *ip= 0);
+static ACL_ROLE *find_acl_role(const char *user);
+static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_STRING *u, const LEX_STRING *h, const LEX_STRING *r);
+static ACL_USER_BASE *find_acl_user_base(const char *user, const char *host);
static bool update_user_table(THD *thd, TABLE *table, const char *host,
const char *user, const char *new_password,
uint new_password_len);
static my_bool acl_load(THD *thd, TABLE_LIST *tables);
static my_bool grant_load(THD *thd, TABLE_LIST *tables);
static inline void get_grantor(THD *thd, char* grantor);
+static bool add_role_user_mapping(const char *uname, const char *hname, const char *rname);
+
+#define ROLE_CYCLE_FOUND 2
+static int traverse_role_graph_up(ACL_ROLE *, void *,
+ int (*) (ACL_ROLE *, void *),
+ int (*) (ACL_ROLE *, ACL_ROLE *, void *));
+
+static int traverse_role_graph_down(ACL_USER_BASE *, void *,
+ int (*) (ACL_USER_BASE *, void *),
+ int (*) (ACL_USER_BASE *, ACL_ROLE *, void *));
+
/*
Enumeration of various ACL's and Hashes used in handle_grant_struct()
*/
enum enum_acl_lists
{
USER_ACL= 0,
+ ROLE_ACL,
DB_ACL,
COLUMN_PRIVILEGES_HASH,
PROC_PRIVILEGES_HASH,
FUNC_PRIVILEGES_HASH,
- PROXY_USERS_ACL
+ PROXY_USERS_ACL,
+ ROLES_MAPPINGS_HASH
};
+ACL_ROLE::ACL_ROLE(ACL_USER *user, MEM_ROOT *root) : counter(0)
+{
+
+ access= user->access;
+ /* set initial role access the same as the table row privileges */
+ initial_role_access= user->access;
+ this->user.str= safe_strdup_root(root, user->user.str);
+ this->user.length= user->user.length;
+ bzero(&role_grants, sizeof(role_grants));
+ bzero(&parent_grantee, sizeof(parent_grantee));
+ flags= IS_ROLE;
+}
+
+ACL_ROLE::ACL_ROLE(const char * rolename, ulong privileges, MEM_ROOT *root) :
+ initial_role_access(privileges), counter(0)
+{
+ this->access= initial_role_access;
+ this->user.str= safe_strdup_root(root, rolename);
+ this->user.length= strlen(rolename);
+ bzero(&role_grants, sizeof(role_grants));
+ bzero(&parent_grantee, sizeof(parent_grantee));
+ flags= IS_ROLE;
+}
+
+
+static bool is_invalid_role_name(const char *str)
+{
+ if (strcasecmp(str, "PUBLIC") && strcasecmp(str, "NONE"))
+ return false;
+
+ my_error(ER_INVALID_ROLE, MYF(0), str);
+ return true;
+}
+
+
+static void free_acl_user(ACL_USER *user)
+{
+ delete_dynamic(&(user->role_grants));
+}
+
+static void free_acl_role(ACL_ROLE *role)
+{
+ delete_dynamic(&(role->role_grants));
+ delete_dynamic(&(role->parent_grantee));
+}
+
/*
- Convert scrambled password to binary form, according to scramble type,
+ Convert scrambled password to binary form, according to scramble type,
Binary form is stored in user.salt.
*/
-static
-void
+static void
set_user_salt(ACL_USER *acl_user, const char *password, uint password_len)
{
if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH)
@@ -637,11 +879,20 @@ static bool fix_user_plugin_ptr(ACL_USER *user)
user->plugin= old_password_plugin_name;
else
return true;
-
+
set_user_salt(user, user->auth_string.str, user->auth_string.length);
return false;
}
+static bool get_YN_as_bool(Field *field)
+{
+ char buff[2];
+ String res(buff,sizeof(buff),&my_charset_latin1);
+ field->val_str(&res);
+ return res[0] == 'Y' || res[0] == 'y';
+}
+
+
/*
Initialize structures responsible for user/db-level privilege checking and
load privilege information for them from tables in the 'mysql' database.
@@ -711,10 +962,9 @@ my_bool acl_init(bool dont_read_acl_tables)
Choose from either native or old password plugins when assigning a password
*/
-static bool
-set_user_plugin (ACL_USER *user, int password_len)
+static bool set_user_plugin (ACL_USER *user, int password_len)
{
- switch (password_len)
+ switch (password_len)
{
case 0: /* no password */
case SCRAMBLED_PASSWORD_CHAR_LENGTH:
@@ -725,8 +975,8 @@ set_user_plugin (ACL_USER *user, int password_len)
return FALSE;
default:
sql_print_warning("Found invalid password for user: '%s@%s'; "
- "Ignoring user", user->user ? user->user : "",
- user->host.hostname ? user->host.hostname : "");
+ "Ignoring user", safe_str(user->user.str),
+ safe_str(user->host.hostname));
return TRUE;
}
}
@@ -739,8 +989,9 @@ set_user_plugin (ACL_USER *user, int password_len)
SYNOPSIS
acl_load()
thd Current thread
- tables List containing open "mysql.host", "mysql.user" and
- "mysql.db" tables.
+ tables List containing open "mysql.host", "mysql.user",
+ "mysql.db", "mysql.proxies_priv" and "mysql.roles_mapping"
+ tables.
RETURN VALUES
FALSE Success
@@ -762,8 +1013,6 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
grant_version++; /* Privileges updated */
- acl_cache->clear(1); // Clear locked hostname cache
-
init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
(void) my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST), 20, 50, MYF(0));
if (tables[0].table) // "host" table may not exist (e.g. in MySQL 5.6.7+)
@@ -795,8 +1044,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
"case that has been forced to lowercase because "
"lower_case_table_names is set. It will not be "
"possible to remove this privilege using REVOKE.",
- host.host.hostname ? host.host.hostname : "",
- host.db ? host.db : "");
+ host.host.hostname, host.db);
}
host.access= get_access(table,2);
host.access= fix_rights_for_db(host.access);
@@ -805,8 +1053,8 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
{
sql_print_warning("'host' entry '%s|%s' "
"ignored in --skip-name-resolve mode.",
- host.host.hostname ? host.host.hostname : "",
- host.db ? host.db : "");
+ safe_str(host.host.hostname),
+ safe_str(host.db));
continue;
}
#ifndef TO_BE_REMOVED
@@ -829,6 +1077,10 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
goto end;
table->use_all_columns();
(void) my_init_dynamic_array(&acl_users,sizeof(ACL_USER), 50, 100, MYF(0));
+ (void) my_hash_init2(&acl_roles,50, &my_charset_utf8_bin,
+ 0,0,0, (my_hash_get_key) acl_role_get_key,
+ (void (*)(void *))free_acl_role, 0);
+
username_char_length= MY_MIN(table->field[1]->char_length(),
USERNAME_CHAR_LENGTH);
password_length= table->field[2]->field_length /
@@ -876,25 +1128,42 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
while (!(read_record_info.read_record(&read_record_info)))
{
ACL_USER user;
+ bool is_role= FALSE;
bzero(&user, sizeof(user));
update_hostname(&user.host, get_field(&mem, table->field[0]));
- user.user= get_field(&mem, table->field[1]);
- if (check_no_resolve && hostname_requires_resolving(user.host.hostname))
+ char *username= get_field(&mem, table->field[1]);
+ user.user.str= username;
+ user.user.length= username? strlen(username) : 0;
+
+ /*
+ If the user entry is a role, skip password and hostname checks
+ A user can not log in with a role so some checks are not necessary
+ */
+ is_role= check_is_role(table);
+
+ if (is_role && is_invalid_role_name(username))
+ {
+ thd->clear_error(); // the warning is still issued
+ continue;
+ }
+
+ if (!is_role && check_no_resolve &&
+ hostname_requires_resolving(user.host.hostname))
{
sql_print_warning("'user' entry '%s@%s' "
"ignored in --skip-name-resolve mode.",
- user.user ? user.user : "",
- user.host.hostname ? user.host.hostname : "");
+ safe_str(user.user.str),
+ safe_str(user.host.hostname));
continue;
}
char *password= get_field(&mem, table->field[2]);
uint password_len= password ? strlen(password) : 0;
- user.auth_string.str= password ? password : const_cast<char*>("");
+ user.auth_string.str= safe_str(password);
user.auth_string.length= password_len;
set_user_salt(&user, password, password_len);
- if (set_user_plugin(&user, password_len))
+ if (!is_role && set_user_plugin(&user, password_len))
continue;
{
@@ -936,7 +1205,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
if (table->s->fields <= 38 && (user.access & SUPER_ACL))
user.access|= TRIGGER_ACL;
- user.sort= get_sort(2,user.host.hostname,user.user);
+ user.sort= get_sort(2, user.host.hostname, user.user.str);
user.hostname_length= (user.host.hostname ?
(uint) strlen(user.host.hostname) : 0);
@@ -974,7 +1243,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
user.user_resource.user_conn= ptr ? atoi(ptr) : 0;
}
- if (table->s->fields >= 41)
+ if (!is_role && table->s->fields >= 41)
{
/* We may have plugin & auth_String fields */
char *tmpstr= get_field(&mem, table->field[next_field++]);
@@ -987,12 +1256,11 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
sql_print_warning("'user' entry '%s@%s' has both a password "
"and an authentication plugin specified. The "
"password will be ignored.",
- user.user ? user.user : "",
- user.host.hostname ? user.host.hostname : "");
+ safe_str(user.user.str),
+ safe_str(user.host.hostname));
}
- user.auth_string.str= get_field(&mem, table->field[next_field++]);
- if (!user.auth_string.str)
- user.auth_string.str= const_cast<char*>("");
+ user.auth_string.str=
+ safe_str(get_field(&mem, table->field[next_field++]));
user.auth_string.length= strlen(user.auth_string.str);
fix_user_plugin_ptr(&user);
@@ -1016,7 +1284,26 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
user.access|= SUPER_ACL | EXECUTE_ACL;
#endif
}
- (void) push_dynamic(&acl_users,(uchar*) &user);
+
+ (void) my_init_dynamic_array(&user.role_grants,sizeof(ACL_ROLE *),
+ 8, 8, MYF(0));
+
+ if (is_role)
+ {
+ DBUG_PRINT("info", ("Found role %s", user.user.str));
+ ACL_ROLE *entry= new (&mem) ACL_ROLE(&user, &mem);
+ entry->role_grants = user.role_grants;
+ (void) my_init_dynamic_array(&entry->parent_grantee,
+ sizeof(ACL_USER_BASE *), 8, 8, MYF(0));
+ my_hash_insert(&acl_roles, (uchar *)entry);
+
+ continue;
+ }
+ else
+ {
+ DBUG_PRINT("info", ("Found user %s", user.user.str));
+ (void) push_dynamic(&acl_users,(uchar*) &user);
+ }
if (!user.host.hostname ||
(user.host.hostname[0] == wild_many && !user.host.hostname[1]))
allow_all_hosts=1; // Anyone can connect
@@ -1035,25 +1322,27 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
while (!(read_record_info.read_record(&read_record_info)))
{
ACL_DB db;
- update_hostname(&db.host,get_field(&mem, table->field[MYSQL_DB_FIELD_HOST]));
+ db.user=get_field(&mem, table->field[MYSQL_DB_FIELD_USER]);
+ const char *hostname= get_field(&mem, table->field[MYSQL_DB_FIELD_HOST]);
+ if (!hostname && find_acl_role(db.user))
+ hostname= "";
+ update_hostname(&db.host, hostname);
db.db=get_field(&mem, table->field[MYSQL_DB_FIELD_DB]);
if (!db.db)
{
sql_print_warning("Found an entry in the 'db' table with empty database name; Skipped");
continue;
}
- db.user=get_field(&mem, table->field[MYSQL_DB_FIELD_USER]);
if (check_no_resolve && hostname_requires_resolving(db.host.hostname))
{
sql_print_warning("'db' entry '%s %s@%s' "
"ignored in --skip-name-resolve mode.",
- db.db,
- db.user ? db.user : "",
- db.host.hostname ? db.host.hostname : "");
+ db.db, safe_str(db.user), safe_str(db.host.hostname));
continue;
}
db.access=get_access(table,3);
db.access=fix_rights_for_db(db.access);
+ db.initial_access= db.access;
if (lower_case_table_names)
{
/*
@@ -1073,9 +1362,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
"case that has been forced to lowercase because "
"lower_case_table_names is set. It will not be "
"possible to remove this privilege using REVOKE.",
- db.db,
- db.user ? db.user : "",
- db.host.hostname ? db.host.hostname : "");
+ db.db, safe_str(db.user), safe_str(db.host.hostname));
}
}
db.sort=get_sort(3,db.host.hostname,db.db,db.user);
@@ -1093,7 +1380,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
end_read_record(&read_record_info);
freeze_size(&acl_dbs);
- (void) my_init_dynamic_array(&acl_proxy_users, sizeof(ACL_PROXY_USER),
+ (void) my_init_dynamic_array(&acl_proxy_users, sizeof(ACL_PROXY_USER),
50, 100, MYF(0));
if (tables[3].table)
{
@@ -1125,6 +1412,48 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
}
freeze_size(&acl_proxy_users);
+ if (tables[4].table)
+ {
+ if (init_read_record(&read_record_info, thd, table= tables[4].table,
+ NULL, 1, 1, FALSE))
+ goto end;
+ table->use_all_columns();
+ /* account for every role mapping */
+
+ (void) my_hash_init2(&acl_roles_mappings, 50, system_charset_info,
+ 0,0,0, (my_hash_get_key) acl_role_map_get_key, 0,0);
+ MEM_ROOT temp_root;
+ init_alloc_root(&temp_root, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
+ while (!(read_record_info.read_record(&read_record_info)))
+ {
+ char *hostname= safe_str(get_field(&temp_root, table->field[0]));
+ char *username= safe_str(get_field(&temp_root, table->field[1]));
+ char *rolename= safe_str(get_field(&temp_root, table->field[2]));
+ bool with_grant_option= get_YN_as_bool(table->field[3]);
+
+ if (add_role_user_mapping(username, hostname, rolename)) {
+ sql_print_error("Invalid roles_mapping table entry user:'%s@%s', rolename:'%s'",
+ username, hostname, rolename);
+ continue;
+ }
+
+ ROLE_GRANT_PAIR *mapping= new (&mem) ROLE_GRANT_PAIR;
+
+ if (mapping->init(&mem, username, hostname, rolename, with_grant_option))
+ continue;
+
+ my_hash_insert(&acl_roles_mappings, (uchar*) mapping);
+ }
+
+ free_root(&temp_root, MYF(0));
+ end_read_record(&read_record_info);
+ }
+ else
+ {
+ sql_print_error("Missing system table mysql.roles_mapping; "
+ "please run mysql_upgrade to create it");
+ }
+
init_check_host();
initialized=1;
@@ -1138,13 +1467,15 @@ end:
void acl_free(bool end)
{
+ my_hash_free(&acl_roles);
free_root(&mem,MYF(0));
delete_dynamic(&acl_hosts);
- delete_dynamic(&acl_users);
+ delete_dynamic_with_callback(&acl_users, (FREE_FUNC) free_acl_user);
delete_dynamic(&acl_dbs);
delete_dynamic(&acl_wild_hosts);
delete_dynamic(&acl_proxy_users);
my_hash_free(&acl_check_hosts);
+ my_hash_free(&acl_roles_mappings);
plugin_unlock(0, native_password_plugin);
plugin_unlock(0, old_password_plugin);
if (!end)
@@ -1178,10 +1509,10 @@ void acl_free(bool end)
my_bool acl_reload(THD *thd)
{
- TABLE_LIST tables[4];
+ TABLE_LIST tables[5];
DYNAMIC_ARRAY old_acl_hosts, old_acl_users, old_acl_dbs, old_acl_proxy_users;
+ HASH old_acl_roles, old_acl_roles_mappings;
MEM_ROOT old_mem;
- bool old_initialized;
my_bool return_val= TRUE;
DBUG_ENTER("acl_reload");
@@ -1198,13 +1529,18 @@ my_bool acl_reload(THD *thd)
tables[3].init_one_table(C_STRING_WITH_LEN("mysql"),
C_STRING_WITH_LEN("proxies_priv"),
"proxies_priv", TL_READ);
+ tables[4].init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("roles_mapping"),
+ "roles_mapping", TL_READ);
tables[0].next_local= tables[0].next_global= tables + 1;
tables[1].next_local= tables[1].next_global= tables + 2;
tables[2].next_local= tables[2].next_global= tables + 3;
+ tables[3].next_local= tables[3].next_global= tables + 4;
tables[0].open_type= tables[1].open_type= tables[2].open_type=
- tables[3].open_type= OT_BASE_ONLY;
- tables[0].open_strategy= tables[3].open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
-
+ tables[3].open_type= tables[4].open_type= OT_BASE_ONLY;
+ tables[0].open_strategy= tables[3].open_strategy=
+ tables[4].open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
+
if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
{
/*
@@ -1217,11 +1553,13 @@ my_bool acl_reload(THD *thd)
goto end;
}
- if ((old_initialized=initialized))
- mysql_mutex_lock(&acl_cache->lock);
+ acl_cache->clear(0);
+ mysql_mutex_lock(&acl_cache->lock);
old_acl_hosts= acl_hosts;
old_acl_users= acl_users;
+ old_acl_roles= acl_roles;
+ old_acl_roles_mappings= acl_roles_mappings;
old_acl_proxy_users= acl_proxy_users;
old_acl_dbs= acl_dbs;
old_mem= mem;
@@ -1234,6 +1572,8 @@ my_bool acl_reload(THD *thd)
acl_free(); /* purecov: inspected */
acl_hosts= old_acl_hosts;
acl_users= old_acl_users;
+ acl_roles= old_acl_roles;
+ acl_roles_mappings= old_acl_roles_mappings;
acl_proxy_users= old_acl_proxy_users;
acl_dbs= old_acl_dbs;
mem= old_mem;
@@ -1241,20 +1581,20 @@ my_bool acl_reload(THD *thd)
}
else
{
+ my_hash_free(&old_acl_roles);
free_root(&old_mem,MYF(0));
delete_dynamic(&old_acl_hosts);
- delete_dynamic(&old_acl_users);
+ delete_dynamic_with_callback(&old_acl_users, (FREE_FUNC) free_acl_user);
delete_dynamic(&old_acl_proxy_users);
delete_dynamic(&old_acl_dbs);
+ my_hash_free(&old_acl_roles_mappings);
}
- if (old_initialized)
- mysql_mutex_unlock(&acl_cache->lock);
+ mysql_mutex_unlock(&acl_cache->lock);
end:
close_mysql_tables(thd);
DBUG_RETURN(return_val);
}
-
/*
Get all access bits from table after fieldnr
@@ -1286,8 +1626,7 @@ static ulong get_access(TABLE *form, uint fieldnr, uint *next_field)
((Field_enum*) (*pos))->typelib->count == 2 ;
pos++, fieldnr++, bit<<=1)
{
- (*pos)->val_str(&res);
- if (my_toupper(&my_charset_latin1, res[0]) == 'Y')
+ if (get_YN_as_bool(*pos))
access_bits|= bit;
}
if (next_field)
@@ -1295,6 +1634,34 @@ static ulong get_access(TABLE *form, uint fieldnr, uint *next_field)
return access_bits;
}
+/*
+ Check if a user entry in the user table is marked as being a role entry
+
+ IMPLEMENTATION
+ Access the coresponding column and check the coresponding ENUM of the form
+ ENUM('N', 'Y')
+
+ SYNOPSIS
+ check_is_role()
+ form an open table to read the entry from.
+ The record should be already read in table->record[0]
+
+ RETURN VALUE
+ TRUE if the user is marked as a role
+ FALSE otherwise
+*/
+
+static bool check_is_role(TABLE *form)
+{
+ char buff[2];
+ String res(buff, sizeof(buff), &my_charset_latin1);
+ /* Table version does not support roles */
+ if (form->s->fields <= ROLE_ASSIGN_COLUMN_IDX)
+ return FALSE;
+
+ return get_YN_as_bool(form->field[ROLE_ASSIGN_COLUMN_IDX]);
+}
+
/*
Return a number which, if sorted 'desc', puts strings in this order:
@@ -1374,12 +1741,11 @@ bool acl_getroot(Security_context *sctx, char *user, char *host,
DBUG_ENTER("acl_getroot");
DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'",
- (host ? host : "(NULL)"), (ip ? ip : "(NULL)"),
- user, (db ? db : "(NULL)")));
+ host, ip, user, db));
sctx->user= user;
sctx->host= host;
sctx->ip= ip;
- sctx->host_or_ip= host ? host : (ip ? ip : "");
+ sctx->host_or_ip= host ? host : (safe_str(ip));
if (!initialized)
{
@@ -1394,63 +1760,166 @@ bool acl_getroot(Security_context *sctx, char *user, char *host,
sctx->master_access= 0;
sctx->db_access= 0;
- *sctx->priv_user= *sctx->priv_host= 0;
+ *sctx->priv_user= *sctx->priv_host= *sctx->priv_role= 0;
- /*
- Find acl entry in user database.
- This is specially tailored to suit the check we do for CALL of
- a stored procedure; user is set to what is actually a
- priv_user, which can be ''.
- */
- for (i=0 ; i < acl_users.elements ; i++)
+ if (host[0]) // User, not Role
{
- ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*);
- if ((!acl_user_tmp->user && !user[0]) ||
- (acl_user_tmp->user && strcmp(user, acl_user_tmp->user) == 0))
+ acl_user= find_user_wild(host, user, ip);
+
+ if (acl_user)
{
- if (compare_hostname(&acl_user_tmp->host, host, ip))
+ res= 0;
+ for (i=0 ; i < acl_dbs.elements ; i++)
{
- acl_user= acl_user_tmp;
- res= 0;
- break;
+ ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*);
+ if (!acl_db->user ||
+ (user && user[0] && !strcmp(user, acl_db->user)))
+ {
+ if (compare_hostname(&acl_db->host, host, ip))
+ {
+ if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
+ {
+ sctx->db_access= acl_db->access;
+ break;
+ }
+ }
+ }
}
+ sctx->master_access= acl_user->access;
+
+ if (acl_user->user.str)
+ strmake_buf(sctx->priv_user, user);
+
+ if (acl_user->host.hostname)
+ strmake_buf(sctx->priv_host, acl_user->host.hostname);
}
}
-
- if (acl_user)
+ else // Role, not User
{
- for (i=0 ; i < acl_dbs.elements ; i++)
+ ACL_ROLE *acl_role= find_acl_role(user);
+ if (acl_role)
{
- ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*);
- if (!acl_db->user ||
- (user && user[0] && !strcmp(user, acl_db->user)))
+ res= 0;
+ for (i=0 ; i < acl_dbs.elements ; i++)
{
- if (compare_hostname(&acl_db->host, host, ip))
- {
- if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
- {
- sctx->db_access= acl_db->access;
- break;
- }
- }
+ ACL_DB *acl_db= dynamic_element(&acl_dbs, i, ACL_DB*);
+ if (!acl_db->user ||
+ (user && user[0] && !strcmp(user, acl_db->user)))
+ {
+ if (compare_hostname(&acl_db->host, "", ""))
+ {
+ if (!acl_db->db || (db && !wild_compare(db, acl_db->db, 0)))
+ {
+ sctx->db_access= acl_db->access;
+ break;
+ }
+ }
+ }
}
+ sctx->master_access= acl_role->access;
+
+ if (acl_role->user.str)
+ strmake_buf(sctx->priv_user, user);
+ sctx->priv_host[0]= 0;
}
- sctx->master_access= acl_user->access;
+ }
- if (acl_user->user)
- strmake_buf(sctx->priv_user, user);
- else
- *sctx->priv_user= 0;
+ mysql_mutex_unlock(&acl_cache->lock);
+ DBUG_RETURN(res);
+}
- if (acl_user->host.hostname)
- strmake_buf(sctx->priv_host, acl_user->host.hostname);
- else
- *sctx->priv_host= 0;
+int acl_check_setrole(THD *thd, char *rolename, ulonglong *access)
+{
+ ACL_ROLE *role;
+ ACL_USER_BASE *acl_user_base;
+ ACL_USER *UNINIT_VAR(acl_user);
+ bool is_granted= FALSE;
+ int result= 0;
+
+ /* clear role privileges */
+ mysql_mutex_lock(&acl_cache->lock);
+
+ if (!strcasecmp(rolename, "NONE"))
+ {
+ /* have to clear the privileges */
+ /* get the current user */
+ acl_user= find_user_exact(thd->security_ctx->priv_host,
+ thd->security_ctx->priv_user);
+ if (acl_user == NULL)
+ {
+ my_error(ER_INVALID_CURRENT_USER, MYF(0), rolename);
+ result= -1;
+ }
+ else if (access)
+ *access= acl_user->access;
+
+ goto end;
}
+
+ role= find_acl_role(rolename);
+
+ /* According to SQL standard, the same error message must be presented */
+ if (role == NULL) {
+ my_error(ER_INVALID_ROLE, MYF(0), rolename);
+ result= -1;
+ goto end;
+ }
+
+ for (uint i=0 ; i < role->parent_grantee.elements ; i++)
+ {
+ acl_user_base= *(dynamic_element(&role->parent_grantee, i, ACL_USER_BASE**));
+ if (acl_user_base->flags & IS_ROLE)
+ continue;
+
+ acl_user= (ACL_USER *)acl_user_base;
+ if (acl_user->wild_eq(thd->security_ctx->user, thd->security_ctx->host))
+ {
+ is_granted= TRUE;
+ break;
+ }
+ }
+
+ /* According to SQL standard, the same error message must be presented */
+ if (!is_granted)
+ {
+ my_error(ER_INVALID_ROLE, MYF(0), rolename);
+ result= 1;
+ goto end;
+ }
+
+ if (access)
+ {
+ *access = acl_user->access | role->access;
+ }
+end:
mysql_mutex_unlock(&acl_cache->lock);
- DBUG_RETURN(res);
+ return result;
}
+
+int acl_setrole(THD *thd, char *rolename, ulonglong access)
+{
+ /* merge the privileges */
+ Security_context *sctx= thd->security_ctx;
+ sctx->master_access= static_cast<ulong>(access);
+ if (thd->db)
+ sctx->db_access= acl_get(sctx->host, sctx->ip, sctx->user, thd->db, FALSE);
+
+ if (!strcasecmp(rolename, "NONE"))
+ {
+ thd->security_ctx->priv_role[0]= 0;
+ }
+ else
+ {
+ if (thd->db)
+ sctx->db_access|= acl_get("", "", rolename, thd->db, FALSE);
+ /* mark the current role */
+ strmake_buf(thd->security_ctx->priv_role, rolename);
+ }
+ return 0;
+}
+
+
static uchar* check_get_key(ACL_USER *buff, size_t *length,
my_bool not_used __attribute__((unused)))
{
@@ -1459,6 +1928,14 @@ static uchar* check_get_key(ACL_USER *buff, size_t *length,
}
+static void acl_update_role(const char *rolename, ulong privileges)
+{
+ ACL_ROLE *role= find_acl_role(rolename);
+ if (role)
+ role->initial_role_access= role->access= privileges;
+}
+
+
static void acl_update_user(const char *user, const char *host,
const char *password, uint password_len,
enum SSL_type ssl_type,
@@ -1475,57 +1952,66 @@ static void acl_update_user(const char *user, const char *host,
for (uint i=0 ; i < acl_users.elements ; i++)
{
ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
- if ((!acl_user->user && !user[0]) ||
- (acl_user->user && !strcmp(user,acl_user->user)))
+ if (acl_user->eq(user, host))
{
- if ((!acl_user->host.hostname && !host[0]) ||
- (acl_user->host.hostname &&
- !my_strcasecmp(system_charset_info, host, acl_user->host.hostname)))
+ if (plugin->str[0])
{
- if (plugin->str[0])
+ acl_user->plugin= *plugin;
+ acl_user->auth_string.str= auth->str ?
+ strmake_root(&mem, auth->str, auth->length) : const_cast<char*>("");
+ acl_user->auth_string.length= auth->length;
+ if (fix_user_plugin_ptr(acl_user))
+ acl_user->plugin.str= strmake_root(&mem, plugin->str, plugin->length);
+ }
+ else
+ if (password[0])
{
- acl_user->plugin= *plugin;
- acl_user->auth_string.str= auth->str ?
- strmake_root(&mem, auth->str, auth->length) : const_cast<char*>("");
- acl_user->auth_string.length= auth->length;
- if (fix_user_plugin_ptr(acl_user))
- acl_user->plugin.str= strmake_root(&mem, plugin->str, plugin->length);
+ acl_user->auth_string.str= strmake_root(&mem, password, password_len);
+ acl_user->auth_string.length= password_len;
+ set_user_salt(acl_user, password, password_len);
+ set_user_plugin(acl_user, password_len);
}
- else
- if (password[0])
- {
- acl_user->auth_string.str= strmake_root(&mem, password, password_len);
- acl_user->auth_string.length= password_len;
- set_user_salt(acl_user, password, password_len);
- set_user_plugin(acl_user, password_len);
- }
- acl_user->access=privileges;
- if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
- acl_user->user_resource.questions=mqh->questions;
- if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
- acl_user->user_resource.updates=mqh->updates;
- if (mqh->specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
- acl_user->user_resource.conn_per_hour= mqh->conn_per_hour;
- if (mqh->specified_limits & USER_RESOURCES::USER_CONNECTIONS)
- acl_user->user_resource.user_conn= mqh->user_conn;
- if (ssl_type != SSL_TYPE_NOT_SPECIFIED)
- {
- acl_user->ssl_type= ssl_type;
- acl_user->ssl_cipher= (ssl_cipher ? strdup_root(&mem,ssl_cipher) :
- 0);
- acl_user->x509_issuer= (x509_issuer ? strdup_root(&mem,x509_issuer) :
- 0);
- acl_user->x509_subject= (x509_subject ?
- strdup_root(&mem,x509_subject) : 0);
- }
- /* search complete: */
- break;
+ acl_user->access=privileges;
+ if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
+ acl_user->user_resource.questions=mqh->questions;
+ if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
+ acl_user->user_resource.updates=mqh->updates;
+ if (mqh->specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
+ acl_user->user_resource.conn_per_hour= mqh->conn_per_hour;
+ if (mqh->specified_limits & USER_RESOURCES::USER_CONNECTIONS)
+ acl_user->user_resource.user_conn= mqh->user_conn;
+ if (ssl_type != SSL_TYPE_NOT_SPECIFIED)
+ {
+ acl_user->ssl_type= ssl_type;
+ acl_user->ssl_cipher= (ssl_cipher ? strdup_root(&mem,ssl_cipher) :
+ 0);
+ acl_user->x509_issuer= (x509_issuer ? strdup_root(&mem,x509_issuer) :
+ 0);
+ acl_user->x509_subject= (x509_subject ?
+ strdup_root(&mem,x509_subject) : 0);
}
+ /* search complete: */
+ break;
}
}
}
+static void acl_insert_role(const char *rolename, ulong privileges)
+{
+ ACL_ROLE *entry;
+
+ mysql_mutex_assert_owner(&acl_cache->lock);
+ entry= new (&mem) ACL_ROLE(rolename, privileges, &mem);
+ (void) my_init_dynamic_array(&entry->parent_grantee,
+ sizeof(ACL_USER_BASE *), 8, 8, MYF(0));
+ (void) my_init_dynamic_array(&entry->role_grants,sizeof(ACL_ROLE *),
+ 8, 8, MYF(0));
+
+ my_hash_insert(&acl_roles, (uchar *)entry);
+}
+
+
static void acl_insert_user(const char *user, const char *host,
const char *password, uint password_len,
enum SSL_type ssl_type,
@@ -1541,8 +2027,9 @@ static void acl_insert_user(const char *user, const char *host,
mysql_mutex_assert_owner(&acl_cache->lock);
- acl_user.user=*user ? strdup_root(&mem,user) : 0;
- update_hostname(&acl_user.host, *host ? strdup_root(&mem, host): 0);
+ acl_user.user.str=*user ? strdup_root(&mem,user) : 0;
+ acl_user.user.length= strlen(user);
+ update_hostname(&acl_user.host, safe_strdup_root(&mem, host));
if (plugin->str[0])
{
acl_user.plugin= *plugin;
@@ -1560,15 +2047,18 @@ static void acl_insert_user(const char *user, const char *host,
set_user_plugin(&acl_user, password_len);
}
+ acl_user.flags= 0;
acl_user.access=privileges;
acl_user.user_resource = *mqh;
- acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user);
+ acl_user.sort=get_sort(2, acl_user.host.hostname, acl_user.user.str);
acl_user.hostname_length=(uint) strlen(host);
acl_user.ssl_type= (ssl_type != SSL_TYPE_NOT_SPECIFIED ?
ssl_type : SSL_TYPE_NONE);
acl_user.ssl_cipher= ssl_cipher ? strdup_root(&mem,ssl_cipher) : 0;
acl_user.x509_issuer= x509_issuer ? strdup_root(&mem,x509_issuer) : 0;
acl_user.x509_subject=x509_subject ? strdup_root(&mem,x509_subject) : 0;
+ (void) my_init_dynamic_array(&acl_user.role_grants, sizeof(ACL_USER *),
+ 8, 8, MYF(0));
(void) push_dynamic(&acl_users,(uchar*) &acl_user);
if (!acl_user.host.hostname ||
@@ -1579,11 +2069,17 @@ static void acl_insert_user(const char *user, const char *host,
/* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
rebuild_check_host();
+
+ /*
+ Rebuild every user's role_grants since 'acl_users' has been sorted
+ and old pointers to ACL_USER elements are no longer valid
+ */
+ rebuild_role_grants();
}
static void acl_update_db(const char *user, const char *host, const char *db,
- ulong privileges)
+ ulong privileges)
{
mysql_mutex_assert_owner(&acl_cache->lock);
@@ -1603,7 +2099,10 @@ static void acl_update_db(const char *user, const char *host, const char *db,
{
if (privileges)
- acl_db->access=privileges;
+ {
+ acl_db->access= privileges;
+ acl_db->initial_access= acl_db->access;
+ }
else
delete_dynamic_element(&acl_dbs,i);
}
@@ -1628,14 +2127,14 @@ static void acl_update_db(const char *user, const char *host, const char *db,
*/
static void acl_insert_db(const char *user, const char *host, const char *db,
- ulong privileges)
+ ulong privileges)
{
ACL_DB acl_db;
mysql_mutex_assert_owner(&acl_cache->lock);
acl_db.user=strdup_root(&mem,user);
- update_hostname(&acl_db.host, *host ? strdup_root(&mem,host) : 0);
+ update_hostname(&acl_db.host, safe_strdup_root(&mem, host));
acl_db.db=strdup_root(&mem,db);
- acl_db.access=privileges;
+ acl_db.initial_access= acl_db.access= privileges;
acl_db.sort=get_sort(3,acl_db.host.hostname,acl_db.db,acl_db.user);
(void) push_dynamic(&acl_dbs,(uchar*) &acl_db);
my_qsort((uchar*) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements,
@@ -1643,7 +2142,6 @@ static void acl_insert_db(const char *user, const char *host, const char *db,
}
-
/*
Get privilege for a host, user and db combination
@@ -1661,7 +2159,7 @@ ulong acl_get(const char *host, const char *ip,
acl_entry *entry;
DBUG_ENTER("acl_get");
- tmp_db= strmov(strmov(key, ip ? ip : "") + 1, user) + 1;
+ tmp_db= strmov(strmov(key, safe_str(ip)) + 1, user) + 1;
end= strnmov(tmp_db, db, key + sizeof(key) - tmp_db);
if (end >= key + sizeof(key)) // db name was truncated
@@ -1694,12 +2192,15 @@ ulong acl_get(const char *host, const char *ip,
{
if (compare_hostname(&acl_db->host,host,ip))
{
- if (!acl_db->db || !wild_compare(db,acl_db->db,db_is_pattern))
- {
- db_access=acl_db->access;
- if (acl_db->host.hostname)
- goto exit; // Fully specified. Take it
- break; /* purecov: tested */
+ if (!acl_db->db || !wild_compare(db,acl_db->db,db_is_pattern))
+ {
+ db_access=acl_db->access;
+ if (acl_db->host.hostname)
+ goto exit; // Fully specified. Take it
+ /* the host table is not used for roles */
+ if ((!host || !host[0]) && !acl_db->host.hostname && find_acl_role(user))
+ goto exit;
+ break; /* purecov: tested */
}
}
}
@@ -1808,9 +2309,143 @@ void rebuild_check_host(void)
init_check_host();
}
+/*
+ Reset a role role_grants dynamic array.
+ Also, the role's access bits are reset to the ones present in the table.
+*/
+static my_bool acl_role_reset_role_arrays(void *ptr,
+ void * not_used __attribute__((unused)))
+{
+ ACL_ROLE *role= (ACL_ROLE *)ptr;
+ reset_dynamic(&role->role_grants);
+ reset_dynamic(&role->parent_grantee);
+ role->counter= 0;
+ return 0;
+}
-/* Return true if there is no users that can match the given host */
+/*
+ Add a the coresponding pointers present in the mapping to the entries in
+ acl_users and acl_roles
+*/
+static bool add_role_user_mapping(ACL_USER_BASE *grantee, ACL_ROLE *role)
+{
+ return push_dynamic(&grantee->role_grants, (uchar*) &role)
+ || push_dynamic(&role->parent_grantee, (uchar*) &grantee);
+
+}
+
+/*
+ Revert the last add_role_user_mapping() action
+*/
+static void undo_add_role_user_mapping(ACL_USER_BASE *grantee, ACL_ROLE *role)
+{
+ void *pop __attribute__((unused));
+
+ pop= pop_dynamic(&grantee->role_grants);
+ DBUG_ASSERT(role == *(ACL_ROLE**)pop);
+
+ pop= pop_dynamic(&role->parent_grantee);
+ DBUG_ASSERT(grantee == *(ACL_USER_BASE**)pop);
+}
+
+/*
+ this helper is used when building role_grants and parent_grantee arrays
+ from scratch.
+
+ this happens either on initial loading of data from tables, in acl_load().
+ or in rebuild_role_grants after acl_role_reset_role_arrays().
+*/
+static bool add_role_user_mapping(const char *uname, const char *hname,
+ const char *rname)
+{
+ ACL_USER_BASE *grantee= find_acl_user_base(uname, hname);
+ ACL_ROLE *role= find_acl_role(rname);
+
+ if (grantee == NULL || role == NULL)
+ return 1;
+
+ /*
+ because all arrays are rebuilt completely, and counters were also reset,
+ we can increment them here, and after the rebuild all counters will
+ have correct values (equal to the number of roles granted).
+ */
+ if (grantee->flags & IS_ROLE)
+ ((ACL_ROLE*)grantee)->counter++;
+ return add_role_user_mapping(grantee, role);
+}
+
+/*
+ This helper function is used to removes roles and grantees
+ from the corresponding cross-reference arrays. see remove_role_user_mapping().
+ as such, it asserts that an element to delete is present in the array,
+ and is present only once.
+*/
+static void remove_ptr_from_dynarray(DYNAMIC_ARRAY *array, void *ptr)
+{
+ bool found __attribute__((unused))= false;
+ for (uint i= 0; i < array->elements; i++)
+ {
+ if (ptr == *dynamic_element(array, i, void**))
+ {
+ DBUG_ASSERT(!found);
+ delete_dynamic_element(array, i);
+ IF_DBUG(found= true, break);
+ }
+ }
+ DBUG_ASSERT(found);
+}
+
+static void remove_role_user_mapping(ACL_USER_BASE *grantee, ACL_ROLE *role,
+ int grantee_idx=-1, int role_idx=-1)
+{
+ remove_ptr_from_dynarray(&grantee->role_grants, role);
+ remove_ptr_from_dynarray(&role->parent_grantee, grantee);
+}
+
+
+static my_bool add_role_user_mapping_action(void *ptr, void *unused __attribute__((unused)))
+{
+ ROLE_GRANT_PAIR *pair= (ROLE_GRANT_PAIR*)ptr;
+ my_bool status __attribute__((unused));
+ status= add_role_user_mapping(pair->u_uname, pair->u_hname, pair->r_uname);
+ /*
+ The invariant chosen is that acl_roles_mappings should _always_
+ only contain valid entries, referencing correct user and role grants.
+ If add_role_user_mapping detects an invalid entry, it will not add
+ the mapping into the ACL_USER::role_grants array.
+ */
+ DBUG_ASSERT(status >= 0);
+ return 0;
+}
+
+/*
+ Rebuild the role grants every time the acl_users is modified
+
+ The role grants in the ACL_USER class need to be rebuilt, as they contain
+ pointers to elements of the acl_users array.
+*/
+
+static void rebuild_role_grants(void)
+{
+ DBUG_ENTER("rebuild_role_grants");
+ /*
+ Reset every user's and role's role_grants array
+ */
+ for (uint i=0; i < acl_users.elements; i++) {
+ ACL_USER *user= dynamic_element(&acl_users, i, ACL_USER *);
+ reset_dynamic(&user->role_grants);
+ }
+ my_hash_iterate(&acl_roles, acl_role_reset_role_arrays, NULL);
+
+ /* Rebuild the direct links between users and roles in ACL_USER::role_grants */
+ my_hash_iterate(&acl_roles_mappings, add_role_user_mapping_action, NULL);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/* Return true if there is no users that can match the given host */
bool acl_check_host(const char *host, const char *ip)
{
if (allow_all_hosts)
@@ -1870,20 +2505,25 @@ int check_change_password(THD *thd, const char *host, const char *user,
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
return(1);
}
+ if (!thd->slave_thread && !thd->security_ctx->priv_user[0])
+ {
+ my_message(ER_PASSWORD_ANONYMOUS_USER, ER(ER_PASSWORD_ANONYMOUS_USER),
+ MYF(0));
+ return(1);
+ }
+ if (!host) // Role
+ {
+ my_error(ER_PASSWORD_NO_MATCH, MYF(0));
+ return 1;
+ }
if (!thd->slave_thread &&
- (strcmp(thd->security_ctx->user, user) ||
+ (strcmp(thd->security_ctx->priv_user, user) ||
my_strcasecmp(system_charset_info, host,
thd->security_ctx->priv_host)))
{
if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 0))
return(1);
}
- if (!thd->slave_thread && !thd->security_ctx->user[0])
- {
- my_message(ER_PASSWORD_ANONYMOUS_USER, ER(ER_PASSWORD_ANONYMOUS_USER),
- MYF(0));
- return(1);
- }
size_t len= strlen(new_password);
if (len && len != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
len != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
@@ -1963,7 +2603,7 @@ bool change_password(THD *thd, const char *host, const char *user,
mysql_mutex_lock(&acl_cache->lock);
ACL_USER *acl_user;
- if (!(acl_user= find_acl_user(host, user, TRUE)))
+ if (!(acl_user= find_user_exact(host, user)))
{
mysql_mutex_unlock(&acl_cache->lock);
my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
@@ -1971,7 +2611,7 @@ bool change_password(THD *thd, const char *host, const char *user,
}
/* update loaded acl entry: */
- if (acl_user->plugin.str == native_password_plugin_name.str ||
+ if (acl_user->plugin.str == native_password_plugin_name.str ||
acl_user->plugin.str == old_password_plugin_name.str)
{
acl_user->auth_string.str= strmake_root(&mem, new_password, new_password_len);
@@ -1984,8 +2624,8 @@ bool change_password(THD *thd, const char *host, const char *user,
ER_SET_PASSWORD_AUTH_PLUGIN, ER(ER_SET_PASSWORD_AUTH_PLUGIN));
if (update_user_table(thd, table,
- acl_user->host.hostname ? acl_user->host.hostname : "",
- acl_user->user ? acl_user->user : "",
+ safe_str(acl_user->host.hostname),
+ safe_str(acl_user->user.str),
new_password, new_password_len))
{
mysql_mutex_unlock(&acl_cache->lock); /* purecov: deadcode */
@@ -1999,8 +2639,8 @@ bool change_password(THD *thd, const char *host, const char *user,
{
query_length=
sprintf(buff,"SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'",
- acl_user->user ? acl_user->user : "",
- acl_user->host.hostname ? acl_user->host.hostname : "",
+ safe_str(acl_user->user.str),
+ safe_str(acl_user->host.hostname),
new_password);
thd->clear_error();
result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length,
@@ -2024,7 +2664,7 @@ end:
RETURN
FALSE user not fond
- TRUE there are such user
+ TRUE there is such user
*/
bool is_acl_user(const char *host, const char *user)
@@ -2036,45 +2676,96 @@ bool is_acl_user(const char *host, const char *user)
return TRUE;
mysql_mutex_lock(&acl_cache->lock);
- res= find_acl_user(host, user, TRUE) != NULL;
+
+ if (*host) // User
+ res= find_user_exact(host, user) != NULL;
+ else // Role
+ res= find_acl_role(user) != NULL;
+
mysql_mutex_unlock(&acl_cache->lock);
return res;
}
/*
- Find first entry that matches the current user
+ unlike find_user_exact and find_user_wild,
+ this function finds anonymous users too, it's when a
+ user is not empty, but priv_user (acl_user->user) is empty.
*/
+static ACL_USER *find_user_or_anon(const char *host, const char *user, const char *ip)
+{
+ ACL_USER *result= NULL;
+ mysql_mutex_assert_owner(&acl_cache->lock);
+ for (uint i=0; i < acl_users.elements; i++)
+ {
+ ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*);
+ if ((!acl_user_tmp->user.str ||
+ !strcmp(user, acl_user_tmp->user.str)) &&
+ compare_hostname(&acl_user_tmp->host, host, ip))
+ {
+ result= acl_user_tmp;
+ break;
+ }
+ }
+ return result;
+}
+
-static ACL_USER *
-find_acl_user(const char *host, const char *user, my_bool exact)
+/*
+ Find first entry that matches the specified user@host pair
+*/
+static ACL_USER * find_user_exact(const char *host, const char *user)
{
- DBUG_ENTER("find_acl_user");
- DBUG_PRINT("enter",("host: '%s' user: '%s'",host,user));
+ mysql_mutex_assert_owner(&acl_cache->lock);
+
+ for (uint i=0 ; i < acl_users.elements ; i++)
+ {
+ ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
+ if (acl_user->eq(user, host))
+ return acl_user;
+ }
+ return 0;
+}
+/*
+ Find first entry that matches the specified user@host pair
+*/
+static ACL_USER * find_user_wild(const char *host, const char *user, const char *ip)
+{
mysql_mutex_assert_owner(&acl_cache->lock);
for (uint i=0 ; i < acl_users.elements ; i++)
{
ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
- DBUG_PRINT("info",("strcmp('%s','%s'), compare_hostname('%s','%s'),",
- user, acl_user->user ? acl_user->user : "",
- host,
- acl_user->host.hostname ? acl_user->host.hostname :
- ""));
- if ((!acl_user->user && !user[0]) ||
- (acl_user->user && !strcmp(user,acl_user->user)))
- {
- if (exact ? !my_strcasecmp(system_charset_info, host,
- acl_user->host.hostname ?
- acl_user->host.hostname : "") :
- compare_hostname(&acl_user->host,host,host))
- {
- DBUG_RETURN(acl_user);
- }
- }
+ if (acl_user->wild_eq(user, host, ip))
+ return acl_user;
}
- DBUG_RETURN(0);
+ return 0;
+}
+
+/*
+ Find a role with the specified name
+*/
+static ACL_ROLE *find_acl_role(const char *role)
+{
+ DBUG_ENTER("find_acl_role");
+ DBUG_PRINT("enter",("role: '%s'", role));
+ DBUG_PRINT("info", ("Hash elements: %ld", acl_roles.records));
+
+ mysql_mutex_assert_owner(&acl_cache->lock);
+
+ ACL_ROLE *r= (ACL_ROLE *)my_hash_search(&acl_roles, (uchar *)role,
+ role ? strlen(role) : 0);
+ DBUG_RETURN(r);
+}
+
+
+static ACL_USER_BASE *find_acl_user_base(const char *user, const char *host)
+{
+ if (*host)
+ return find_user_exact(host, user);
+
+ return find_acl_role(user);
}
@@ -2111,10 +2802,11 @@ static const char *calc_ip(const char *ip, long *val, char end)
static void update_hostname(acl_host_and_ip *host, const char *hostname)
{
+ // fix historical undocumented convention that empty host is the same as '%'
+ hostname=const_cast<char*>(hostname ? hostname : host_not_specified.str);
host->hostname=(char*) hostname; // This will not be modified!
- if (!hostname ||
- (!(hostname=calc_ip(hostname,&host->ip,'/')) ||
- !(hostname=calc_ip(hostname+1,&host->ip_mask,'\0'))))
+ if (!(hostname= calc_ip(hostname,&host->ip,'/')) ||
+ !(hostname= calc_ip(hostname+1,&host->ip_mask,'\0')))
{
host->ip= host->ip_mask=0; // Not a masked ip
}
@@ -2286,6 +2978,8 @@ static bool test_if_create_new_users(THD *thd)
db_access=acl_get(sctx->host, sctx->ip,
sctx->priv_user, tl.db, 0);
+ if (sctx->priv_role[0])
+ db_access|= acl_get("", "", sctx->priv_role, tl.db, 0);
if (!(db_access & INSERT_ACL))
{
if (check_grant(thd, INSERT_ACL, &tl, FALSE, UINT_MAX, TRUE))
@@ -2302,12 +2996,13 @@ static bool test_if_create_new_users(THD *thd)
static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo,
ulong rights, bool revoke_grant,
- bool can_create_user, bool no_auto_create)
+ bool can_create_user, bool no_auto_create)
{
int error = -1;
bool old_row_exists=0;
char what= (revoke_grant) ? 'N' : 'Y';
uchar user_key[MAX_KEY_LENGTH];
+ bool handle_as_role= combo.is_role();
LEX *lex= thd->lex;
DBUG_ENTER("replace_user_table");
@@ -2325,6 +3020,15 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo,
else
combo.password= empty_lex_str;
+ /* if the user table is not up to date, we can't handle role updates */
+ if (table->s->fields <= ROLE_ASSIGN_COLUMN_IDX && handle_as_role)
+ {
+ my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0),
+ table->alias.c_ptr(), ROLE_ASSIGN_COLUMN_IDX + 1, table->s->fields,
+ static_cast<int>(table->s->mysql_version), MYSQL_VERSION_ID);
+ DBUG_RETURN(-1);
+ }
+
table->use_all_columns();
table->field[0]->store(combo.host.str,combo.host.length,
system_charset_info);
@@ -2483,6 +3187,16 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo,
table->field[next_field + 1]->reset();
}
}
+
+ /* table format checked earlier */
+ if (handle_as_role)
+ {
+ if (old_row_exists && !check_is_role(table))
+ {
+ goto end;
+ }
+ table->field[ROLE_ASSIGN_COLUMN_IDX]->store("Y", 1, system_charset_info);
+ }
}
if (old_row_exists)
@@ -2496,10 +3210,10 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo,
if ((error=
table->file->ha_update_row(table->record[1],table->record[0])) &&
error != HA_ERR_RECORD_IS_THE_SAME)
- { // This should never happen
- table->file->print_error(error,MYF(0)); /* purecov: deadcode */
- error= -1; /* purecov: deadcode */
- goto end; /* purecov: deadcode */
+ { // This should never happen
+ table->file->print_error(error,MYF(0)); /* purecov: deadcode */
+ error= -1; /* purecov: deadcode */
+ goto end; /* purecov: deadcode */
}
else
error= 0;
@@ -2521,27 +3235,37 @@ end:
{
acl_cache->clear(1); // Clear privilege cache
if (old_row_exists)
- acl_update_user(combo.user.str, combo.host.str,
- combo.password.str, combo.password.length,
- lex->ssl_type,
- lex->ssl_cipher,
- lex->x509_issuer,
- lex->x509_subject,
- &lex->mqh,
- rights,
- &combo.plugin,
- &combo.auth);
+ {
+ if (handle_as_role)
+ acl_update_role(combo.user.str, rights);
+ else
+ acl_update_user(combo.user.str, combo.host.str,
+ combo.password.str, combo.password.length,
+ lex->ssl_type,
+ lex->ssl_cipher,
+ lex->x509_issuer,
+ lex->x509_subject,
+ &lex->mqh,
+ rights,
+ &combo.plugin,
+ &combo.auth);
+ }
else
- acl_insert_user(combo.user.str, combo.host.str,
- combo.password.str, combo.password.length,
- lex->ssl_type,
- lex->ssl_cipher,
- lex->x509_issuer,
- lex->x509_subject,
- &lex->mqh,
- rights,
- &combo.plugin,
- &combo.auth);
+ {
+ if (handle_as_role)
+ acl_insert_role(combo.user.str, rights);
+ else
+ acl_insert_user(combo.user.str, combo.host.str,
+ combo.password.str, combo.password.length,
+ lex->ssl_type,
+ lex->ssl_cipher,
+ lex->x509_issuer,
+ lex->x509_subject,
+ &lex->mqh,
+ rights,
+ &combo.plugin,
+ &combo.auth);
+ }
}
DBUG_RETURN(error);
}
@@ -2570,10 +3294,14 @@ static int replace_db_table(TABLE *table, const char *db,
}
/* Check if there is such a user in user table in memory? */
- if (!find_acl_user(combo.host.str,combo.user.str, FALSE))
+ if (!find_user_wild(combo.host.str,combo.user.str))
{
- my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
- DBUG_RETURN(-1);
+ /* The user could be a role, check if the user is registered as a role */
+ if (!combo.host.length && !find_acl_role(combo.user.str))
+ {
+ my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
+ DBUG_RETURN(-1);
+ }
}
table->use_all_columns();
@@ -2655,8 +3383,107 @@ abort:
DBUG_RETURN(-1);
}
+/**
+ Updates the mysql.roles_mapping table and the acl_roles_mappings hash.
+
+ @param table TABLE to update
+ @param user user name of the grantee
+ @param host host name of the grantee
+ @param role role name to grant
+ @param with_admin WITH ADMIN OPTION flag
+ @param existing the entry in the acl_roles_mappings hash or NULL.
+ it is never NULL if revoke_grant is true.
+ it is NULL when a new pair is added, it's not NULL
+ when an existing pair is updated.
+ @param revoke_grant true for REVOKE, false for GRANT
+*/
+static int
+replace_roles_mapping_table(TABLE *table, LEX_STRING *user, LEX_STRING *host,
+ LEX_STRING *role, bool with_admin,
+ ROLE_GRANT_PAIR *existing, bool revoke_grant)
+{
+ DBUG_ENTER("replace_roles_mapping_table");
+
+ uchar row_key[MAX_KEY_LENGTH];
+ int error;
+ table->use_all_columns();
+ restore_record(table, s->default_values);
+ table->field[0]->store(host->str, host->length, system_charset_info);
+ table->field[1]->store(user->str, user->length, system_charset_info);
+ table->field[2]->store(role->str, role->length, system_charset_info);
+
+ DBUG_ASSERT(!revoke_grant || existing);
+
+ if (existing) // delete or update
+ {
+ key_copy(row_key, table->record[0], table->key_info,
+ table->key_info->key_length);
+ if (table->file->ha_index_read_idx_map(table->record[1], 0, row_key,
+ HA_WHOLE_KEY, HA_READ_KEY_EXACT))
+ {
+ /* No match */
+ DBUG_RETURN(1);
+ }
+ if (revoke_grant && !with_admin)
+ {
+ if ((error= table->file->ha_delete_row(table->record[1])))
+ {
+ DBUG_PRINT("info", ("error deleting row '%s' '%s' '%s'",
+ host->str, user->str, role->str));
+ goto table_error;
+ }
+ /*
+ This should always return something, as the check was performed
+ earlier
+ */
+ my_hash_delete(&acl_roles_mappings, (uchar*)existing);
+ }
+ else
+ {
+ if (revoke_grant)
+ existing->with_admin= false;
+ else
+ existing->with_admin|= with_admin;
+
+ table->field[3]->store(existing->with_admin + 1);
-static void
+ if ((error= table->file->ha_update_row(table->record[1], table->record[0])))
+ {
+ DBUG_PRINT("info", ("error updating row '%s' '%s' '%s'",
+ host->str, user->str, role->str));
+ goto table_error;
+ }
+ }
+ DBUG_RETURN(0);
+ }
+
+ table->field[3]->store(with_admin + 1);
+
+ if ((error= table->file->ha_write_row(table->record[0])))
+ {
+ DBUG_PRINT("info", ("error inserting row '%s' '%s' '%s'",
+ host->str, user->str, role->str));
+ goto table_error;
+ }
+ else
+ {
+ /* allocate a new entry that will go in the hash */
+ ROLE_GRANT_PAIR *hash_entry= new (&mem) ROLE_GRANT_PAIR;
+ if (hash_entry->init(&mem, user->str, host->str, role->str, with_admin))
+ DBUG_RETURN(1);
+ my_hash_insert(&acl_roles_mappings, (uchar*) hash_entry);
+ }
+
+ /* all ok */
+ DBUG_RETURN(0);
+
+table_error:
+ DBUG_PRINT("info", ("table error"));
+ table->file->print_error(error, MYF(0));
+ DBUG_RETURN(1);
+}
+
+static void
acl_update_proxy_user(ACL_PROXY_USER *new_value, bool is_revoke)
{
mysql_mutex_assert_owner(&acl_cache->lock);
@@ -2664,7 +3491,7 @@ acl_update_proxy_user(ACL_PROXY_USER *new_value, bool is_revoke)
DBUG_ENTER("acl_update_proxy_user");
for (uint i= 0; i < acl_proxy_users.elements; i++)
{
- ACL_PROXY_USER *acl_user=
+ ACL_PROXY_USER *acl_user=
dynamic_element(&acl_proxy_users, i, ACL_PROXY_USER *);
if (acl_user->pk_equals(new_value))
@@ -2686,7 +3513,7 @@ acl_update_proxy_user(ACL_PROXY_USER *new_value, bool is_revoke)
}
-static void
+static void
acl_insert_proxy_user(ACL_PROXY_USER *new_value)
{
DBUG_ENTER("acl_insert_proxy_user");
@@ -2699,9 +3526,9 @@ acl_insert_proxy_user(ACL_PROXY_USER *new_value)
}
-static int
+static int
replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user,
- const LEX_USER *proxied_user, bool with_grant_arg,
+ const LEX_USER *proxied_user, bool with_grant_arg,
bool revoke_grant)
{
bool old_row_exists= 0;
@@ -2719,14 +3546,14 @@ replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user,
}
/* Check if there is such a user in user table in memory? */
- if (!find_acl_user(user->host.str,user->user.str, FALSE))
+ if (!find_user_wild(user->host.str,user->user.str))
{
my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
DBUG_RETURN(-1);
}
table->use_all_columns();
- ACL_PROXY_USER::store_pk (table, &user->host, &user->user,
+ ACL_PROXY_USER::store_pk (table, &user->host, &user->user,
&proxied_user->host, &proxied_user->user);
key_copy(user_key, table->record[0], table->key_info,
@@ -2825,11 +3652,16 @@ class GRANT_COLUMN :public Sql_alloc
public:
char *column;
ulong rights;
+ ulong init_rights;
uint key_length;
- GRANT_COLUMN(String &c, ulong y) :rights (y)
+ GRANT_COLUMN(String &c, ulong y) :rights (y), init_rights(y)
{
column= (char*) memdup_root(&memex,c.ptr(), key_length=c.length());
}
+
+ /* this constructor assumes thas source->column is allocated in memex */
+ GRANT_COLUMN(GRANT_COLUMN *source) : column(source->column),
+ rights (source->rights), init_rights(0), key_length(source->key_length) { }
};
@@ -2840,13 +3672,13 @@ static uchar* get_key_column(GRANT_COLUMN *buff, size_t *length,
return (uchar*) buff->column;
}
-
class GRANT_NAME :public Sql_alloc
{
public:
acl_host_and_ip host;
char *db, *user, *tname, *hash_key;
ulong privs;
+ ulong init_privs; /* privileges found in physical table */
ulong sort;
size_t key_length;
GRANT_NAME(const char *h, const char *d,const char *u,
@@ -2864,6 +3696,7 @@ class GRANT_TABLE :public GRANT_NAME
{
public:
ulong cols;
+ ulong init_cols; /* privileges found in physical table */
HASH hash_columns;
GRANT_TABLE(const char *h, const char *d,const char *u,
@@ -2871,6 +3704,11 @@ public:
GRANT_TABLE (TABLE *form, TABLE *col_privs);
~GRANT_TABLE();
bool ok() { return privs != 0 || cols != 0; }
+ void init_hash()
+ {
+ my_hash_init2(&hash_columns, 4, system_charset_info,
+ 0, 0, 0, (my_hash_get_key) get_key_column, 0, 0);
+ }
};
@@ -2901,7 +3739,7 @@ void GRANT_NAME::set_user_details(const char *h, const char *d,
GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u,
const char *t, ulong p, bool is_routine)
- :db(0), tname(0), privs(p)
+ :db(0), tname(0), privs(p), init_privs(p)
{
set_user_details(h, d, u, t, is_routine);
}
@@ -2910,18 +3748,25 @@ GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u,
const char *t, ulong p, ulong c)
:GRANT_NAME(h,d,u,t,p, FALSE), cols(c)
{
- (void) my_hash_init2(&hash_columns,4,system_charset_info,
- 0,0,0, (my_hash_get_key) get_key_column,0,0);
+ init_hash();
}
-
+/*
+ create a new GRANT_TABLE entry for role inheritance. init_* fields are set
+ to 0
+*/
GRANT_NAME::GRANT_NAME(TABLE *form, bool is_routine)
{
- update_hostname(&host, get_field(&memex, form->field[0]));
+ user= safe_str(get_field(&memex,form->field[2]));
+
+ const char *hostname= get_field(&memex, form->field[0]);
+ mysql_mutex_lock(&acl_cache->lock);
+ if (!hostname && find_acl_role(user))
+ hostname= "";
+ mysql_mutex_unlock(&acl_cache->lock);
+ update_hostname(&host, hostname);
+
db= get_field(&memex,form->field[1]);
- user= get_field(&memex,form->field[2]);
- if (!user)
- user= (char*) "";
sort= get_sort(3, host.hostname, db, user);
tname= get_field(&memex,form->field[3]);
if (!db || !tname)
@@ -2943,6 +3788,7 @@ GRANT_NAME::GRANT_NAME(TABLE *form, bool is_routine)
strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
privs = (ulong) form->field[6]->val_int();
privs = fix_rights_for_table(privs);
+ init_privs= privs;
}
@@ -2959,10 +3805,17 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
return;
}
cols= (ulong) form->field[7]->val_int();
- cols = fix_rights_for_column(cols);
+ cols= fix_rights_for_column(cols);
+ /*
+ Initial columns privileges are the same as column privileges on creation.
+ In case of roles, the cols privilege bits can get inherited and thus
+ cause the cols field to change. The init_cols field is always the same
+ as the physical table entry
+ */
+ init_cols= cols;
+
+ init_hash();
- (void) my_hash_init2(&hash_columns,4,system_charset_info,
- 0,0,0, (my_hash_get_key) get_key_column,0,0);
if (cols)
{
uint key_prefix_len;
@@ -2985,6 +3838,7 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
if (col_privs->file->ha_index_init(0, 1))
{
cols= 0;
+ init_cols= 0;
return;
}
@@ -2992,7 +3846,8 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
(key_part_map)15,
HA_READ_KEY_EXACT))
{
- cols = 0; /* purecov: deadcode */
+ cols= 0; /* purecov: deadcode */
+ init_cols= 0;
col_privs->file->ha_index_end();
return;
}
@@ -3007,13 +3862,13 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs)
fix_rights_for_column(priv))))
{
/* Don't use this entry */
- privs = cols = 0; /* purecov: deadcode */
+ privs= cols= init_privs= init_cols=0; /* purecov: deadcode */
return; /* purecov: deadcode */
}
if (my_hash_insert(&hash_columns, (uchar *) mem_check))
{
/* Invalidate this entry */
- privs= cols= 0;
+ privs= cols= init_privs= init_cols=0;
return;
}
} while (!col_privs->file->ha_index_next(col_privs->record[0]) &&
@@ -3037,9 +3892,9 @@ static uchar* get_grant_table(GRANT_NAME *buff, size_t *length,
}
-void free_grant_table(GRANT_TABLE *grant_table)
+static void free_grant_table(GRANT_TABLE *grant_table)
{
- my_hash_free(&grant_table->hash_columns);
+ grant_table->~GRANT_TABLE();
}
@@ -3094,7 +3949,7 @@ static GRANT_NAME *name_hash_search(HASH *name_hash,
}
-inline GRANT_NAME *
+static GRANT_NAME *
routine_hash_search(const char *host, const char *ip, const char *db,
const char *user, const char *tname, bool proc, bool exact)
{
@@ -3104,7 +3959,7 @@ routine_hash_search(const char *host, const char *ip, const char *db,
}
-inline GRANT_TABLE *
+static GRANT_TABLE *
table_hash_search(const char *host, const char *ip, const char *db,
const char *user, const char *tname, bool exact)
{
@@ -3113,7 +3968,7 @@ table_hash_search(const char *host, const char *ip, const char *db,
}
-inline GRANT_COLUMN *
+static GRANT_COLUMN *
column_hash_search(GRANT_TABLE *t, const char *cname, uint length)
{
return (GRANT_COLUMN*) my_hash_search(&t->hash_columns,
@@ -3295,7 +4150,10 @@ static int replace_column_table(GRANT_TABLE *g_t,
goto end; /* purecov: deadcode */
}
if (grant_column)
- grant_column->rights = privileges; // Update hash
+ {
+ grant_column->rights = privileges; // Update hash
+ grant_column->init_rights = privileges;
+ }
}
else
{
@@ -3352,11 +4210,14 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
The following should always succeed as new users are created before
this function is called!
*/
- if (!find_acl_user(combo.host.str,combo.user.str, FALSE))
+ if (!find_user_wild(combo.host.str,combo.user.str))
{
- my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH),
- MYF(0)); /* purecov: deadcode */
- DBUG_RETURN(-1); /* purecov: deadcode */
+ if (!combo.host.length && !find_acl_role(combo.user.str))
+ {
+ my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH),
+ MYF(0)); /* purecov: deadcode */
+ DBUG_RETURN(-1); /* purecov: deadcode */
+ }
}
table->use_all_columns();
@@ -3440,6 +4301,9 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
if (rights | col_rights)
{
+ grant_table->init_privs= rights;
+ grant_table->init_cols= col_rights;
+
grant_table->privs= rights;
grant_table->cols= col_rights;
}
@@ -3469,6 +4333,7 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
int old_row_exists= 1;
int error=0;
ulong store_proc_rights;
+ HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash;
DBUG_ENTER("replace_routine_table");
if (!initialized)
@@ -3477,6 +4342,12 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
DBUG_RETURN(-1);
}
+ if (revoke_grant && !grant_name->init_privs) // only inherited role privs
+ {
+ my_hash_delete(hash, (uchar*) grant_name);
+ DBUG_RETURN(0);
+ }
+
get_grantor(thd, grantor);
/*
New users are created before this function is called.
@@ -3506,6 +4377,9 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
The following should never happen as we first check the in memory
grant tables for the user. There is however always a small change that
the user has modified the grant tables directly.
+
+ Also, there is also a second posibility that this routine entry
+ is created for a role by being inherited from a granted role.
*/
if (revoke_grant)
{ // no row, no revoke
@@ -3560,12 +4434,12 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
if (rights)
{
+ grant_name->init_privs= rights;
grant_name->privs= rights;
}
else
{
- my_hash_delete(is_proc ? &proc_priv_hash : &func_priv_hash,(uchar*)
- grant_name);
+ my_hash_delete(hash, (uchar*) grant_name);
}
DBUG_RETURN(0);
@@ -3576,6 +4450,886 @@ table_error:
}
+/*****************************************************************
+ Role privilege propagation and graph traversal functionality
+
+ According to the SQL standard, a role can be granted to a role,
+ thus role grants can create an arbitrarily complex directed acyclic
+ graph (a standard explicitly specifies that cycles are not allowed).
+
+ When a privilege is granted to a role, it becomes available to all grantees.
+ The code below recursively traverses a DAG of role grants, propagating
+ privilege changes.
+
+ The traversal function can work both ways, from roles to grantees or
+ from grantees to roles. The first is used for privilege propagation,
+ the second - for SHOW GRANTS and I_S.APPLICABLE_ROLES
+
+ The role propagation code is smart enough to propagate only privilege
+ changes to one specific database, table, or routine, if only they
+ were changed (like in GRANT ... ON ... TO ...) or it can propagate
+ everything (on startup or after FLUSH PRIVILEGES).
+
+ It traverses only a subgraph that's accessible from the modified role,
+ only visiting roles that can be possibly affected by the GRANT statement.
+
+ Additionally, it stops traversal early, if this particular GRANT statement
+ didn't result in any changes of privileges (e.g. both role1 and role2
+ are granted to the role3, both role1 and role2 have SELECT privilege.
+ if SELECT is revoked from role1 it won't change role3 privileges,
+ so we won't traverse from role3 to its grantees).
+******************************************************************/
+struct PRIVS_TO_MERGE
+{
+ enum what { ALL, GLOBAL, DB, TABLE_COLUMN, PROC, FUNC } what;
+ const char *db, *name;
+};
+
+static int init_role_for_merging(ACL_ROLE *role, void *context)
+{
+ role->counter= 0;
+ return 0;
+}
+
+static int count_subgraph_nodes(ACL_ROLE *role, ACL_ROLE *grantee, void *context)
+{
+ grantee->counter++;
+ return 0;
+}
+
+static int merge_role_privileges(ACL_ROLE *, ACL_ROLE *, void *);
+
+/**
+ rebuild privileges of all affected roles
+
+ entry point into role privilege propagation. after privileges of the
+ 'role' were changed, this function rebuilds privileges of all affected roles
+ as necessary.
+*/
+static void propagate_role_grants(ACL_ROLE *role,
+ enum PRIVS_TO_MERGE::what what,
+ const char *db, const char *name)
+{
+
+ mysql_mutex_assert_owner(&acl_cache->lock);
+ PRIVS_TO_MERGE data= { what, db, name };
+
+ /*
+ Changing privileges of a role causes all other roles that had
+ this role granted to them to have their rights invalidated.
+
+ We need to rebuild all roles' related access bits.
+
+ This cannot be a simple depth-first search, instead we have to merge
+ privieges for all roles granted to a specific grantee, *before*
+ merging privileges for this grantee. In other words, we must visit all
+ parent nodes of a specific node, before descencing into this node.
+
+ For example, if role1 is granted to role2 and role3, and role3 is
+ granted to role2, after "GRANT ... role1", we cannot merge privileges
+ for role2, until role3 is merged. The counter will be 0 for role1, 2
+ for role2, 1 for role3. Traversal will start from role1, go to role2,
+ decrement the counter, backtrack, go to role3, merge it, go to role2
+ again, merge it.
+
+ And the counter is not just "all parent nodes", but only parent nodes
+ that are part of the subgraph we're interested in. For example, if
+ both roleA and roleB are granted to roleC, then roleC has two parent
+ nodes. But when granting a privilege to roleA, we're only looking at a
+ subgraph that includes roleA and roleC (roleB cannot be possibly
+ affected by that grant statement). In this subgraph roleC has only one
+ parent.
+
+ (on the other hand, in acl_load we want to update all roles, and
+ the counter is exactly equal to the number of all parent nodes)
+
+ Thus, we do two graph traversals here. First we only count parents
+ that are part of the subgraph. On the second traversal we decrement
+ the counter and actually merge privileges for a node when a counter
+ drops to zero.
+ */
+ traverse_role_graph_up(role, &data, init_role_for_merging, count_subgraph_nodes);
+ traverse_role_graph_up(role, &data, NULL, merge_role_privileges);
+}
+
+
+// State of a node during a Depth First Search exploration
+struct NODE_STATE
+{
+ ACL_USER_BASE *node_data; /* pointer to the node data */
+ uint neigh_idx; /* the neighbour that needs to be evaluated next */
+};
+
+/**
+ Traverse the role grant graph and invoke callbacks at the specified points.
+
+ @param user user or role to start traversal from
+ @param context opaque parameter to pass to callbacks
+ @param offset offset to ACL_ROLE::parent_grantee or to
+ ACL_USER_BASE::role_grants. Depending on this value,
+ traversal will go from roles to grantees or from
+ grantees to roles.
+ @param on_node called when a node is visited for the first time.
+ Returning a value <0 will abort the traversal.
+ @param on_edge called for every edge in the graph, when traversal
+ goes from a node to a neighbour node.
+ Returning <0 will abort the traversal. Returning >0
+ will make the traversal not to follow this edge.
+
+ @note
+ The traverse method is a DEPTH FIRST SEARCH, but callbacks can influence
+ that (on_edge returning >0 value).
+
+ @note
+ This function should not be called directly, use
+ traverse_role_graph_up() and traverse_role_graph_down() instead.
+
+ @retval 0 traversal finished successfully
+ @retval ROLE_CYCLE_FOUND traversal aborted, cycle detected
+ @retval <0 traversal was aborted, because a callback returned
+ this error code
+*/
+static int traverse_role_graph_impl(ACL_USER_BASE *user, void *context,
+ off_t offset,
+ int (*on_node) (ACL_USER_BASE *role, void *context),
+ int (*on_edge) (ACL_USER_BASE *current, ACL_ROLE *neighbour, void *context))
+{
+ DBUG_ENTER("traverse_role_graph_impl");
+ DBUG_ASSERT(user);
+ DBUG_PRINT("enter",("role: '%s'", user->user.str));
+ /*
+ The search operation should always leave the ROLE_ON_STACK and
+ ROLE_EXPLORED flags clean for all nodes involved in the search
+ */
+ DBUG_ASSERT(!(user->flags & ROLE_ON_STACK));
+ DBUG_ASSERT(!(user->flags & ROLE_EXPLORED));
+ mysql_mutex_assert_owner(&acl_cache->lock);
+
+ /*
+ Stack used to simulate the recursive calls of DFS.
+ It uses a Dynamic_array to reduce the number of
+ malloc calls to a minimum
+ */
+ Dynamic_array<NODE_STATE> stack(20,50);
+ Dynamic_array<ACL_USER_BASE *> to_clear(20,50);
+ NODE_STATE state; /* variable used to insert elements in the stack */
+ int result= 0;
+
+ state.neigh_idx= 0;
+ state.node_data= user;
+ user->flags|= ROLE_ON_STACK;
+
+ stack.push(state);
+ to_clear.push(user);
+
+ user->flags|= ROLE_OPENED;
+ if (on_node && ((result= on_node(user, context)) < 0))
+ goto end;
+
+ while (stack.elements())
+ {
+ NODE_STATE *curr_state= stack.back();
+
+ DBUG_ASSERT(curr_state->node_data->flags & ROLE_ON_STACK);
+
+ ACL_USER_BASE *current= curr_state->node_data;
+ ACL_USER_BASE *neighbour= NULL;
+ DBUG_PRINT("info", ("Examining role %s", current->user.str));
+ /*
+ Iterate through the neighbours until a first valid jump-to
+ neighbour is found
+ */
+ my_bool found= FALSE;
+ uint i;
+ DYNAMIC_ARRAY *array= (DYNAMIC_ARRAY *)(((char*)current) + offset);
+
+ DBUG_ASSERT(array == &current->role_grants || current->flags & IS_ROLE);
+ for (i= curr_state->neigh_idx; i < array->elements; i++)
+ {
+ neighbour= *(dynamic_element(array, i, ACL_ROLE**));
+ if (!(neighbour->flags & IS_ROLE))
+ continue;
+
+ DBUG_PRINT("info", ("Examining neighbour role %s", neighbour->user.str));
+
+ /* check if it forms a cycle */
+ if (neighbour->flags & ROLE_ON_STACK)
+ {
+ DBUG_PRINT("info", ("Found cycle"));
+ result= ROLE_CYCLE_FOUND;
+ goto end;
+ }
+
+ if (!(neighbour->flags & ROLE_OPENED))
+ {
+ neighbour->flags|= ROLE_OPENED;
+ to_clear.push(neighbour);
+ if (on_node && ((result= on_node(neighbour, context)) < 0))
+ goto end;
+ }
+
+ if (on_edge)
+ {
+ result= on_edge(current, (ACL_ROLE*)neighbour, context);
+ if (result < 0)
+ goto end;
+ if (result > 0)
+ continue;
+ }
+
+ /* Check if it was already explored, in that case, move on */
+ if (neighbour->flags & ROLE_EXPLORED)
+ continue;
+
+ found= TRUE;
+ break;
+ }
+
+ /* found states that we have found a node to jump next into */
+ if (found)
+ {
+ curr_state->neigh_idx= i + 1;
+
+ /* some sanity checks */
+ DBUG_ASSERT(!(neighbour->flags & ROLE_ON_STACK));
+
+ /* add the neighbour on the stack */
+ neighbour->flags|= ROLE_ON_STACK;
+ state.neigh_idx= 0;
+ state.node_data= neighbour;
+ stack.push(state);
+ }
+ else
+ {
+ /* Make sure we got a correct node */
+ DBUG_ASSERT(curr_state->node_data->flags & ROLE_ON_STACK);
+ /* Finished with exploring the current node, pop it off the stack */
+ curr_state= &stack.pop();
+ curr_state->node_data->flags&= ~ROLE_ON_STACK; /* clear the on-stack bit */
+ curr_state->node_data->flags|= ROLE_EXPLORED;
+ }
+ }
+
+end:
+ /* Cleanup */
+ for (uint i= 0; i < to_clear.elements(); i++)
+ {
+ ACL_USER_BASE *current= to_clear.at(i);
+ DBUG_ASSERT(current->flags & (ROLE_EXPLORED | ROLE_ON_STACK | ROLE_OPENED));
+ current->flags&= ~(ROLE_EXPLORED | ROLE_ON_STACK | ROLE_OPENED);
+ }
+ DBUG_RETURN(result);
+}
+
+/**
+ Traverse the role grant graph, going from a role to its grantees.
+
+ This is used to propagate changes in privileges, for example,
+ when GRANT or REVOKE is issued for a role.
+*/
+
+static int traverse_role_graph_up(ACL_ROLE *role, void *context,
+ int (*on_node) (ACL_ROLE *role, void *context),
+ int (*on_edge) (ACL_ROLE *current, ACL_ROLE *neighbour, void *context))
+{
+ return traverse_role_graph_impl(role, context,
+ my_offsetof(ACL_ROLE, parent_grantee),
+ (int (*)(ACL_USER_BASE *, void *))on_node,
+ (int (*)(ACL_USER_BASE *, ACL_ROLE *, void *))on_edge);
+}
+
+/**
+ Traverse the role grant graph, going from a user or a role to granted roles.
+
+ This is used, for example, to print all grants available to a user or a role
+ (as in SHOW GRANTS).
+*/
+
+static int traverse_role_graph_down(ACL_USER_BASE *user, void *context,
+ int (*on_node) (ACL_USER_BASE *role, void *context),
+ int (*on_edge) (ACL_USER_BASE *current, ACL_ROLE *neighbour, void *context))
+{
+ return traverse_role_graph_impl(user, context,
+ my_offsetof(ACL_USER_BASE, role_grants),
+ on_node, on_edge);
+}
+
+/*
+ To find all db/table/routine privilege for a specific role
+ we need to scan the array of privileges. It can be big.
+ But the set of privileges granted to a role in question (or
+ to roles directly granted to the role in question) is supposedly
+ much smaller.
+
+ We put a role and all roles directly granted to it in a hash, and iterate
+ the (suposedly long) array of privileges, filtering out "interesting"
+ entries using the role hash. We put all these "interesting"
+ entries in a (suposedly small) dynamic array and them use it for merging.
+*/
+static uchar* role_key(const ACL_ROLE *role, size_t *klen, my_bool)
+{
+ *klen= role->user.length;
+ return (uchar*) role->user.str;
+}
+typedef Hash_set<ACL_ROLE> role_hash_t;
+
+static bool merge_role_global_privileges(ACL_ROLE *grantee)
+{
+ ulong old= grantee->access;
+ grantee->access= grantee->initial_role_access;
+
+ DBUG_EXECUTE_IF("role_merge_stats", role_global_merges++;);
+
+ for (uint i= 0; i < grantee->role_grants.elements; i++)
+ {
+ ACL_ROLE *r= *dynamic_element(&grantee->role_grants, i, ACL_ROLE**);
+ grantee->access|= r->access;
+ }
+ return old != grantee->access;
+}
+
+static int db_name_sort(ACL_DB * const *db1, ACL_DB * const *db2)
+{
+ return strcmp((*db1)->db, (*db2)->db);
+}
+
+/**
+ update ACL_DB for given database and a given role with merged privileges
+
+ @param merged ACL_DB of the role in question (or NULL if it wasn't found)
+ @param first first ACL_DB in an array for the database in question
+ @param access new privileges for the given role on the gived database
+ @param role the name of the given role
+
+ @return a bitmap of
+ 1 - privileges were changed
+ 2 - ACL_DB was added
+ 4 - ACL_DB was deleted
+*/
+static int update_role_db(ACL_DB *merged, ACL_DB **first, ulong access, char *role)
+{
+ if (!first)
+ return 0;
+
+ DBUG_EXECUTE_IF("role_merge_stats", role_db_merges++;);
+
+ if (merged == NULL)
+ {
+ /*
+ there's no ACL_DB for this role (all db grants come from granted roles)
+ we need to create it
+
+ Note that we cannot use acl_insert_db() now:
+ 1. it'll sort elements in the acl_dbs, so the pointers will become invalid
+ 2. we may need many of them, no need to sort every time
+ */
+ DBUG_ASSERT(access);
+ ACL_DB acl_db;
+ acl_db.user= role;
+ acl_db.host.hostname= const_cast<char*>("");
+ acl_db.host.ip= acl_db.host.ip_mask= 0;
+ acl_db.db= first[0]->db;
+ acl_db.access= access;
+ acl_db.initial_access= 0;
+ acl_db.sort=get_sort(3, "", acl_db.db, role);
+ push_dynamic(&acl_dbs,(uchar*) &acl_db);
+ return 2;
+ }
+ else if (access == 0)
+ {
+ /*
+ there is ACL_DB but the role has no db privileges granted
+ (all privileges were coming from granted roles, and now those roles
+ were dropped or had their privileges revoked).
+ we need to remove this ACL_DB entry
+
+ Note, that we cannot delete now:
+ 1. it'll shift elements in the acl_dbs, so the pointers will become invalid
+ 2. it's O(N) operation, and we may need many of them
+ so we only mark elements deleted and will delete later.
+ */
+ merged->sort= 0; // lower than any valid ACL_DB sort value, will be sorted last
+ return 4;
+ }
+ else if (merged->access != access)
+ {
+ /* this is easy */
+ merged->access= access;
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ merges db privileges from roles granted to the role 'grantee'.
+
+ @return true if database privileges of the 'grantee' were changed
+
+*/
+static bool merge_role_db_privileges(ACL_ROLE *grantee, const char *dbname,
+ role_hash_t *rhash)
+{
+ Dynamic_array<ACL_DB *> dbs;
+
+ /*
+ Supposedly acl_dbs can be huge, but only a handful of db grants
+ apply to grantee or roles directly granted to grantee.
+
+ Collect these applicable db grants.
+ */
+ for (uint i=0 ; i < acl_dbs.elements ; i++)
+ {
+ ACL_DB *db= dynamic_element(&acl_dbs,i,ACL_DB*);
+ if (db->host.hostname[0])
+ continue;
+ if (dbname && strcmp(db->db, dbname))
+ continue;
+ ACL_ROLE *r= rhash->find(db->user, strlen(db->user));
+ if (!r)
+ continue;
+ dbs.append(db);
+ }
+ dbs.sort(db_name_sort);
+
+ /*
+ Because dbs array is sorted by the db name, all grants for the same db
+ (that should be merged) are sorted together. The grantee's ACL_DB element
+ is not necessarily the first and may be not present at all.
+ */
+ ACL_DB **first= NULL, *UNINIT_VAR(merged);
+ ulong UNINIT_VAR(access), update_flags= 0;
+ for (ACL_DB **cur= dbs.front(); cur <= dbs.back(); cur++)
+ {
+ if (!first || (!dbname && strcmp(cur[0]->db, cur[-1]->db)))
+ { // new db name series
+ update_flags|= update_role_db(merged, first, access, grantee->user.str);
+ merged= NULL;
+ access= 0;
+ first= cur;
+ }
+ if (strcmp(cur[0]->user, grantee->user.str) == 0)
+ access|= (merged= cur[0])->initial_access;
+ else
+ access|= cur[0]->access;
+ }
+ update_flags|= update_role_db(merged, first, access, grantee->user.str);
+
+ /*
+ to make this code a bit simpler, we sort on deletes, to move
+ deleted elements to the end of the array. strictly speaking it's
+ unnecessary, it'd be faster to remove them in one O(N) array scan.
+
+ on the other hand, qsort on almost sorted array is pretty fast anyway...
+ */
+ if (update_flags & (2|4))
+ { // inserted or deleted, need to sort
+ my_qsort((uchar*) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements,
+ sizeof(ACL_DB),(qsort_cmp) acl_compare);
+ }
+ if (update_flags & 4)
+ { // deleted, trim the end
+ while (acl_dbs.elements &&
+ dynamic_element(&acl_dbs, acl_dbs.elements-1, ACL_DB*)->sort == 0)
+ acl_dbs.elements--;
+ }
+ return update_flags;
+}
+
+static int table_name_sort(GRANT_TABLE * const *tbl1, GRANT_TABLE * const *tbl2)
+{
+ int res = strcmp((*tbl1)->db, (*tbl2)->db);
+ if (res) return res;
+ return strcmp((*tbl1)->tname, (*tbl2)->tname);
+}
+
+/**
+ merges column privileges for the entry 'merged'
+
+ @param merged GRANT_TABLE to merge the privileges into
+ @param cur first entry in the array of GRANT_TABLE's for a given table
+ @param last last entry in the array of GRANT_TABLE's for a given table,
+ all entries between cur and last correspond to the *same* table
+
+ @return 1 if the _set of columns_ in 'merged' was changed
+ (not if the _set of privileges_ was changed).
+*/
+static int update_role_columns(GRANT_TABLE *merged,
+ GRANT_TABLE **cur, GRANT_TABLE **last)
+
+{
+ ulong rights __attribute__((unused))= 0;
+ int changed= 0;
+ if (!merged->cols)
+ {
+ changed= merged->hash_columns.records > 0;
+ my_hash_reset(&merged->hash_columns);
+ return changed;
+ }
+
+ DBUG_EXECUTE_IF("role_merge_stats", role_column_merges++;);
+
+ HASH *mh= &merged->hash_columns;
+ for (uint i=0 ; i < mh->records ; i++)
+ {
+ GRANT_COLUMN *col = (GRANT_COLUMN *)my_hash_element(mh, i);
+ col->rights= col->init_rights;
+ }
+
+ for (; cur < last; cur++)
+ {
+ if (*cur == merged)
+ continue;
+ HASH *ch= &cur[0]->hash_columns;
+ for (uint i=0 ; i < ch->records ; i++)
+ {
+ GRANT_COLUMN *ccol = (GRANT_COLUMN *)my_hash_element(ch, i);
+ GRANT_COLUMN *mcol = (GRANT_COLUMN *)my_hash_search(mh,
+ (uchar *)ccol->column, ccol->key_length);
+ if (mcol)
+ mcol->rights|= ccol->rights;
+ else
+ {
+ changed= 1;
+ my_hash_insert(mh, (uchar*)new (&memex) GRANT_COLUMN(ccol));
+ }
+ }
+ }
+
+ for (uint i=0 ; i < mh->records ; i++)
+ {
+ GRANT_COLUMN *col = (GRANT_COLUMN *)my_hash_element(mh, i);
+ rights|= col->rights;
+ if (!col->rights)
+ {
+ changed= 1;
+ my_hash_delete(mh, (uchar*)col);
+ }
+ }
+ DBUG_ASSERT(rights == merged->cols);
+ return changed;
+}
+
+/**
+ update GRANT_TABLE for a given table and a given role with merged privileges
+
+ @param merged GRANT_TABLE of the role in question (or NULL if it wasn't found)
+ @param first first GRANT_TABLE in an array for the table in question
+ @param last last entry in the array of GRANT_TABLE's for a given table,
+ all entries between first and last correspond to the *same* table
+ @param privs new table-level privileges for 'merged'
+ @param cols new OR-ed column-level privileges for 'merged'
+ @param role the name of the given role
+
+ @return a bitmap of
+ 1 - privileges were changed
+ 2 - GRANT_TABLE was added
+ 4 - GRANT_TABLE was deleted
+*/
+static int update_role_table_columns(GRANT_TABLE *merged,
+ GRANT_TABLE **first, GRANT_TABLE **last,
+ ulong privs, ulong cols, char *role)
+{
+ if (!first)
+ return 0;
+
+ DBUG_EXECUTE_IF("role_merge_stats", role_table_merges++;);
+
+ if (merged == NULL)
+ {
+ /*
+ there's no GRANT_TABLE for this role (all table grants come from granted
+ roles) we need to create it
+ */
+ DBUG_ASSERT(privs | cols);
+ merged= new (&memex) GRANT_TABLE("", first[0]->db, role, first[0]->tname,
+ privs, cols);
+ merged->init_privs= merged->init_cols= 0;
+ update_role_columns(merged, first, last);
+ my_hash_insert(&column_priv_hash,(uchar*) merged);
+ return 2;
+ }
+ else if ((privs | cols) == 0)
+ {
+ /*
+ there is GRANT_TABLE object but the role has no table or column
+ privileges granted (all privileges were coming from granted roles, and
+ now those roles were dropped or had their privileges revoked).
+ we need to remove this GRANT_TABLE
+ */
+ DBUG_EXECUTE_IF("role_merge_stats", role_column_merges+= test(merged->cols););
+ my_hash_delete(&column_priv_hash,(uchar*) merged);
+ return 4;
+ }
+ else
+ {
+ bool changed= merged->cols != cols || merged->privs != privs;
+ merged->cols= cols;
+ merged->privs= privs;
+ if (update_role_columns(merged, first, last))
+ changed= true;
+ return changed;
+ }
+}
+
+/**
+ merges table privileges from roles granted to the role 'grantee'.
+
+ @return true if table privileges of the 'grantee' were changed
+
+*/
+static bool merge_role_table_and_column_privileges(ACL_ROLE *grantee,
+ const char *db, const char *tname, role_hash_t *rhash)
+{
+ Dynamic_array<GRANT_TABLE *> grants;
+ DBUG_ASSERT(test(db) == test(tname)); // both must be set, or neither
+
+ /*
+ first, collect table/column privileges granted to
+ roles in question.
+ */
+ for (uint i=0 ; i < column_priv_hash.records ; i++)
+ {
+ GRANT_TABLE *grant= (GRANT_TABLE *) my_hash_element(&column_priv_hash, i);
+ if (grant->host.hostname[0])
+ continue;
+ if (tname && (strcmp(grant->db, db) || strcmp(grant->tname, tname)))
+ continue;
+ ACL_ROLE *r= rhash->find(grant->user, strlen(grant->user));
+ if (!r)
+ continue;
+ grants.append(grant);
+ }
+ grants.sort(table_name_sort);
+
+ GRANT_TABLE **first= NULL, *UNINIT_VAR(merged), **cur;
+ ulong UNINIT_VAR(privs), UNINIT_VAR(cols), update_flags= 0;
+ for (cur= grants.front(); cur <= grants.back(); cur++)
+ {
+ if (!first ||
+ (!tname && (strcmp(cur[0]->db, cur[-1]->db) ||
+ strcmp(cur[0]->tname, cur[-1]->tname))))
+ { // new db.tname series
+ update_flags|= update_role_table_columns(merged, first, cur,
+ privs, cols, grantee->user.str);
+ merged= NULL;
+ privs= cols= 0;
+ first= cur;
+ }
+ if (strcmp(cur[0]->user, grantee->user.str) == 0)
+ {
+ merged= cur[0];
+ cols|= cur[0]->init_cols;
+ privs|= cur[0]->init_privs;
+ }
+ else
+ {
+ cols|= cur[0]->cols;
+ privs|= cur[0]->privs;
+ }
+ }
+ update_flags|= update_role_table_columns(merged, first, cur,
+ privs, cols, grantee->user.str);
+
+ return update_flags;
+}
+
+static int routine_name_sort(GRANT_NAME * const *r1, GRANT_NAME * const *r2)
+{
+ int res= strcmp((*r1)->db, (*r2)->db);
+ if (res) return res;
+ return strcmp((*r1)->tname, (*r2)->tname);
+}
+
+/**
+ update GRANT_NAME for a given routine and a given role with merged privileges
+
+ @param merged GRANT_NAME of the role in question (or NULL if it wasn't found)
+ @param first first GRANT_NAME in an array for the routine in question
+ @param privs new routine-level privileges for 'merged'
+ @param role the name of the given role
+ @param hash proc_priv_hash or func_priv_hash
+
+ @return a bitmap of
+ 1 - privileges were changed
+ 2 - GRANT_NAME was added
+ 4 - GRANT_NAME was deleted
+*/
+static int update_role_routines(GRANT_NAME *merged, GRANT_NAME **first,
+ ulong privs, char *role, HASH *hash)
+{
+ if (!first)
+ return 0;
+
+ if (merged == NULL)
+ {
+ /*
+ there's no GRANT_NAME for this role (all routine grants come from granted
+ roles) we need to create it
+ */
+ DBUG_ASSERT(privs);
+ merged= new (&memex) GRANT_NAME("", first[0]->db, role, first[0]->tname,
+ privs, true);
+ merged->init_privs= 0; // all privs are inherited
+ my_hash_insert(hash, (uchar *)merged);
+ return 2;
+ }
+ else if (privs == 0)
+ {
+ /*
+ there is GRANT_NAME but the role has no privileges granted
+ (all privileges were coming from granted roles, and now those roles
+ were dropped or had their privileges revoked).
+ we need to remove this entry
+ */
+ my_hash_delete(hash, (uchar*)merged);
+ return 4;
+ }
+ else if (merged->privs != privs)
+ {
+ /* this is easy */
+ merged->privs= privs;
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ merges routine privileges from roles granted to the role 'grantee'.
+
+ @return true if routine privileges of the 'grantee' were changed
+
+*/
+static bool merge_role_routine_grant_privileges(ACL_ROLE *grantee,
+ const char *db, const char *tname, role_hash_t *rhash, HASH *hash)
+{
+ ulong update_flags= 0;
+
+ DBUG_ASSERT(test(db) == test(tname)); // both must be set, or neither
+
+ DBUG_EXECUTE_IF("role_merge_stats", role_routine_merges++;);
+
+ Dynamic_array<GRANT_NAME *> grants;
+
+ /* first, collect routine privileges granted to roles in question */
+ for (uint i=0 ; i < hash->records ; i++)
+ {
+ GRANT_NAME *grant= (GRANT_NAME *) my_hash_element(hash, i);
+ if (grant->host.hostname[0])
+ continue;
+ if (tname && (strcmp(grant->db, db) || strcmp(grant->tname, tname)))
+ continue;
+ ACL_ROLE *r= rhash->find(grant->user, strlen(grant->user));
+ if (!r)
+ continue;
+ grants.append(grant);
+ }
+ grants.sort(routine_name_sort);
+
+ GRANT_NAME **first= NULL, *UNINIT_VAR(merged);
+ ulong UNINIT_VAR(privs);
+ for (GRANT_NAME **cur= grants.front(); cur <= grants.back(); cur++)
+ {
+ if (!first ||
+ (!tname && (strcmp(cur[0]->db, cur[-1]->db) ||
+ strcmp(cur[0]->tname, cur[-1]->tname))))
+ { // new db.tname series
+ update_flags|= update_role_routines(merged, first, privs,
+ grantee->user.str, hash);
+ merged= NULL;
+ privs= 0;
+ first= cur;
+ }
+ if (strcmp(cur[0]->user, grantee->user.str) == 0)
+ {
+ merged= cur[0];
+ privs|= cur[0]->init_privs;
+ }
+ else
+ {
+ privs|= cur[0]->privs;
+ }
+ }
+ update_flags|= update_role_routines(merged, first, privs,
+ grantee->user.str, hash);
+ return update_flags;
+}
+
+/**
+ update privileges of the 'grantee' from all roles, granted to it
+*/
+static int merge_role_privileges(ACL_ROLE *role __attribute__((unused)),
+ ACL_ROLE *grantee, void *context)
+{
+ PRIVS_TO_MERGE *data= (PRIVS_TO_MERGE *)context;
+
+ if (--grantee->counter)
+ return 1; // don't recurse into grantee just yet
+
+ /* if we'll do db/table/routine privileges, create a hash of role names */
+ role_hash_t role_hash(role_key);
+ if (data->what != PRIVS_TO_MERGE::GLOBAL)
+ {
+ role_hash.insert(grantee);
+ for (uint i= 0; i < grantee->role_grants.elements; i++)
+ role_hash.insert(*dynamic_element(&grantee->role_grants, i, ACL_ROLE**));
+ }
+
+ bool all= data->what == PRIVS_TO_MERGE::ALL;
+ bool changed= false;
+ if (all || data->what == PRIVS_TO_MERGE::GLOBAL)
+ changed|= merge_role_global_privileges(grantee);
+ if (all || data->what == PRIVS_TO_MERGE::DB)
+ changed|= merge_role_db_privileges(grantee, data->db, &role_hash);
+ if (all || data->what == PRIVS_TO_MERGE::TABLE_COLUMN)
+ changed|= merge_role_table_and_column_privileges(grantee,
+ data->db, data->name, &role_hash);
+ if (all || data->what == PRIVS_TO_MERGE::PROC)
+ changed|= merge_role_routine_grant_privileges(grantee,
+ data->db, data->name, &role_hash, &proc_priv_hash);
+ if (all || data->what == PRIVS_TO_MERGE::FUNC)
+ changed|= merge_role_routine_grant_privileges(grantee,
+ data->db, data->name, &role_hash, &func_priv_hash);
+
+ return !changed; // don't recurse into the subgraph if privs didn't change
+}
+
+/*****************************************************************
+ End of the role privilege propagation and graph traversal code
+******************************************************************/
+
+bool copy_and_check_auth(LEX_USER *to, LEX_USER *from, LEX *lex)
+{
+ if (to != from)
+ {
+ /* preserve authentication information, if LEX_USER was reallocated */
+ to->password= from->password;
+ to->plugin= from->plugin;
+ to->auth= from->auth;
+ }
+
+ /*
+ Note, that no password is null_lex_str, while no plugin is empty_lex_str.
+ See sql_yacc.yy
+ */
+ bool has_auth= to->password.str || to->plugin.length || to->auth.length ||
+ lex->ssl_type != SSL_TYPE_NOT_SPECIFIED || lex->ssl_cipher ||
+ lex->x509_issuer || lex->x509_subject ||
+ lex->mqh.specified_limits;
+
+ /*
+ Specifying authentication clauses forces the name to be interpreted
+ as a user, not a role. See also check_change_password()
+ */
+ if (to->is_role() && has_auth)
+ {
+ my_error(ER_PASSWORD_NO_MATCH, MYF(0));
+ return true;
+ }
+
+ return false;
+}
+
+
/*
Store table level and column level grants in the privilege tables
@@ -3711,7 +5465,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
}
#endif
- /*
+ /*
The lock api is depending on the thd->lex variable which needs to be
re-initialized.
*/
@@ -3742,16 +5496,19 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
{
int error;
GRANT_TABLE *grant_table;
- if (!(Str= get_current_user(thd, tmp_Str)))
+ if (!(Str= get_current_user(thd, tmp_Str, false)))
{
result= TRUE;
continue;
- }
+ }
/* Create user if needed */
- error=replace_user_table(thd, tables[0].table, *Str,
- 0, revoke_grant, create_new_users,
- test(thd->variables.sql_mode &
- MODE_NO_AUTO_CREATE_USER));
+ if (copy_and_check_auth(Str, tmp_Str, thd->lex))
+ error= -1;
+ else
+ error=replace_user_table(thd, tables[0].table, *Str,
+ 0, revoke_grant, create_new_users,
+ test(thd->variables.sql_mode &
+ MODE_NO_AUTO_CREATE_USER));
if (error)
{
result= TRUE; // Remember error
@@ -3828,15 +5585,17 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list,
}
else if (tables[2].table)
{
- if ((replace_column_table(grant_table, tables[2].table, *Str,
- columns,
- db_name, table_name,
- rights, revoke_grant)))
+ if (replace_column_table(grant_table, tables[2].table, *Str, columns,
+ db_name, table_name, rights, revoke_grant))
{
result= TRUE;
}
}
+ if (Str->is_role())
+ propagate_role_grants(find_acl_role(Str->user.str),
+ PRIVS_TO_MERGE::TABLE_COLUMN, db_name, table_name);
}
+
thd->mem_root= old_root;
mysql_mutex_unlock(&acl_cache->lock);
@@ -3948,11 +5707,11 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
{
int error;
GRANT_NAME *grant_name;
- if (!(Str= get_current_user(thd, tmp_Str)))
+ if (!(Str= get_current_user(thd, tmp_Str, false)))
{
result= TRUE;
continue;
- }
+ }
/* Create user if needed */
error=replace_user_table(thd, tables[0].table, *Str,
0, revoke_grant, create_new_users,
@@ -3968,7 +5727,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
table_name= table_list->table_name;
grant_name= routine_hash_search(Str->host.str, NullS, db_name,
Str->user.str, table_name, is_proc, 1);
- if (!grant_name)
+ if (!grant_name || !grant_name->init_privs)
{
if (revoke_grant)
{
@@ -3990,12 +5749,16 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
}
if (replace_routine_table(thd, grant_name, tables[1].table, *Str,
- db_name, table_name, is_proc, rights,
+ db_name, table_name, is_proc, rights,
revoke_grant) != 0)
{
result= TRUE;
continue;
}
+ if (Str->is_role())
+ propagate_role_grants(find_acl_role(Str->user.str),
+ is_proc ? PRIVS_TO_MERGE::PROC : PRIVS_TO_MERGE::FUNC,
+ db_name, table_name);
}
thd->mem_root= old_root;
mysql_mutex_unlock(&acl_cache->lock);
@@ -4012,6 +5775,277 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
DBUG_RETURN(result);
}
+static void append_user(String *str, const char *u, const char *h)
+{
+ if (str->length())
+ str->append(',');
+ str->append('\'');
+ str->append(u);
+ /* hostname part is not relevant for roles, it is always empty */
+ if (*h)
+ {
+ str->append(STRING_WITH_LEN("'@'"));
+ str->append(h);
+ }
+ str->append('\'');
+}
+
+static int can_grant_role_callback(ACL_USER_BASE *grantee,
+ ACL_ROLE *role, void *data)
+{
+ ROLE_GRANT_PAIR *pair;
+
+ if (role != (ACL_ROLE*)data)
+ return 0; // keep searching
+
+ if (grantee->flags & IS_ROLE)
+ pair= find_role_grant_pair(&grantee->user, &empty_lex_str, &role->user);
+ else
+ {
+ ACL_USER *user= (ACL_USER *)grantee;
+ LEX_STRING host= { user->host.hostname, user->hostname_length };
+ pair= find_role_grant_pair(&user->user, &host, &role->user);
+ }
+ if (!pair->with_admin)
+ return 0; // keep searching
+
+ return -1; // abort the traversal
+}
+
+
+/*
+ One can only grant a role if SELECT * FROM I_S.APPLICABLE_ROLES shows this
+ role as grantable.
+
+ What this really means - we need to traverse role graph for the current user
+ looking for our role being granted with the admin option.
+*/
+static bool can_grant_role(THD *thd, ACL_ROLE *role)
+{
+ Security_context *sctx= thd->security_ctx;
+
+ if (!sctx->user) // replication
+ return true;
+
+ ACL_USER *grantee= find_user_exact(sctx->priv_host, sctx->priv_user);
+ if (!grantee)
+ return false;
+
+ return traverse_role_graph_down(grantee, role, NULL,
+ can_grant_role_callback) == -1;
+}
+
+
+bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke)
+{
+ DBUG_ENTER("mysql_grant_role");
+ /*
+ The first entry in the list is the granted role. Need at least two
+ entries for the command to be valid
+ */
+ DBUG_ASSERT(list.elements >= 2);
+ bool result= 0;
+ String wrong_users;
+ LEX_USER *user, *granted_role;
+ LEX_STRING rolename;
+ LEX_STRING username;
+ LEX_STRING hostname;
+ ACL_ROLE *role, *role_as_user;
+
+ List_iterator <LEX_USER> user_list(list);
+ granted_role= user_list++;
+ if (!(granted_role= get_current_user(thd, granted_role)))
+ DBUG_RETURN(TRUE);
+
+ DBUG_ASSERT(granted_role->is_role());
+ rolename= granted_role->user;
+
+ TABLE_LIST tables;
+ tables.init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("roles_mapping"),
+ "roles_mapping", TL_WRITE);
+
+ if (open_and_lock_tables(thd, &tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
+ DBUG_RETURN(TRUE); /* purecov: deadcode */
+
+ mysql_rwlock_wrlock(&LOCK_grant);
+ mysql_mutex_lock(&acl_cache->lock);
+ if (!(role= find_acl_role(rolename.str)))
+ {
+ mysql_mutex_unlock(&acl_cache->lock);
+ mysql_rwlock_unlock(&LOCK_grant);
+ my_error(ER_INVALID_ROLE, MYF(0), rolename.str);
+ DBUG_RETURN(TRUE);
+ }
+
+ if (!can_grant_role(thd, role))
+ {
+ mysql_mutex_unlock(&acl_cache->lock);
+ mysql_rwlock_unlock(&LOCK_grant);
+ my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
+ thd->security_ctx->priv_user, thd->security_ctx->priv_host);
+ DBUG_RETURN(TRUE);
+ }
+
+ while ((user= user_list++))
+ {
+ role_as_user= NULL;
+ /* current_role is treated slightly different */
+ if (user->user.str == current_role.str)
+ {
+ /* current_role is NONE */
+ if (!thd->security_ctx->priv_role[0])
+ {
+ my_error(ER_INVALID_ROLE, MYF(0), "NONE");
+ append_user(&wrong_users, "NONE", "");
+ result= 1;
+ continue;
+ }
+ if (!(role_as_user= find_acl_role(thd->security_ctx->priv_role)))
+ {
+ append_user(&wrong_users, thd->security_ctx->priv_role, "");
+ result= 1;
+ continue;
+ }
+
+ /* can not grant current_role to current_role */
+ if (granted_role->user.str == current_role.str)
+ {
+ append_user(&wrong_users, thd->security_ctx->priv_role, "");
+ result= 1;
+ continue;
+ }
+ username.str= thd->security_ctx->priv_role;
+ username.length= strlen(username.str);
+ hostname= empty_lex_str;
+ }
+ else if (user->user.str == current_user.str)
+ {
+ username.str= thd->security_ctx->priv_user;
+ username.length= strlen(username.str);
+ hostname.str= thd->security_ctx->priv_host;
+ hostname.length= strlen(hostname.str);
+ }
+ else
+ {
+ username= user->user;
+ if (user->host.str)
+ hostname= user->host;
+ else
+ if ((role_as_user= find_acl_role(user->user.str)))
+ hostname= empty_lex_str;
+ else
+ {
+ if (is_invalid_role_name(username.str))
+ {
+ append_user(&wrong_users, username.str, "");
+ result= 1;
+ continue;
+ }
+ hostname= host_not_specified;
+ }
+ }
+
+ ROLE_GRANT_PAIR *hash_entry= find_role_grant_pair(&username, &hostname,
+ &rolename);
+ ACL_USER_BASE *grantee= role_as_user;
+
+ if (!grantee)
+ grantee= find_user_exact(hostname.str, username.str);
+
+ if (!grantee)
+ {
+ append_user(&wrong_users, username.str, hostname.str);
+ result= 1;
+ continue;
+ }
+
+ if (!revoke)
+ {
+ if (hash_entry)
+ {
+ // perhaps, updating an existing grant, adding WITH ADMIN OPTION
+ }
+ else
+ {
+ add_role_user_mapping(grantee, role);
+
+ /*
+ Check if this grant would cause a cycle. It only needs to be run
+ if we're granting a role to a role
+ */
+ if (role_as_user &&
+ traverse_role_graph_down(role, 0, 0, 0) == ROLE_CYCLE_FOUND)
+ {
+ append_user(&wrong_users, username.str, "");
+ result= 1;
+ undo_add_role_user_mapping(grantee, role);
+ continue;
+ }
+ }
+ }
+ else
+ {
+ /* grant was already removed or never existed */
+ if (!hash_entry)
+ {
+ append_user(&wrong_users, username.str, hostname.str);
+ result= 1;
+ continue;
+ }
+ if (thd->lex->with_admin_option)
+ {
+ // only revoking an admin option, not the complete grant
+ }
+ else
+ {
+ /* revoke a role grant */
+ remove_role_user_mapping(grantee, role);
+ }
+ }
+
+ /* write into the roles_mapping table */
+ if (replace_roles_mapping_table(tables.table,
+ &username, &hostname, &rolename,
+ thd->lex->with_admin_option,
+ hash_entry, revoke))
+ {
+ append_user(&wrong_users, username.str, "");
+ result= 1;
+ if (!revoke)
+ {
+ /* need to remove the mapping added previously */
+ undo_add_role_user_mapping(grantee, role);
+ }
+ else
+ {
+ /* need to restore the mapping deleted previously */
+ add_role_user_mapping(grantee, role);
+ }
+ continue;
+ }
+
+ /*
+ Only need to propagate grants when granting/revoking a role to/from
+ a role
+ */
+ if (role_as_user)
+ propagate_role_grants(role_as_user, PRIVS_TO_MERGE::ALL, 0, 0);
+ }
+
+ mysql_mutex_unlock(&acl_cache->lock);
+
+ if (result)
+ my_error(revoke ? ER_CANNOT_REVOKE_ROLE : ER_CANNOT_GRANT_ROLE, MYF(0),
+ rolename.str, wrong_users.c_ptr_safe());
+ else
+ result= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
+
+ mysql_rwlock_unlock(&LOCK_grant);
+
+ DBUG_RETURN(result);
+}
+
bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
ulong rights, bool revoke_grant, bool is_proxy)
@@ -4056,12 +6090,12 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
C_STRING_WITH_LEN("proxies_priv"),
- "proxies_priv",
+ "proxies_priv",
TL_WRITE);
else
tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
- C_STRING_WITH_LEN("db"),
- "db",
+ C_STRING_WITH_LEN("db"),
+ "db",
TL_WRITE);
tables[0].next_local= tables[0].next_global= tables+1;
@@ -4095,23 +6129,25 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
mysql_mutex_lock(&acl_cache->lock);
grant_version++;
+ if (proxied_user)
+ {
+ if (!(proxied_user= get_current_user(thd, proxied_user, false)))
+ DBUG_RETURN(TRUE);
+ DBUG_ASSERT(proxied_user->host.length); // not a Role
+ }
+
int result=0;
while ((tmp_Str = str_list++))
{
- if (!(Str= get_current_user(thd, tmp_Str)))
+ if (!(Str= get_current_user(thd, tmp_Str, false)))
{
result= TRUE;
continue;
}
- /*
- No User, but a password?
- They did GRANT ... TO CURRENT_USER() IDENTIFIED BY ... !
- Get the current user, and shallow-copy the new password to them!
- */
- if (!tmp_Str->user.str && tmp_Str->password.str)
- Str->password= tmp_Str->password;
-
+ if (copy_and_check_auth(Str, tmp_Str, thd->lex))
+ result= -1;
+ else
if (replace_user_table(thd, tables[0].table, *Str,
(!db ? rights : 0), revoke_grant, create_new_users,
test(thd->variables.sql_mode &
@@ -4135,10 +6171,14 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
else if (is_proxy)
{
if (replace_proxies_priv_table (thd, tables[1].table, Str, proxied_user,
- rights & GRANT_ACL ? TRUE : FALSE,
+ rights & GRANT_ACL ? TRUE : FALSE,
revoke_grant))
result= -1;
}
+ if (Str->is_role())
+ propagate_role_grants(find_acl_role(Str->user.str),
+ db ? PRIVS_TO_MERGE::DB : PRIVS_TO_MERGE::GLOBAL,
+ db, 0);
}
mysql_mutex_unlock(&acl_cache->lock);
@@ -4197,105 +6237,6 @@ my_bool grant_init()
/**
- @brief Helper function to grant_reload_procs_priv
-
- Reads the procs_priv table into memory hash.
-
- @param table A pointer to the procs_priv table structure.
-
- @see grant_reload
- @see grant_reload_procs_priv
-
- @return Error state
- @retval TRUE An error occurred
- @retval FALSE Success
-*/
-
-static my_bool grant_load_procs_priv(TABLE *p_table)
-{
- MEM_ROOT *memex_ptr;
- my_bool return_val= 1;
- bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
- MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,
- THR_MALLOC);
- DBUG_ENTER("grant_load_procs_priv");
- (void) my_hash_init(&proc_priv_hash, &my_charset_utf8_bin,
- 0,0,0, (my_hash_get_key) get_grant_table,
- 0,0);
- (void) my_hash_init(&func_priv_hash, &my_charset_utf8_bin,
- 0,0,0, (my_hash_get_key) get_grant_table,
- 0,0);
-
- if (p_table->file->ha_index_init(0, 1))
- DBUG_RETURN(TRUE);
-
- p_table->use_all_columns();
-
- if (!p_table->file->ha_index_first(p_table->record[0]))
- {
- memex_ptr= &memex;
- my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
- do
- {
- GRANT_NAME *mem_check;
- HASH *hash;
- if (!(mem_check=new (memex_ptr) GRANT_NAME(p_table, TRUE)))
- {
- /* This could only happen if we are out memory */
- goto end_unlock;
- }
-
- if (check_no_resolve)
- {
- if (hostname_requires_resolving(mem_check->host.hostname))
- {
- sql_print_warning("'procs_priv' entry '%s %s@%s' "
- "ignored in --skip-name-resolve mode.",
- mem_check->tname, mem_check->user,
- mem_check->host.hostname ?
- mem_check->host.hostname : "");
- continue;
- }
- }
- if (p_table->field[4]->val_int() == TYPE_ENUM_PROCEDURE)
- {
- hash= &proc_priv_hash;
- }
- else
- if (p_table->field[4]->val_int() == TYPE_ENUM_FUNCTION)
- {
- hash= &func_priv_hash;
- }
- else
- {
- sql_print_warning("'procs_priv' entry '%s' "
- "ignored, bad routine type",
- mem_check->tname);
- continue;
- }
-
- mem_check->privs= fix_rights_for_procedure(mem_check->privs);
- if (! mem_check->ok())
- delete mem_check;
- else if (my_hash_insert(hash, (uchar*) mem_check))
- {
- delete mem_check;
- goto end_unlock;
- }
- }
- while (!p_table->file->ha_index_next(p_table->record[0]));
- }
- /* Return ok */
- return_val= 0;
-
-end_unlock:
- p_table->file->ha_index_end();
- my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
- DBUG_RETURN(return_val);
-}
-
-
-/**
@brief Initialize structures responsible for table/column-level privilege
checking and load information about grants from open privilege tables.
@@ -4314,7 +6255,7 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables)
{
MEM_ROOT *memex_ptr;
my_bool return_val= 1;
- TABLE *t_table= 0, *c_table= 0;
+ TABLE *t_table, *c_table, *p_table;
bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE;
MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**,
THR_MALLOC);
@@ -4326,9 +6267,15 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables)
(void) my_hash_init(&column_priv_hash, &my_charset_utf8_bin,
0,0,0, (my_hash_get_key) get_grant_table,
(my_hash_free_key) free_grant_table,0);
+ (void) my_hash_init(&proc_priv_hash, &my_charset_utf8_bin,
+ 0,0,0, (my_hash_get_key) get_grant_table, 0,0);
+ (void) my_hash_init(&func_priv_hash, &my_charset_utf8_bin,
+ 0,0,0, (my_hash_get_key) get_grant_table, 0,0);
+ init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
- t_table = tables[0].table;
- c_table = tables[1].table;
+ t_table= tables[0].table;
+ c_table= tables[1].table;
+ p_table= tables[2].table; // this can be NULL
if (t_table->file->ha_index_init(0, 1))
goto end_index_init;
@@ -4336,10 +6283,11 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables)
t_table->use_all_columns();
c_table->use_all_columns();
+ memex_ptr= &memex;
+ my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
+
if (!t_table->file->ha_index_first(t_table->record[0]))
{
- memex_ptr= &memex;
- my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr);
do
{
GRANT_TABLE *mem_check;
@@ -4356,9 +6304,8 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables)
sql_print_warning("'tables_priv' entry '%s %s@%s' "
"ignored in --skip-name-resolve mode.",
mem_check->tname,
- mem_check->user ? mem_check->user : "",
- mem_check->host.hostname ?
- mem_check->host.hostname : "");
+ safe_str(mem_check->user),
+ safe_str(mem_check->host.hostname));
continue;
}
}
@@ -4374,8 +6321,72 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables)
while (!t_table->file->ha_index_next(t_table->record[0]));
}
- return_val=0; // Return ok
+ return_val= 0;
+ if (p_table)
+ {
+ if (p_table->file->ha_index_init(0, 1))
+ goto end_unlock;
+
+ p_table->use_all_columns();
+
+ if (!p_table->file->ha_index_first(p_table->record[0]))
+ {
+ do
+ {
+ GRANT_NAME *mem_check;
+ HASH *hash;
+ if (!(mem_check=new (memex_ptr) GRANT_NAME(p_table, TRUE)))
+ {
+ /* This could only happen if we are out memory */
+ goto end_unlock_p;
+ }
+
+ if (check_no_resolve)
+ {
+ if (hostname_requires_resolving(mem_check->host.hostname))
+ {
+ sql_print_warning("'procs_priv' entry '%s %s@%s' "
+ "ignored in --skip-name-resolve mode.",
+ mem_check->tname, mem_check->user,
+ safe_str(mem_check->host.hostname));
+ continue;
+ }
+ }
+ if (p_table->field[4]->val_int() == TYPE_ENUM_PROCEDURE)
+ {
+ hash= &proc_priv_hash;
+ }
+ else
+ if (p_table->field[4]->val_int() == TYPE_ENUM_FUNCTION)
+ {
+ hash= &func_priv_hash;
+ }
+ else
+ {
+ sql_print_warning("'procs_priv' entry '%s' "
+ "ignored, bad routine type",
+ mem_check->tname);
+ continue;
+ }
+
+ mem_check->privs= fix_rights_for_procedure(mem_check->privs);
+ mem_check->init_privs= mem_check->privs;
+ if (! mem_check->ok())
+ delete mem_check;
+ else if (my_hash_insert(hash, (uchar*) mem_check))
+ {
+ delete mem_check;
+ goto end_unlock_p;
+ }
+ }
+ while (!p_table->file->ha_index_next(p_table->record[0]));
+ }
+ }
+
+end_unlock_p:
+ if (p_table)
+ p_table->file->ha_index_end();
end_unlock:
t_table->file->ha_index_end();
my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr);
@@ -4385,56 +6396,16 @@ end_index_init:
}
-/**
- @brief Helper function to grant_reload. Reloads procs_priv table is it
- exists.
-
- @param thd A pointer to the thread handler object.
-
- @see grant_reload
-
- @return Error state
- @retval FALSE Success
- @retval TRUE An error has occurred.
-*/
-
-static my_bool grant_reload_procs_priv(THD *thd)
+my_bool role_propagate_grants_action(void *ptr, void *unused __attribute__((unused)))
{
- HASH old_proc_priv_hash, old_func_priv_hash;
- TABLE_LIST table;
- my_bool return_val= FALSE;
- DBUG_ENTER("grant_reload_procs_priv");
-
- table.init_one_table("mysql", 5, "procs_priv",
- strlen("procs_priv"), "procs_priv",
- TL_READ);
- table.open_type= OT_BASE_ONLY;
-
- if (open_and_lock_tables(thd, &table, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
- DBUG_RETURN(TRUE);
-
- mysql_rwlock_wrlock(&LOCK_grant);
- /* Save a copy of the current hash if we need to undo the grant load */
- old_proc_priv_hash= proc_priv_hash;
- old_func_priv_hash= func_priv_hash;
-
- if ((return_val= grant_load_procs_priv(table.table)))
- {
- /* Error; Reverting to old hash */
- DBUG_PRINT("error",("Reverting to old privileges"));
- grant_free();
- proc_priv_hash= old_proc_priv_hash;
- func_priv_hash= old_func_priv_hash;
- }
- else
- {
- my_hash_free(&old_proc_priv_hash);
- my_hash_free(&old_func_priv_hash);
- }
- mysql_rwlock_unlock(&LOCK_grant);
+ ACL_ROLE *role= (ACL_ROLE *)ptr;
+ if (role->counter)
+ return 0;
- close_mysql_tables(thd);
- DBUG_RETURN(return_val);
+ mysql_mutex_assert_owner(&acl_cache->lock);
+ PRIVS_TO_MERGE data= { PRIVS_TO_MERGE::ALL, 0, 0 };
+ traverse_role_graph_up(role, &data, NULL, merge_role_privileges);
+ return 0;
}
@@ -4455,8 +6426,8 @@ static my_bool grant_reload_procs_priv(THD *thd)
my_bool grant_reload(THD *thd)
{
- TABLE_LIST tables[2];
- HASH old_column_priv_hash;
+ TABLE_LIST tables[3];
+ HASH old_column_priv_hash, old_proc_priv_hash, old_func_priv_hash;
MEM_ROOT old_mem;
my_bool return_val= 1;
DBUG_ENTER("grant_reload");
@@ -4471,8 +6442,13 @@ my_bool grant_reload(THD *thd)
tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
C_STRING_WITH_LEN("columns_priv"),
"columns_priv", TL_READ);
+ tables[2].init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("procs_priv"),
+ "procs_priv", TL_READ);
tables[0].next_local= tables[0].next_global= tables+1;
- tables[0].open_type= tables[1].open_type= OT_BASE_ONLY;
+ tables[1].next_local= tables[1].next_global= tables+2;
+ tables[0].open_type= tables[1].open_type= tables[2].open_type= OT_BASE_ONLY;
+ tables[2].open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
/*
To avoid deadlocks we should obtain table locks before
@@ -4482,41 +6458,42 @@ my_bool grant_reload(THD *thd)
goto end;
mysql_rwlock_wrlock(&LOCK_grant);
+ grant_version++;
old_column_priv_hash= column_priv_hash;
+ old_proc_priv_hash= proc_priv_hash;
+ old_func_priv_hash= func_priv_hash;
/*
Create a new memory pool but save the current memory pool to make an undo
opertion possible in case of failure.
*/
old_mem= memex;
- init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0));
if ((return_val= grant_load(thd, tables)))
{ // Error. Revert to old hash
DBUG_PRINT("error",("Reverting to old privileges"));
grant_free(); /* purecov: deadcode */
column_priv_hash= old_column_priv_hash; /* purecov: deadcode */
+ proc_priv_hash= old_proc_priv_hash;
+ func_priv_hash= old_func_priv_hash;
memex= old_mem; /* purecov: deadcode */
}
else
{
my_hash_free(&old_column_priv_hash);
+ my_hash_free(&old_proc_priv_hash);
+ my_hash_free(&old_func_priv_hash);
free_root(&old_mem,MYF(0));
}
- mysql_rwlock_unlock(&LOCK_grant);
- close_mysql_tables(thd);
- /*
- It is OK failing to load procs_priv table because we may be
- working with 4.1 privilege tables.
- */
- if (grant_reload_procs_priv(thd))
- return_val= 1;
+ mysql_mutex_lock(&acl_cache->lock);
+ my_hash_iterate(&acl_roles, role_propagate_grants_action, NULL);
+ mysql_mutex_unlock(&acl_cache->lock);
- mysql_rwlock_wrlock(&LOCK_grant);
- grant_version++;
mysql_rwlock_unlock(&LOCK_grant);
+ close_mysql_tables(thd);
+
end:
DBUG_RETURN(return_val);
}
@@ -4574,6 +6551,8 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
uint i;
ulong orig_want_access= want_access;
my_bool locked= 0;
+ GRANT_TABLE *grant_table;
+ GRANT_TABLE *grant_table_role= NULL;
DBUG_ENTER("check_grant");
DBUG_ASSERT(number > 0);
@@ -4672,16 +6651,21 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
mysql_rwlock_rdlock(&LOCK_grant);
}
- GRANT_TABLE *grant_table= table_hash_search(sctx->host, sctx->ip,
- tl->get_db_name(),
- sctx->priv_user,
- tl->get_table_name(),
- FALSE);
+ grant_table= table_hash_search(sctx->host, sctx->ip,
+ tl->get_db_name(),
+ sctx->priv_user,
+ tl->get_table_name(),
+ FALSE);
+ if (sctx->priv_role[0])
+ grant_table_role= table_hash_search("", NULL, tl->get_db_name(),
+ sctx->priv_role,
+ tl->get_table_name(),
+ TRUE);
- if (!grant_table)
+ if (!grant_table && !grant_table_role)
{
- want_access &= ~tl->grant.privilege;
- goto err; // No grants
+ want_access&= ~tl->grant.privilege;
+ goto err;
}
/*
@@ -4691,18 +6675,21 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
if (any_combination_will_do)
continue;
- tl->grant.grant_table= grant_table; // Remember for column test
+ tl->grant.grant_table_user= grant_table; // Remember for column test
+ tl->grant.grant_table_role= grant_table_role;
tl->grant.version= grant_version;
- tl->grant.privilege|= grant_table->privs;
+ tl->grant.privilege|= grant_table ? grant_table->privs : 0;
+ tl->grant.privilege|= grant_table_role ? grant_table_role->privs : 0;
tl->grant.want_privilege= ((want_access & COL_ACLS) & ~tl->grant.privilege);
if (!(~tl->grant.privilege & want_access))
continue;
- if (want_access & ~(grant_table->cols | tl->grant.privilege))
+ if ((want_access&= ~((grant_table ? grant_table->cols : 0) |
+ (grant_table_role ? grant_table_role->cols : 0) |
+ tl->grant.privilege)))
{
- want_access &= ~(grant_table->cols | tl->grant.privilege);
- goto err; // impossible
+ goto err; // impossible
}
}
if (locked)
@@ -4751,6 +6738,7 @@ bool check_grant_column(THD *thd, GRANT_INFO *grant,
const char *name, uint length, Security_context *sctx)
{
GRANT_TABLE *grant_table;
+ GRANT_TABLE *grant_table_role;
GRANT_COLUMN *grant_column;
ulong want_access= grant->want_privilege & ~grant->privilege;
DBUG_ENTER("check_grant_column");
@@ -4765,17 +6753,40 @@ bool check_grant_column(THD *thd, GRANT_INFO *grant,
if (grant->version != grant_version)
{
- grant->grant_table=
+ grant->grant_table_user=
table_hash_search(sctx->host, sctx->ip, db_name,
sctx->priv_user,
table_name, 0); /* purecov: inspected */
+ grant->grant_table_role=
+ sctx->priv_role[0] ? table_hash_search("", NULL, db_name,
+ sctx->priv_role,
+ table_name, TRUE) : NULL;
grant->version= grant_version; /* purecov: inspected */
}
- if (!(grant_table= grant->grant_table))
- goto err; /* purecov: deadcode */
- grant_column=column_hash_search(grant_table, name, length);
- if (grant_column && !(~grant_column->rights & want_access))
+ grant_table= grant->grant_table_user;
+ grant_table_role= grant->grant_table_role;
+
+ if (!grant_table && !grant_table_role)
+ goto err;
+
+ if (grant_table)
+ {
+ grant_column= column_hash_search(grant_table, name, length);
+ if (grant_column)
+ {
+ want_access&= ~grant_column->rights;
+ }
+ }
+ if (grant_table_role)
+ {
+ grant_column= column_hash_search(grant_table_role, name, length);
+ if (grant_column)
+ {
+ want_access&= ~grant_column->rights;
+ }
+ }
+ if (!want_access)
{
mysql_rwlock_unlock(&LOCK_grant);
DBUG_RETURN(0);
@@ -4785,6 +6796,7 @@ err:
mysql_rwlock_unlock(&LOCK_grant);
char command[128];
get_privilege_desc(command, sizeof(command), want_access);
+ /* TODO perhaps error should print current rolename aswell */
my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
command,
sctx->priv_user,
@@ -4833,7 +6845,7 @@ bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
grant= &(table_ref->grant);
db_name= table_ref->view_db.str;
table_name= table_ref->view_name.str;
- if (table_ref->belong_to_view &&
+ if (table_ref->belong_to_view &&
thd->lex->sql_command == SQLCOM_SHOW_FIELDS)
{
view_privs= get_column_grant(thd, grant, db_name, table_name, name);
@@ -4865,7 +6877,7 @@ bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
}
-/**
+/**
@brief check if a query can access a set of columns
@param thd the current thread
@@ -4874,24 +6886,23 @@ bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
@return Operation status
@retval 0 Success
@retval 1 Falure
- @details This function walks over the columns of a table reference
+ @details This function walks over the columns of a table reference
The columns may originate from different tables, depending on the kind of
table reference, e.g. join, view.
For each table it will retrieve the grant information and will use it
to check the required access privileges for the fields requested from it.
-*/
-bool check_grant_all_columns(THD *thd, ulong want_access_arg,
+*/
+bool check_grant_all_columns(THD *thd, ulong want_access_arg,
Field_iterator_table_ref *fields)
{
Security_context *sctx= thd->security_ctx;
- ulong want_access= want_access_arg;
+ ulong UNINIT_VAR(want_access);
const char *table_name= NULL;
-
- const char* db_name;
+ const char* db_name;
GRANT_INFO *grant;
- /* Initialized only to make gcc happy */
- GRANT_TABLE *grant_table= NULL;
- /*
+ GRANT_TABLE *UNINIT_VAR(grant_table);
+ GRANT_TABLE *UNINIT_VAR(grant_table_role);
+ /*
Flag that gets set if privilege checking has to be performed on column
level.
*/
@@ -4915,26 +6926,46 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg,
/* reload table if someone has modified any grants */
if (grant->version != grant_version)
{
- grant->grant_table=
+ grant->grant_table_user=
table_hash_search(sctx->host, sctx->ip, db_name,
sctx->priv_user,
table_name, 0); /* purecov: inspected */
+ grant->grant_table_role=
+ sctx->priv_role[0] ? table_hash_search("", NULL, db_name,
+ sctx->priv_role,
+ table_name, TRUE) : NULL;
grant->version= grant_version; /* purecov: inspected */
}
- grant_table= grant->grant_table;
- DBUG_ASSERT (grant_table);
+ grant_table= grant->grant_table_user;
+ grant_table_role= grant->grant_table_role;
+ DBUG_ASSERT (grant_table || grant_table_role);
}
}
if (want_access)
{
- GRANT_COLUMN *grant_column=
- column_hash_search(grant_table, field_name,
- (uint) strlen(field_name));
- if (grant_column)
+ ulong have_access= 0;
+ if (grant_table)
+ {
+ GRANT_COLUMN *grant_column=
+ column_hash_search(grant_table, field_name,
+ (uint) strlen(field_name));
+ if (grant_column)
+ have_access= grant_column->rights;
+ }
+ if (grant_table_role)
+ {
+ GRANT_COLUMN *grant_column=
+ column_hash_search(grant_table_role, field_name,
+ (uint) strlen(field_name));
+ if (grant_column)
+ have_access|= grant_column->rights;
+ }
+
+ if (have_access)
using_column_privileges= TRUE;
- if (!grant_column || (~grant_column->rights & want_access))
+ if (want_access & ~have_access)
goto err;
}
}
@@ -4953,7 +6984,7 @@ err:
if (using_column_privileges)
my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
command, sctx->priv_user,
- sctx->host_or_ip, table_name);
+ sctx->host_or_ip, table_name);
else
my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
command,
@@ -4979,6 +7010,12 @@ static bool check_grant_db_routine(THD *thd, const char *db, HASH *hash)
{
return FALSE;
}
+ if (sctx->priv_role[0] && strcmp(item->user, sctx->priv_role) == 0 &&
+ strcmp(item->db, db) == 0 &&
+ (!item->host.hostname || !item->host.hostname[0]))
+ {
+ return FALSE; /* Found current role match */
+ }
}
return TRUE;
@@ -4991,11 +7028,12 @@ static bool check_grant_db_routine(THD *thd, const char *db, HASH *hash)
Return 1 if access is denied
*/
-bool check_grant_db(THD *thd,const char *db)
+bool check_grant_db(THD *thd, const char *db)
{
Security_context *sctx= thd->security_ctx;
char helping [SAFE_NAME_LEN + USERNAME_LENGTH+2], *end;
- uint len;
+ char helping2 [SAFE_NAME_LEN + USERNAME_LENGTH+2];
+ uint len, UNINIT_VAR(len2);
bool error= TRUE;
end= strmov(helping, sctx->priv_user) + 1;
@@ -5006,6 +7044,18 @@ bool check_grant_db(THD *thd,const char *db)
len= (uint) (end - helping) + 1;
+ /*
+ If a role is set, we need to check for privileges
+ here aswell
+ */
+ if (sctx->priv_role[0])
+ {
+ end= strmov(helping2, sctx->priv_role) + 1;
+ end= strnmov(end, db, helping2 + sizeof(helping2) - end);
+ len2= (uint) (end - helping2) + 1;
+ }
+
+
mysql_rwlock_rdlock(&LOCK_grant);
for (uint idx=0 ; idx < column_priv_hash.records ; idx++)
@@ -5020,6 +7070,14 @@ bool check_grant_db(THD *thd,const char *db)
error= FALSE; /* Found match. */
break;
}
+ if (sctx->priv_role[0] &&
+ len2 < grant_table->key_length &&
+ !memcmp(grant_table->hash_key,helping2,len) &&
+ (!grant_table->host.hostname || !grant_table->host.hostname[0]))
+ {
+ error= FALSE; /* Found role match */
+ break;
+ }
}
if (error)
@@ -5056,6 +7114,7 @@ bool check_grant_routine(THD *thd, ulong want_access,
Security_context *sctx= thd->security_ctx;
char *user= sctx->priv_user;
char *host= sctx->priv_host;
+ char *role= sctx->priv_role;
DBUG_ENTER("check_grant_routine");
want_access&= ~sctx->master_access;
@@ -5069,6 +7128,12 @@ bool check_grant_routine(THD *thd, ulong want_access,
if ((grant_proc= routine_hash_search(host, sctx->ip, table->db, user,
table->table_name, is_proc, 0)))
table->grant.privilege|= grant_proc->privs;
+ if (role[0]) /* current role set check */
+ {
+ if ((grant_proc= routine_hash_search("", NULL, table->db, role,
+ table->table_name, is_proc, 0)))
+ table->grant.privilege|= grant_proc->privs;
+ }
if (want_access & ~table->grant.privilege)
{
@@ -5100,9 +7165,9 @@ err:
/*
- Check if routine has any of the
+ Check if routine has any of the
routine level grants
-
+
SYNPOSIS
bool check_routine_level_acl()
thd Thread handler
@@ -5110,11 +7175,11 @@ err:
name Routine name
RETURN
- 0 Ok
+ 0 Ok
1 error
*/
-bool check_routine_level_acl(THD *thd, const char *db, const char *name,
+bool check_routine_level_acl(THD *thd, const char *db, const char *name,
bool is_proc)
{
bool no_routine_acl= 1;
@@ -5126,6 +7191,15 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name,
sctx->priv_user,
name, is_proc, 0)))
no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
+
+ if (no_routine_acl && sctx->priv_role[0]) /* current set role check */
+ {
+ if ((grant_proc= routine_hash_search("",
+ NULL, db,
+ sctx->priv_role,
+ name, is_proc, 0)))
+ no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS);
+ }
mysql_rwlock_unlock(&LOCK_grant);
return no_routine_acl;
}
@@ -5141,18 +7215,26 @@ ulong get_table_grant(THD *thd, TABLE_LIST *table)
Security_context *sctx= thd->security_ctx;
const char *db = table->db ? table->db : thd->db;
GRANT_TABLE *grant_table;
+ GRANT_TABLE *grant_table_role= NULL;
mysql_rwlock_rdlock(&LOCK_grant);
#ifdef EMBEDDED_LIBRARY
grant_table= NULL;
+ grant_table_role= NULL;
#else
grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user,
table->table_name, 0);
+ if (sctx->priv_role[0])
+ grant_table_role= table_hash_search("", "", db, sctx->priv_role,
+ table->table_name, 0);
#endif
- table->grant.grant_table=grant_table; // Remember for column test
+ table->grant.grant_table_user= grant_table; // Remember for column test
+ table->grant.grant_table_role= grant_table_role;
table->grant.version=grant_version;
if (grant_table)
table->grant.privilege|= grant_table->privs;
+ if (grant_table_role)
+ table->grant.privilege|= grant_table_role->privs;
privilege= table->grant.privilege;
mysql_rwlock_unlock(&LOCK_grant);
return privilege;
@@ -5182,31 +7264,52 @@ ulong get_column_grant(THD *thd, GRANT_INFO *grant,
const char *field_name)
{
GRANT_TABLE *grant_table;
+ GRANT_TABLE *grant_table_role;
GRANT_COLUMN *grant_column;
- ulong priv;
+ ulong priv= 0;
mysql_rwlock_rdlock(&LOCK_grant);
/* reload table if someone has modified any grants */
if (grant->version != grant_version)
{
Security_context *sctx= thd->security_ctx;
- grant->grant_table=
+ grant->grant_table_user=
table_hash_search(sctx->host, sctx->ip,
db_name, sctx->priv_user,
- table_name, 0); /* purecov: inspected */
+ table_name, 0); /* purecov: inspected */
+ grant->grant_table_role=
+ sctx->priv_role[0] ? table_hash_search("", "", db_name,
+ sctx->priv_role,
+ table_name, TRUE) : NULL;
grant->version= grant_version; /* purecov: inspected */
}
- if (!(grant_table= grant->grant_table))
+ grant_table= grant->grant_table_user;
+ grant_table_role= grant->grant_table_role;
+
+ if (!grant_table && !grant_table_role)
priv= grant->privilege;
else
{
- grant_column= column_hash_search(grant_table, field_name,
- (uint) strlen(field_name));
- if (!grant_column)
- priv= (grant->privilege | grant_table->privs);
- else
- priv= (grant->privilege | grant_table->privs | grant_column->rights);
+ if (grant_table)
+ {
+ grant_column= column_hash_search(grant_table, field_name,
+ (uint) strlen(field_name));
+ if (!grant_column)
+ priv= (grant->privilege | grant_table->privs);
+ else
+ priv= (grant->privilege | grant_table->privs | grant_column->rights);
+ }
+
+ if (grant_table_role)
+ {
+ grant_column= column_hash_search(grant_table_role, field_name,
+ (uint) strlen(field_name));
+ if (!grant_column)
+ priv|= (grant->privilege | grant_table_role->privs);
+ else
+ priv|= (grant->privilege | grant_table->privs | grant_column->rights);
+ }
}
mysql_rwlock_unlock(&LOCK_grant);
return priv;
@@ -5246,9 +7349,43 @@ static uint command_lengths[]=
};
-static int show_routine_grants(THD *thd, LEX_USER *lex_user, HASH *hash,
- const char *type, int typelen,
- char *buff, int buffsize);
+static bool print_grants_for_role(THD *thd, ACL_ROLE * role)
+{
+ char buff[1024];
+
+ if (show_role_grants(thd, role->user.str, "", role, buff, sizeof(buff)))
+ return TRUE;
+
+ if (show_global_privileges(thd, role, TRUE, buff, sizeof(buff)))
+ return TRUE;
+
+ if (show_database_privileges(thd, role->user.str, "", buff, sizeof(buff)))
+ return TRUE;
+
+ if (show_table_and_column_privileges(thd, role->user.str, "", buff, sizeof(buff)))
+ return TRUE;
+
+ if (show_routine_grants(thd, role->user.str, "", &proc_priv_hash,
+ STRING_WITH_LEN("PROCEDURE"), buff, sizeof(buff)))
+ return TRUE;
+
+ if (show_routine_grants(thd, role->user.str, "", &func_priv_hash,
+ STRING_WITH_LEN("FUNCTION"), buff, sizeof(buff)))
+ return TRUE;
+
+ return FALSE;
+
+}
+
+
+static int show_grants_callback(ACL_USER_BASE *role, void *data)
+{
+ THD *thd= (THD *)data;
+ DBUG_ASSERT(role->flags & IS_ROLE);
+ if (print_grants_for_role(thd, (ACL_ROLE *)role))
+ return -1;
+ return 0;
+}
/*
@@ -5258,18 +7395,18 @@ static int show_routine_grants(THD *thd, LEX_USER *lex_user, HASH *hash,
Send to client grant-like strings depicting user@host privileges
*/
-bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
+bool mysql_show_grants(THD *thd, LEX_USER *lex_user)
{
- ulong want_access;
- uint counter,index;
- int error = 0;
- ACL_USER *acl_user;
- ACL_DB *acl_db;
+ int error = -1;
+ ACL_USER *UNINIT_VAR(acl_user);
+ ACL_ROLE *acl_role= NULL;
char buff[1024];
Protocol *protocol= thd->protocol;
+ char *username= NULL;
+ char *hostname= NULL;
+ char *rolename= NULL;
DBUG_ENTER("mysql_show_grants");
- LINT_INIT(acl_user);
if (!initialized)
{
my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
@@ -5279,26 +7416,54 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
mysql_rwlock_rdlock(&LOCK_grant);
mysql_mutex_lock(&acl_cache->lock);
- acl_user= find_acl_user(lex_user->host.str, lex_user->user.str, TRUE);
- if (!acl_user)
+ if (lex_user->user.str == current_user.str)
{
- mysql_mutex_unlock(&acl_cache->lock);
- mysql_rwlock_unlock(&LOCK_grant);
+ username= thd->security_ctx->priv_user;
+ hostname= thd->security_ctx->priv_host;
+ }
+ else if (lex_user->user.str == current_role.str)
+ {
+ rolename= thd->security_ctx->priv_role;
+ }
+ else if (lex_user->user.str == current_user_and_current_role.str)
+ {
+ username= thd->security_ctx->priv_user;
+ hostname= thd->security_ctx->priv_host;
+ rolename= thd->security_ctx->priv_role;
+ }
+ else
+ {
+ lex_user= get_current_user(thd, lex_user, false);
+ if (!lex_user)
+ {
+ mysql_mutex_unlock(&acl_cache->lock);
+ mysql_rwlock_unlock(&LOCK_grant);
+ DBUG_RETURN(TRUE);
+ }
- my_error(ER_NONEXISTING_GRANT, MYF(0),
- lex_user->user.str, lex_user->host.str);
- DBUG_RETURN(TRUE);
+ if (lex_user->is_role())
+ {
+ rolename= lex_user->user.str;
+ }
+ else
+ {
+ username= lex_user->user.str;
+ hostname= lex_user->host.str;
+ }
}
+ DBUG_ASSERT(rolename || username);
Item_string *field=new Item_string("",0,&my_charset_latin1);
List<Item> field_list;
field->name=buff;
field->max_length=1024;
- strxmov(buff,"Grants for ",lex_user->user.str,"@",
- lex_user->host.str,NullS);
+ if (!username)
+ strxmov(buff,"Grants for ",rolename, NullS);
+ else
+ strxmov(buff,"Grants for ",username,"@",hostname, NullS);
field_list.push_back(field);
if (protocol->send_result_set_metadata(&field_list,
- Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
{
mysql_mutex_unlock(&acl_cache->lock);
mysql_rwlock_unlock(&LOCK_grant);
@@ -5306,39 +7471,188 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
DBUG_RETURN(TRUE);
}
- /* Add first global access grants */
+ if (username)
{
- String global(buff,sizeof(buff),system_charset_info);
- global.length(0);
- global.append(STRING_WITH_LEN("GRANT "));
+ acl_user= find_user_exact(hostname, username);
+ if (!acl_user)
+ {
+ mysql_mutex_unlock(&acl_cache->lock);
+ mysql_rwlock_unlock(&LOCK_grant);
- want_access= acl_user->access;
- if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL)))
- global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
- else if (!(want_access & ~GRANT_ACL))
- global.append(STRING_WITH_LEN("USAGE"));
+ my_error(ER_NONEXISTING_GRANT, MYF(0),
+ username, hostname);
+ DBUG_RETURN(TRUE);
+ }
+
+ /* Show granted roles to acl_user */
+ if (show_role_grants(thd, username, hostname, acl_user, buff, sizeof(buff)))
+ goto end;
+
+ /* Add first global access grants */
+ if (show_global_privileges(thd, acl_user, FALSE, buff, sizeof(buff)))
+ goto end;
+
+ /* Add database access */
+ if (show_database_privileges(thd, username, hostname, buff, sizeof(buff)))
+ goto end;
+
+ /* Add table & column access */
+ if (show_table_and_column_privileges(thd, username, hostname, buff, sizeof(buff)))
+ goto end;
+
+ if (show_routine_grants(thd, username, hostname, &proc_priv_hash,
+ STRING_WITH_LEN("PROCEDURE"), buff, sizeof(buff)))
+ goto end;
+
+ if (show_routine_grants(thd, username, hostname, &func_priv_hash,
+ STRING_WITH_LEN("FUNCTION"), buff, sizeof(buff)))
+ goto end;
+
+ if (show_proxy_grants(thd, username, hostname, buff, sizeof(buff)))
+ goto end;
+ }
+
+ if (rolename)
+ {
+ acl_role= find_acl_role(rolename);
+ if (acl_role)
+ {
+ /* get a list of all inherited roles */
+ traverse_role_graph_down(acl_role, thd, show_grants_callback, NULL);
+ }
else
{
- bool found=0;
- ulong j,test_access= want_access & ~GRANT_ACL;
- for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1)
+ if (lex_user->user.str == current_role.str)
{
- if (test_access & j)
- {
- if (found)
- global.append(STRING_WITH_LEN(", "));
- found=1;
- global.append(command_array[counter],command_lengths[counter]);
- }
+ mysql_mutex_unlock(&acl_cache->lock);
+ mysql_rwlock_unlock(&LOCK_grant);
+ my_error(ER_NONEXISTING_GRANT, MYF(0),
+ thd->security_ctx->priv_user,
+ thd->security_ctx->priv_host);
+ DBUG_RETURN(TRUE);
}
}
- global.append (STRING_WITH_LEN(" ON *.* TO '"));
- global.append(lex_user->user.str, lex_user->user.length,
- system_charset_info);
- global.append (STRING_WITH_LEN("'@'"));
- global.append(lex_user->host.str,lex_user->host.length,
- system_charset_info);
+ }
+
+ error= 0;
+end:
+ mysql_mutex_unlock(&acl_cache->lock);
+ mysql_rwlock_unlock(&LOCK_grant);
+
+ my_eof(thd);
+ DBUG_RETURN(error);
+}
+
+static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_STRING *u,
+ const LEX_STRING *h,
+ const LEX_STRING *r)
+{
+ char buf[1024];
+ String pair_key(buf, sizeof(buf), &my_charset_bin);
+
+ size_t key_length= u->length + h->length + r->length + 3;
+ pair_key.alloc(key_length);
+
+ strmov(strmov(strmov(const_cast<char*>(pair_key.ptr()),
+ u->str) + 1, h->str) + 1, r->str);
+
+ return (ROLE_GRANT_PAIR *)
+ my_hash_search(&acl_roles_mappings, (uchar*)pair_key.ptr(), key_length);
+}
+
+static bool show_role_grants(THD *thd, const char *username,
+ const char *hostname, ACL_USER_BASE *acl_entry,
+ char *buff, size_t buffsize)
+{
+ uint counter;
+ Protocol *protocol= thd->protocol;
+ LEX_STRING host= {const_cast<char*>(hostname), strlen(hostname)};
+
+ String grant(buff,sizeof(buff),system_charset_info);
+ for (counter= 0; counter < acl_entry->role_grants.elements; counter++)
+ {
+ grant.length(0);
+ grant.append(STRING_WITH_LEN("GRANT "));
+ ACL_ROLE *acl_role= *(dynamic_element(&acl_entry->role_grants, counter,
+ ACL_ROLE**));
+ grant.append(acl_role->user.str, acl_role->user.length,
+ system_charset_info);
+ grant.append(STRING_WITH_LEN(" TO '"));
+ grant.append(acl_entry->user.str, acl_entry->user.length,
+ system_charset_info);
+ if (!(acl_entry->flags & IS_ROLE))
+ {
+ grant.append(STRING_WITH_LEN("'@'"));
+ grant.append(&host);
+ }
+ grant.append('\'');
+
+ ROLE_GRANT_PAIR *pair=
+ find_role_grant_pair(&acl_entry->user, &host, &acl_role->user);
+ DBUG_ASSERT(pair);
+
+ if (pair->with_admin)
+ grant.append(STRING_WITH_LEN(" WITH ADMIN OPTION"));
+
+ protocol->prepare_for_resend();
+ protocol->store(grant.ptr(),grant.length(),grant.charset());
+ if (protocol->write())
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static bool show_global_privileges(THD *thd, ACL_USER_BASE *acl_entry,
+ bool handle_as_role,
+ char *buff, size_t buffsize)
+{
+ uint counter;
+ ulong want_access;
+ Protocol *protocol= thd->protocol;
+
+ String global(buff,sizeof(buff),system_charset_info);
+ global.length(0);
+ global.append(STRING_WITH_LEN("GRANT "));
+
+ if (handle_as_role)
+ want_access= ((ACL_ROLE *)acl_entry)->initial_role_access;
+ else
+ want_access= acl_entry->access;
+ if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL)))
+ global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
+ else if (!(want_access & ~GRANT_ACL))
+ global.append(STRING_WITH_LEN("USAGE"));
+ else
+ {
+ bool found=0;
+ ulong j,test_access= want_access & ~GRANT_ACL;
+ for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1)
+ {
+ if (test_access & j)
+ {
+ if (found)
+ global.append(STRING_WITH_LEN(", "));
+ found=1;
+ global.append(command_array[counter],command_lengths[counter]);
+ }
+ }
+ }
+ global.append (STRING_WITH_LEN(" ON *.* TO '"));
+ global.append(acl_entry->user.str, acl_entry->user.length,
+ system_charset_info);
+ global.append('\'');
+
+ if (!handle_as_role)
+ {
+ ACL_USER *acl_user= (ACL_USER *)acl_entry;
+
+ global.append (STRING_WITH_LEN("@'"));
+ global.append(acl_user->host.hostname, acl_user->hostname_length,
+ system_charset_info);
global.append ('\'');
+
if (acl_user->plugin.str == native_password_plugin_name.str ||
acl_user->plugin.str == old_password_plugin_name.str)
{
@@ -5352,14 +7666,14 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
}
else
{
- global.append(STRING_WITH_LEN(" IDENTIFIED VIA "));
- global.append(acl_user->plugin.str, acl_user->plugin.length);
- if (acl_user->auth_string.length)
- {
- global.append(STRING_WITH_LEN(" USING '"));
- global.append(acl_user->auth_string.str, acl_user->auth_string.length);
- global.append('\'');
- }
+ global.append(STRING_WITH_LEN(" IDENTIFIED VIA "));
+ global.append(acl_user->plugin.str, acl_user->plugin.length);
+ if (acl_user->auth_string.length)
+ {
+ global.append(STRING_WITH_LEN(" USING '"));
+ global.append(acl_user->auth_string.str, acl_user->auth_string.length);
+ global.append('\'');
+ }
}
/* "show grants" SSL related stuff */
if (acl_user->ssl_type == SSL_TYPE_ANY)
@@ -5372,67 +7686,75 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
global.append(STRING_WITH_LEN(" REQUIRE "));
if (acl_user->x509_issuer)
{
- ssl_options++;
- global.append(STRING_WITH_LEN("ISSUER \'"));
- global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer));
- global.append('\'');
+ ssl_options++;
+ global.append(STRING_WITH_LEN("ISSUER \'"));
+ global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer));
+ global.append('\'');
}
if (acl_user->x509_subject)
{
- if (ssl_options++)
- global.append(' ');
- global.append(STRING_WITH_LEN("SUBJECT \'"));
- global.append(acl_user->x509_subject,strlen(acl_user->x509_subject),
+ if (ssl_options++)
+ global.append(' ');
+ global.append(STRING_WITH_LEN("SUBJECT \'"));
+ global.append(acl_user->x509_subject,strlen(acl_user->x509_subject),
system_charset_info);
- global.append('\'');
+ global.append('\'');
}
if (acl_user->ssl_cipher)
{
- if (ssl_options++)
- global.append(' ');
- global.append(STRING_WITH_LEN("CIPHER '"));
- global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher),
+ if (ssl_options++)
+ global.append(' ');
+ global.append(STRING_WITH_LEN("CIPHER '"));
+ global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher),
system_charset_info);
- global.append('\'');
+ global.append('\'');
}
}
if ((want_access & GRANT_ACL) ||
- (acl_user->user_resource.questions ||
+ (acl_user->user_resource.questions ||
acl_user->user_resource.updates ||
acl_user->user_resource.conn_per_hour ||
acl_user->user_resource.user_conn))
{
global.append(STRING_WITH_LEN(" WITH"));
if (want_access & GRANT_ACL)
- global.append(STRING_WITH_LEN(" GRANT OPTION"));
+ global.append(STRING_WITH_LEN(" GRANT OPTION"));
add_user_option(&global, acl_user->user_resource.questions,
- "MAX_QUERIES_PER_HOUR", 0);
+ "MAX_QUERIES_PER_HOUR", 0);
add_user_option(&global, acl_user->user_resource.updates,
- "MAX_UPDATES_PER_HOUR", 0);
+ "MAX_UPDATES_PER_HOUR", 0);
add_user_option(&global, acl_user->user_resource.conn_per_hour,
- "MAX_CONNECTIONS_PER_HOUR", 0);
+ "MAX_CONNECTIONS_PER_HOUR", 0);
add_user_option(&global, acl_user->user_resource.user_conn,
- "MAX_USER_CONNECTIONS", 1);
- }
- protocol->prepare_for_resend();
- protocol->store(global.ptr(),global.length(),global.charset());
- if (protocol->write())
- {
- error= -1;
- goto end;
+ "MAX_USER_CONNECTIONS", 1);
}
}
- /* Add database access */
+ protocol->prepare_for_resend();
+ protocol->store(global.ptr(),global.length(),global.charset());
+ if (protocol->write())
+ return TRUE;
+
+ return FALSE;
+
+}
+
+static bool show_database_privileges(THD *thd, const char *username,
+ const char *hostname,
+ char *buff, size_t buffsize)
+{
+ ACL_DB *acl_db;
+ ulong want_access;
+ uint counter;
+ Protocol *protocol= thd->protocol;
+
for (counter=0 ; counter < acl_dbs.elements ; counter++)
{
const char *user, *host;
acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
- if (!(user=acl_db->user))
- user= "";
- if (!(host=acl_db->host.hostname))
- host= "";
+ user= safe_str(acl_db->user);
+ host=acl_db->host.hostname;
/*
We do not make SHOW GRANTS case-sensitive here (like REVOKE),
@@ -5441,68 +7763,84 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
would be wrong from a security point of view.
*/
- if (!strcmp(lex_user->user.str,user) &&
- !my_strcasecmp(system_charset_info, lex_user->host.str, host))
+ if (!strcmp(username, user) &&
+ !my_strcasecmp(system_charset_info, hostname, host))
{
- want_access=acl_db->access;
+ /*
+ do not print inherited access bits for roles,
+ the role bits present in the table are what matters
+ */
+ if (*hostname) // User
+ want_access=acl_db->access;
+ else // Role
+ want_access=acl_db->initial_access;
if (want_access)
{
- String db(buff,sizeof(buff),system_charset_info);
- db.length(0);
- db.append(STRING_WITH_LEN("GRANT "));
-
- if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL)))
- db.append(STRING_WITH_LEN("ALL PRIVILEGES"));
- else if (!(want_access & ~GRANT_ACL))
- db.append(STRING_WITH_LEN("USAGE"));
- else
- {
- int found=0, cnt;
- ulong j,test_access= want_access & ~GRANT_ACL;
- for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
- {
- if (test_access & j)
- {
- if (found)
- db.append(STRING_WITH_LEN(", "));
- found = 1;
- db.append(command_array[cnt],command_lengths[cnt]);
- }
- }
- }
- db.append (STRING_WITH_LEN(" ON "));
- append_identifier(thd, &db, acl_db->db, strlen(acl_db->db));
- db.append (STRING_WITH_LEN(".* TO '"));
- db.append(lex_user->user.str, lex_user->user.length,
- system_charset_info);
- db.append (STRING_WITH_LEN("'@'"));
- // host and lex_user->host are equal except for case
- db.append(host, strlen(host), system_charset_info);
- db.append ('\'');
- if (want_access & GRANT_ACL)
- db.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
- protocol->prepare_for_resend();
- protocol->store(db.ptr(),db.length(),db.charset());
- if (protocol->write())
- {
- error= -1;
- goto end;
- }
+ String db(buff,sizeof(buff),system_charset_info);
+ db.length(0);
+ db.append(STRING_WITH_LEN("GRANT "));
+
+ if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL)))
+ db.append(STRING_WITH_LEN("ALL PRIVILEGES"));
+ else if (!(want_access & ~GRANT_ACL))
+ db.append(STRING_WITH_LEN("USAGE"));
+ else
+ {
+ int found=0, cnt;
+ ulong j,test_access= want_access & ~GRANT_ACL;
+ for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
+ {
+ if (test_access & j)
+ {
+ if (found)
+ db.append(STRING_WITH_LEN(", "));
+ found = 1;
+ db.append(command_array[cnt],command_lengths[cnt]);
+ }
+ }
+ }
+ db.append (STRING_WITH_LEN(" ON "));
+ append_identifier(thd, &db, acl_db->db, strlen(acl_db->db));
+ db.append (STRING_WITH_LEN(".* TO '"));
+ db.append(username, strlen(username),
+ system_charset_info);
+ if (*hostname)
+ {
+ db.append (STRING_WITH_LEN("'@'"));
+ // host and lex_user->host are equal except for case
+ db.append(host, strlen(host), system_charset_info);
+ }
+ db.append ('\'');
+ if (want_access & GRANT_ACL)
+ db.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
+ protocol->prepare_for_resend();
+ protocol->store(db.ptr(),db.length(),db.charset());
+ if (protocol->write())
+ {
+ return TRUE;
+ }
}
}
}
+ return FALSE;
+
+}
+
+static bool show_table_and_column_privileges(THD *thd, const char *username,
+ const char *hostname,
+ char *buff, size_t buffsize)
+{
+ uint counter, index;
+ Protocol *protocol= thd->protocol;
- /* Add table & column access */
for (index=0 ; index < column_priv_hash.records ; index++)
{
const char *user, *host;
GRANT_TABLE *grant_table= (GRANT_TABLE*)
my_hash_element(&column_priv_hash, index);
- if (!(user=grant_table->user))
- user= "";
- if (!(host= grant_table->host.hostname))
- host= "";
+ user= safe_str(grant_table->user);
+ host= grant_table->host.hostname;
/*
We do not make SHOW GRANTS case-sensitive here (like REVOKE),
@@ -5511,132 +7849,126 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
would be wrong from a security point of view.
*/
- if (!strcmp(lex_user->user.str,user) &&
- !my_strcasecmp(system_charset_info, lex_user->host.str, host))
+ if (!strcmp(username,user) &&
+ !my_strcasecmp(system_charset_info, hostname, host))
{
- ulong table_access= grant_table->privs;
- if ((table_access | grant_table->cols) != 0)
+ ulong table_access;
+ ulong cols_access;
+ if (*hostname) // User
+ {
+ table_access= grant_table->privs;
+ cols_access= grant_table->cols;
+ }
+ else // Role
{
- String global(buff, sizeof(buff), system_charset_info);
- ulong test_access= (table_access | grant_table->cols) & ~GRANT_ACL;
+ table_access= grant_table->init_privs;
+ cols_access= grant_table->init_cols;
+ }
- global.length(0);
- global.append(STRING_WITH_LEN("GRANT "));
+ if ((table_access | cols_access) != 0)
+ {
+ String global(buff, sizeof(buff), system_charset_info);
+ ulong test_access= (table_access | cols_access) & ~GRANT_ACL;
- if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL)))
- global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
- else if (!test_access)
- global.append(STRING_WITH_LEN("USAGE"));
- else
- {
+ global.length(0);
+ global.append(STRING_WITH_LEN("GRANT "));
+
+ if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL)))
+ global.append(STRING_WITH_LEN("ALL PRIVILEGES"));
+ else if (!test_access)
+ global.append(STRING_WITH_LEN("USAGE"));
+ else
+ {
/* Add specific column access */
- int found= 0;
- ulong j;
+ int found= 0;
+ ulong j;
- for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1)
- {
- if (test_access & j)
- {
- if (found)
- global.append(STRING_WITH_LEN(", "));
- found= 1;
- global.append(command_array[counter],command_lengths[counter]);
+ for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1)
+ {
+ if (test_access & j)
+ {
+ if (found)
+ global.append(STRING_WITH_LEN(", "));
+ found= 1;
+ global.append(command_array[counter],command_lengths[counter]);
- if (grant_table->cols)
- {
- uint found_col= 0;
- for (uint col_index=0 ;
- col_index < grant_table->hash_columns.records ;
- col_index++)
- {
- GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
- my_hash_element(&grant_table->hash_columns,col_index);
- if (grant_column->rights & j)
- {
- if (!found_col)
- {
- found_col= 1;
- /*
- If we have a duplicated table level privilege, we
- must write the access privilege name again.
- */
- if (table_access & j)
- {
- global.append(STRING_WITH_LEN(", "));
- global.append(command_array[counter],
- command_lengths[counter]);
- }
- global.append(STRING_WITH_LEN(" ("));
- }
- else
- global.append(STRING_WITH_LEN(", "));
- global.append(grant_column->column,
- grant_column->key_length,
- system_charset_info);
- }
- }
- if (found_col)
- global.append(')');
- }
- }
- }
- }
- global.append(STRING_WITH_LEN(" ON "));
- append_identifier(thd, &global, grant_table->db,
- strlen(grant_table->db));
- global.append('.');
- append_identifier(thd, &global, grant_table->tname,
- strlen(grant_table->tname));
- global.append(STRING_WITH_LEN(" TO '"));
- global.append(lex_user->user.str, lex_user->user.length,
- system_charset_info);
- global.append(STRING_WITH_LEN("'@'"));
- // host and lex_user->host are equal except for case
- global.append(host, strlen(host), system_charset_info);
- global.append('\'');
- if (table_access & GRANT_ACL)
- global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
- protocol->prepare_for_resend();
- protocol->store(global.ptr(),global.length(),global.charset());
- if (protocol->write())
- {
- error= -1;
- break;
- }
+ if (grant_table->cols)
+ {
+ uint found_col= 0;
+ HASH *hash_columns;
+ hash_columns= &grant_table->hash_columns;
+
+ for (uint col_index=0 ;
+ col_index < hash_columns->records ;
+ col_index++)
+ {
+ GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
+ my_hash_element(hash_columns,col_index);
+ if (j & (*hostname ? grant_column->rights // User
+ : grant_column->init_rights)) // Role
+ {
+ if (!found_col)
+ {
+ found_col= 1;
+ /*
+ If we have a duplicated table level privilege, we
+ must write the access privilege name again.
+ */
+ if (table_access & j)
+ {
+ global.append(STRING_WITH_LEN(", "));
+ global.append(command_array[counter],
+ command_lengths[counter]);
+ }
+ global.append(STRING_WITH_LEN(" ("));
+ }
+ else
+ global.append(STRING_WITH_LEN(", "));
+ global.append(grant_column->column,
+ grant_column->key_length,
+ system_charset_info);
+ }
+ }
+ if (found_col)
+ global.append(')');
+ }
+ }
+ }
+ }
+ global.append(STRING_WITH_LEN(" ON "));
+ append_identifier(thd, &global, grant_table->db,
+ strlen(grant_table->db));
+ global.append('.');
+ append_identifier(thd, &global, grant_table->tname,
+ strlen(grant_table->tname));
+ global.append(STRING_WITH_LEN(" TO '"));
+ global.append(username, strlen(username),
+ system_charset_info);
+ if (*hostname)
+ {
+ global.append(STRING_WITH_LEN("'@'"));
+ // host and lex_user->host are equal except for case
+ global.append(host, strlen(host), system_charset_info);
+ }
+ global.append('\'');
+ if (table_access & GRANT_ACL)
+ global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
+ protocol->prepare_for_resend();
+ protocol->store(global.ptr(),global.length(),global.charset());
+ if (protocol->write())
+ {
+ return TRUE;
+ }
}
}
}
+ return FALSE;
- if (show_routine_grants(thd, lex_user, &proc_priv_hash,
- STRING_WITH_LEN("PROCEDURE"), buff, sizeof(buff)))
- {
- error= -1;
- goto end;
- }
-
- if (show_routine_grants(thd, lex_user, &func_priv_hash,
- STRING_WITH_LEN("FUNCTION"), buff, sizeof(buff)))
- {
- error= -1;
- goto end;
- }
-
- if (show_proxy_grants(thd, lex_user, buff, sizeof(buff)))
- {
- error= -1;
- goto end;
- }
-
-end:
- mysql_mutex_unlock(&acl_cache->lock);
- mysql_rwlock_unlock(&LOCK_grant);
-
- my_eof(thd);
- DBUG_RETURN(error);
}
-static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash,
- const char *type, int typelen,
+static int show_routine_grants(THD* thd,
+ const char *username, const char *hostname,
+ HASH *hash, const char *type, int typelen,
char *buff, int buffsize)
{
uint counter, index;
@@ -5648,10 +7980,8 @@ static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash,
const char *user, *host;
GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, index);
- if (!(user=grant_proc->user))
- user= "";
- if (!(host= grant_proc->host.hostname))
- host= "";
+ user= safe_str(grant_proc->user);
+ host= grant_proc->host.hostname;
/*
We do not make SHOW GRANTS case-sensitive here (like REVOKE),
@@ -5660,10 +7990,15 @@ static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash,
would be wrong from a security point of view.
*/
- if (!strcmp(lex_user->user.str,user) &&
- !my_strcasecmp(system_charset_info, lex_user->host.str, host))
+ if (!strcmp(username, user) &&
+ !my_strcasecmp(system_charset_info, hostname, host))
{
- ulong proc_access= grant_proc->privs;
+ ulong proc_access;
+ if (*hostname) // User
+ proc_access= grant_proc->privs;
+ else // Role
+ proc_access= grant_proc->init_privs;
+
if (proc_access != 0)
{
String global(buff, buffsize, system_charset_info);
@@ -5700,11 +8035,14 @@ static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash,
append_identifier(thd, &global, grant_proc->tname,
strlen(grant_proc->tname));
global.append(STRING_WITH_LEN(" TO '"));
- global.append(lex_user->user.str, lex_user->user.length,
+ global.append(username, strlen(username),
system_charset_info);
- global.append(STRING_WITH_LEN("'@'"));
- // host and lex_user->host are equal except for case
- global.append(host, strlen(host), system_charset_info);
+ if (*hostname)
+ {
+ global.append(STRING_WITH_LEN("'@'"));
+ // host and lex_user->host are equal except for case
+ global.append(host, strlen(host), system_charset_info);
+ }
global.append('\'');
if (proc_access & GRANT_ACL)
global.append(STRING_WITH_LEN(" WITH GRANT OPTION"));
@@ -5721,6 +8059,7 @@ static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash,
return error;
}
+
/*
Make a clear-text version of the requested privilege.
*/
@@ -5757,7 +8096,7 @@ void get_mqh(const char *user, const char *host, USER_CONN *uc)
mysql_mutex_lock(&acl_cache->lock);
- if (initialized && (acl_user= find_acl_user(host,user, FALSE)))
+ if (initialized && (acl_user= find_user_wild(host,user)))
uc->user_resources= acl_user->user_resource;
else
bzero((char*) &uc->user_resources, sizeof(uc->user_resources));
@@ -5771,7 +8110,7 @@ void get_mqh(const char *user, const char *host, USER_CONN *uc)
SYNOPSIS
open_grant_tables()
thd The current thread.
- tables (out) The 4 elements array for the opened tables.
+ tables (out) The 7 elements array for the opened tables.
DESCRIPTION
Tables are numbered as follows:
@@ -5779,6 +8118,9 @@ void get_mqh(const char *user, const char *host, USER_CONN *uc)
1 db
2 tables_priv
3 columns_priv
+ 4 procs_priv
+ 5 proxies_priv
+ 6 roles_mapping
RETURN
1 Skip GRANT handling during replication.
@@ -5786,8 +8128,8 @@ void get_mqh(const char *user, const char *host, USER_CONN *uc)
< 0 Error.
*/
-#define GRANT_TABLES 6
-int open_grant_tables(THD *thd, TABLE_LIST *tables)
+#define GRANT_TABLES 7
+static int open_grant_tables(THD *thd, TABLE_LIST *tables)
{
Rpl_filter *rpl_filter= thd->rpl_filter;
DBUG_ENTER("open_grant_tables");
@@ -5814,13 +8156,19 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables)
(tables+5)->init_one_table(C_STRING_WITH_LEN("mysql"),
C_STRING_WITH_LEN("proxies_priv"),
"proxies_priv", TL_WRITE);
- tables[5].open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
+ (tables+5)->open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
+ (tables+6)->init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("roles_mapping"),
+ "roles_mapping", TL_WRITE);
+ (tables+6)->open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
+
tables->next_local= tables->next_global= tables + 1;
(tables+1)->next_local= (tables+1)->next_global= tables + 2;
(tables+2)->next_local= (tables+2)->next_global= tables + 3;
(tables+3)->next_local= (tables+3)->next_global= tables + 4;
(tables+4)->next_local= (tables+4)->next_global= tables + 5;
+ (tables+5)->next_local= (tables+5)->next_global= tables + 6;
#ifdef HAVE_REPLICATION
/*
@@ -5834,11 +8182,13 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables)
account in tests.
*/
tables[0].updating= tables[1].updating= tables[2].updating=
- tables[3].updating= tables[4].updating= tables[5].updating= 1;
+ tables[3].updating= tables[4].updating= tables[5].updating=
+ tables[6].updating= 1;
if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
DBUG_RETURN(1);
tables[0].updating= tables[1].updating= tables[2].updating=
- tables[3].updating= tables[4].updating= tables[5].updating= 0;
+ tables[3].updating= tables[4].updating= tables[5].updating=
+ tables[6].updating= 0;
}
#endif
@@ -5850,8 +8200,7 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables)
DBUG_RETURN(0);
}
-ACL_USER *check_acl_user(LEX_USER *user_name,
- uint *acl_acl_userdx)
+ACL_USER *check_acl_user(LEX_USER *user_name, uint *acl_acl_userdx)
{
ACL_USER *acl_user= 0;
uint counter;
@@ -5860,14 +8209,8 @@ ACL_USER *check_acl_user(LEX_USER *user_name,
for (counter= 0 ; counter < acl_users.elements ; counter++)
{
- const char *user,*host;
acl_user= dynamic_element(&acl_users, counter, ACL_USER*);
- if (!(user=acl_user->user))
- user= "";
- if (!(host=acl_user->host.hostname))
- host= "";
- if (!strcmp(user_name->user.str,user) &&
- !my_strcasecmp(system_charset_info, user_name->host.str, host))
+ if(acl_user->eq(user_name->user.str, user_name->host.str))
break;
}
if (counter == acl_users.elements)
@@ -5911,7 +8254,7 @@ static int modify_grant_table(TABLE *table, Field *host_field,
system_charset_info);
user_field->store(user_to->user.str, user_to->user.length,
system_charset_info);
- if ((error= table->file->ha_update_row(table->record[1],
+ if ((error= table->file->ha_update_row(table->record[1],
table->record[0])) &&
error != HA_ERR_RECORD_IS_THE_SAME)
table->file->print_error(error, MYF(0));
@@ -5929,6 +8272,89 @@ static int modify_grant_table(TABLE *table, Field *host_field,
}
/*
+ Handle the roles_mappings privilege table
+*/
+static int handle_roles_mappings_table(TABLE *table, bool drop,
+ LEX_USER *user_from, LEX_USER *user_to)
+{
+ /*
+ All entries (Host, User) that match user_from will be renamed,
+ as well as all Role entries that match if user_from.host.str == ""
+
+ Otherwise, only matching (Host, User) will be renamed.
+ */
+ DBUG_ENTER("handle_roles_mappings_table");
+
+ int error;
+ int result= 0;
+ THD *thd= current_thd;
+ const char *host, *user, *role;
+ Field *host_field= table->field[0];
+ Field *user_field= table->field[1];
+ Field *role_field= table->field[2];
+
+ DBUG_PRINT("info", ("Rewriting entry in roles_mappings table: %s@%s",
+ user_from->user.str, user_from->host.str));
+ table->use_all_columns();
+ if ((error= table->file->ha_rnd_init(1)))
+ {
+ table->file->print_error(error, MYF(0));
+ result= -1;
+ }
+ else
+ {
+ while((error= table->file->ha_rnd_next(table->record[0])) !=
+ HA_ERR_END_OF_FILE)
+ {
+ if (error)
+ {
+ DBUG_PRINT("info", ("scan error: %d", error));
+ continue;
+ }
+
+ host= safe_str(get_field(thd->mem_root, host_field));
+ user= safe_str(get_field(thd->mem_root, user_field));
+
+ if (!(strcmp(user_from->user.str, user) ||
+ my_strcasecmp(system_charset_info, user_from->host.str, host)))
+ result= ((drop || user_to) &&
+ modify_grant_table(table, host_field, user_field, user_to)) ?
+ -1 : result ? result : 1; /* Error or keep result or found. */
+ else
+ {
+ role= safe_str(get_field(thd->mem_root, role_field));
+
+ if (strcmp(user_from->user.str, role))
+ continue;
+
+ error= 0;
+
+ if (drop) /* drop if requested */
+ {
+ if ((error= table->file->ha_delete_row(table->record[0])))
+ table->file->print_error(error, MYF(0));
+ }
+ else if (user_to)
+ {
+ store_record(table, record[1]);
+ role_field->store(user_to->user.str, user_to->user.length,
+ system_charset_info);
+ if ((error= table->file->ha_update_row(table->record[1],
+ table->record[0])) &&
+ error != HA_ERR_RECORD_IS_THE_SAME)
+ table->file->print_error(error, MYF(0));
+ }
+
+ /* Error or keep result or found. */
+ result= error ? -1 : result ? result : 1;
+ }
+ }
+ table->file->ha_rnd_end();
+ }
+ DBUG_RETURN(result);
+}
+
+/*
Handle a privilege table.
SYNOPSIS
@@ -5953,6 +8379,8 @@ static int modify_grant_table(TABLE *table, Field *host_field,
2 tables_priv
3 columns_priv
4 procs_priv
+ 5 proxies_priv
+ 6 roles_mapping
RETURN
> 0 At least one record matched.
@@ -5968,8 +8396,8 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
TABLE *table= tables[table_no].table;
Field *host_field= table->field[0];
Field *user_field= table->field[table_no && table_no != 5 ? 2 : 1];
- char *host_str= user_from->host.str;
- char *user_str= user_from->user.str;
+ const char *host_str= user_from->host.str;
+ const char *user_str= user_from->user.str;
const char *host;
const char *user;
uchar user_key[MAX_KEY_LENGTH];
@@ -5977,6 +8405,12 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
DBUG_ENTER("handle_grant_table");
THD *thd= current_thd;
+ if (table_no == 6)
+ {
+ result= handle_roles_mappings_table(table, drop, user_from, user_to);
+ DBUG_RETURN(result);
+ }
+
table->use_all_columns();
if (! table_no) // mysql.user table
{
@@ -5998,9 +8432,15 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
table->key_info->key_part[1].store_length);
key_copy(user_key, table->record[0], table->key_info, key_prefix_length);
- if ((error= table->file->ha_index_read_idx_map(table->record[0], 0,
- user_key, (key_part_map)3,
- HA_READ_KEY_EXACT)))
+ error= table->file->ha_index_read_idx_map(table->record[0], 0,
+ user_key, (key_part_map)3,
+ HA_READ_KEY_EXACT);
+ if (!error && !*host_str)
+ { // verify that we got a role or a user, as needed
+ if (check_is_role(table) != user_from->is_role())
+ error= HA_ERR_KEY_NOT_FOUND;
+ }
+ if (error)
{
if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE)
{
@@ -6035,7 +8475,7 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
DBUG_PRINT("info",("scan table: '%s' search: '%s'@'%s'",
table->s->table_name.str, user_str, host_str));
#endif
- while ((error= table->file->ha_rnd_next(table->record[0])) !=
+ while ((error= table->file->ha_rnd_next(table->record[0])) !=
HA_ERR_END_OF_FILE)
{
if (error)
@@ -6044,10 +8484,8 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
DBUG_PRINT("info",("scan error: %d", error));
continue;
}
- if (! (host= get_field(thd->mem_root, host_field)))
- host= "";
- if (! (user= get_field(thd->mem_root, user_field)))
- user= "";
+ host= safe_str(get_field(thd->mem_root, host_field));
+ user= safe_str(get_field(thd->mem_root, user_field));
#ifdef EXTRA_DEBUG
if (table_no != 5)
@@ -6084,7 +8522,7 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
/**
Handle an in-memory privilege structure.
- @param struct_no The number of the structure to handle (0..5).
+ @param struct_no The number of the structure to handle (0..6).
@param drop If user_from is to be dropped.
@param user_from The the user to be searched/dropped/renamed.
@param user_to The new name for the user if to be renamed, NULL otherwise.
@@ -6095,13 +8533,6 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
Delete from grant structure if drop is true.
Update in grant structure if drop is false and user_to is not NULL.
Search in grant structure if drop is false and user_to is NULL.
- Structures are enumerated as follows:
- 0 ACL_USER
- 1 ACL_DB
- 2 COLUMN_PRIVILEGES_HASH
- 3 PROC_PRIVILEGES_HASH
- 4 FUNC_PRIVILEGES_HASH
- 5 PROXY_USERS_ACL
@retval > 0 At least one element matched.
@retval 0 OK, but no element matched.
@@ -6113,22 +8544,76 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
int result= 0;
int idx;
int elements;
- const char *user;
- const char *host;
+ const char *UNINIT_VAR(user);
+ const char *UNINIT_VAR(host);
+ const char *UNINIT_VAR(role);
+ uint role_not_matched= 1;
ACL_USER *acl_user= NULL;
+ ACL_ROLE *acl_role= NULL;
ACL_DB *acl_db= NULL;
ACL_PROXY_USER *acl_proxy_user= NULL;
GRANT_NAME *grant_name= NULL;
+ ROLE_GRANT_PAIR *UNINIT_VAR(role_grant_pair);
HASH *grant_name_hash= NULL;
+ HASH *roles_mappings_hash= NULL;
DBUG_ENTER("handle_grant_struct");
DBUG_PRINT("info",("scan struct: %u search: '%s'@'%s'",
struct_no, user_from->user.str, user_from->host.str));
- LINT_INIT(user);
- LINT_INIT(host);
-
mysql_mutex_assert_owner(&acl_cache->lock);
+ /* No point in querying ROLE ACL if user_from is not a role */
+ if (struct_no == ROLE_ACL && user_from->host.length)
+ DBUG_RETURN(0);
+
+ /* same. no roles in PROXY_USERS_ACL */
+ if (struct_no == PROXY_USERS_ACL && user_from->is_role())
+ DBUG_RETURN(0);
+
+ if (struct_no == ROLE_ACL) //no need to scan the structures in this case
+ {
+ acl_role= find_acl_role(user_from->user.str);
+ if (!acl_role)
+ DBUG_RETURN(0);
+
+ if (!drop && !user_to) //role was found
+ DBUG_RETURN(1);
+
+ /* this calls for a role update */
+ char *old_key= acl_role->user.str;
+ size_t old_key_length= acl_role->user.length;
+ if (drop)
+ {
+ /* all grants must be revoked from this role by now. propagate this */
+ propagate_role_grants(acl_role, PRIVS_TO_MERGE::ALL, 0, 0);
+
+ // delete the role from cross-reference arrays
+ for (uint i=0; i < acl_role->role_grants.elements; i++)
+ {
+ ACL_ROLE *grant= *dynamic_element(&acl_role->role_grants,
+ i, ACL_ROLE**);
+ remove_ptr_from_dynarray(&grant->parent_grantee, acl_role);
+ }
+
+ for (uint i=0; i < acl_role->parent_grantee.elements; i++)
+ {
+ ACL_USER_BASE *grantee= *dynamic_element(&acl_role->parent_grantee,
+ i, ACL_USER_BASE**);
+ remove_ptr_from_dynarray(&grantee->role_grants, acl_role);
+ }
+
+ my_hash_delete(&acl_roles, (uchar*) acl_role);
+ DBUG_RETURN(1);
+ }
+ acl_role->user.str= strdup_root(&mem, user_to->user.str);
+ acl_role->user.length= user_to->user.length;
+
+ my_hash_update(&acl_roles, (uchar*) acl_role, (uchar*) old_key,
+ old_key_length);
+ DBUG_RETURN(1);
+
+ }
+
/* Get the number of elements in the in-memory structure. */
switch (struct_no) {
case USER_ACL:
@@ -6152,9 +8637,13 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
case PROXY_USERS_ACL:
elements= acl_proxy_users.elements;
break;
+ case ROLES_MAPPINGS_HASH:
+ roles_mappings_hash= &acl_roles_mappings;
+ elements= roles_mappings_hash->records;
+ break;
default:
DBUG_ASSERT(0);
- return -1;
+ DBUG_RETURN(-1);
}
#ifdef EXTRA_DEBUG
@@ -6170,7 +8659,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
switch (struct_no) {
case USER_ACL:
acl_user= dynamic_element(&acl_users, idx, ACL_USER*);
- user= acl_user->user;
+ user= acl_user->user.str;
host= acl_user->host.hostname;
break;
@@ -6194,6 +8683,13 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
host= acl_proxy_user->get_host();
break;
+ case ROLES_MAPPINGS_HASH:
+ role_grant_pair= (ROLE_GRANT_PAIR *) my_hash_element(roles_mappings_hash, idx);
+ user= role_grant_pair->u_uname;
+ host= role_grant_pair->u_hname;
+ role= role_grant_pair->r_uname;
+ break;
+
default:
DBUG_ASSERT(0);
}
@@ -6201,14 +8697,28 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
user= "";
if (! host)
host= "";
+ if (! role)
+ role= "";
#ifdef EXTRA_DEBUG
DBUG_PRINT("loop",("scan struct: %u index: %u user: '%s' host: '%s'",
struct_no, idx, user, host));
#endif
- if (strcmp(user_from->user.str, user) ||
- my_strcasecmp(system_charset_info, user_from->host.str, host))
+
+ if (struct_no == ROLES_MAPPINGS_HASH)
+ {
+ role_not_matched= strcmp(user_from->user.str, role);
+ if (role_not_matched &&
+ (strcmp(user_from->user.str, user) ||
+ my_strcasecmp(system_charset_info, user_from->host.str, host)))
continue;
+ }
+ else
+ {
+ if (strcmp(user_from->user.str, user) ||
+ my_strcasecmp(system_charset_info, user_from->host.str, host))
+ continue;
+ }
result= 1; /* At least one element found. */
if ( drop )
@@ -6216,6 +8726,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
elements--;
switch ( struct_no ) {
case USER_ACL:
+ free_acl_user(dynamic_element(&acl_users, idx, ACL_USER*));
delete_dynamic_element(&acl_users, idx);
break;
@@ -6242,13 +8753,21 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
delete_dynamic_element(&acl_proxy_users, idx);
break;
+ case ROLES_MAPPINGS_HASH:
+ my_hash_delete(roles_mappings_hash, (uchar*) role_grant_pair);
+ break;
+
+ default:
+ DBUG_ASSERT(0);
+ break;
}
}
else if ( user_to )
{
switch ( struct_no ) {
case USER_ACL:
- acl_user->user= strdup_root(&mem, user_to->user.str);
+ acl_user->user.str= strdup_root(&mem, user_to->user.str);
+ acl_user->user.length= user_to->user.length;
acl_user->host.hostname= strdup_root(&mem, user_to->host.str);
break;
@@ -6262,7 +8781,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
case FUNC_PRIVILEGES_HASH:
{
/*
- Save old hash key and its length to be able properly update
+ Save old hash key and its length to be able to properly update
element position in hash.
*/
char *old_key= grant_name->hash_key;
@@ -6299,7 +8818,38 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop,
acl_proxy_user->set_user (&mem, user_to->user.str);
acl_proxy_user->set_host (&mem, user_to->host.str);
break;
+
+ case ROLES_MAPPINGS_HASH:
+ {
+ /*
+ Save old hash key and its length to be able to properly update
+ element position in hash.
+ */
+ char *old_key= role_grant_pair->hashkey.str;
+ size_t old_key_length= role_grant_pair->hashkey.length;
+ bool oom;
+
+ if (role_not_matched)
+ oom= role_grant_pair->init(&mem, user_to->user.str,
+ user_to->host.str,
+ role_grant_pair->r_uname, false);
+ else
+ oom= role_grant_pair->init(&mem, role_grant_pair->u_uname,
+ role_grant_pair->u_hname,
+ user_to->user.str, false);
+ if (oom)
+ DBUG_RETURN(-1);
+
+ my_hash_update(roles_mappings_hash, (uchar*) role_grant_pair,
+ (uchar*) old_key, old_key_length);
+ break;
+ }
+
+ default:
+ DBUG_ASSERT(0);
+ break;
}
+
}
else
{
@@ -6344,24 +8894,21 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
{
int result= 0;
int found;
+ bool handle_as_role= user_from->is_role();
+ bool search_only= !drop && !user_to;
DBUG_ENTER("handle_grant_data");
- /* Handle user table. */
- if ((found= handle_grant_table(tables, 0, drop, user_from, user_to)) < 0)
- {
- /* Handle of table failed, don't touch the in-memory array. */
- result= -1;
- }
- else
+ if (user_to)
+ DBUG_ASSERT(handle_as_role == user_to->is_role());
+
+ if (search_only)
{
- /* Handle user array. */
- if ((handle_grant_struct(USER_ACL, drop, user_from, user_to)) || found)
- {
- result= 1; /* At least one record/element found. */
- /* If search is requested, we do not need to search further. */
- if (! drop && ! user_to)
- goto end;
- }
+ /* quickly search in-memory structures first */
+ if (handle_as_role && find_acl_role(user_from->user.str))
+ DBUG_RETURN(1); // found
+
+ if (!handle_as_role && find_user_exact(user_from->host.str, user_from->user.str))
+ DBUG_RETURN(1); // found
}
/* Handle db table. */
@@ -6373,13 +8920,14 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
else
{
/* Handle db array. */
- if (((handle_grant_struct(DB_ACL, drop, user_from, user_to) && ! result) ||
- found) && ! result)
+ if ((handle_grant_struct(DB_ACL, drop, user_from, user_to) || found)
+ && ! result)
{
result= 1; /* At least one record/element found. */
/* If search is requested, we do not need to search further. */
- if (! drop && ! user_to)
+ if (search_only)
goto end;
+ acl_cache->clear(1);
}
}
@@ -6392,21 +8940,21 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
else
{
/* Handle procs array. */
- if (((handle_grant_struct(PROC_PRIVILEGES_HASH, drop, user_from, user_to) && ! result) ||
- found) && ! result)
+ if ((handle_grant_struct(PROC_PRIVILEGES_HASH, drop, user_from, user_to) || found)
+ && ! result)
{
result= 1; /* At least one record/element found. */
/* If search is requested, we do not need to search further. */
- if (! drop && ! user_to)
+ if (search_only)
goto end;
}
/* Handle funcs array. */
- if (((handle_grant_struct(FUNC_PRIVILEGES_HASH, drop, user_from, user_to) && ! result) ||
- found) && ! result)
+ if ((handle_grant_struct(FUNC_PRIVILEGES_HASH, drop, user_from, user_to) || found)
+ && ! result)
{
result= 1; /* At least one record/element found. */
/* If search is requested, we do not need to search further. */
- if (! drop && ! user_to)
+ if (search_only)
goto end;
}
}
@@ -6423,7 +8971,7 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
{
result= 1; /* At least one record found. */
/* If search is requested, we do not need to search further. */
- if (! drop && ! user_to)
+ if (search_only)
goto end;
}
@@ -6436,9 +8984,11 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
else
{
/* Handle columns hash. */
- if (((handle_grant_struct(COLUMN_PRIVILEGES_HASH, drop, user_from, user_to) && ! result) ||
- found) && ! result)
+ if ((handle_grant_struct(COLUMN_PRIVILEGES_HASH, drop, user_from, user_to) || found)
+ && ! result)
result= 1; /* At least one record/element found. */
+ if (search_only)
+ goto end;
}
}
@@ -6453,28 +9003,68 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
else
{
/* Handle proxies_priv array. */
- if ((handle_grant_struct(PROXY_USERS_ACL, drop, user_from, user_to) && !result) ||
- found)
+ if ((handle_grant_struct(PROXY_USERS_ACL, drop, user_from, user_to) || found)
+ && ! result)
result= 1; /* At least one record/element found. */
+ if (search_only)
+ goto end;
}
}
- end:
+
+ /* Handle roles_mappings table. */
+ if (tables[6].table)
+ {
+ if ((found= handle_grant_table(tables, 6, drop, user_from, user_to)) < 0)
+ {
+ /* Handle of table failed, don't touch the in-memory array. */
+ result= -1;
+ }
+ else
+ {
+ /* Handle acl_roles_mappings array */
+ if ((handle_grant_struct(ROLES_MAPPINGS_HASH, drop, user_from, user_to) || found)
+ && ! result)
+ result= 1; /* At least one record/element found */
+ if (search_only)
+ goto end;
+ }
+ }
+
+ /* Handle user table. */
+ if ((found= handle_grant_table(tables, 0, drop, user_from, user_to)) < 0)
+ {
+ /* Handle of table failed, don't touch the in-memory array. */
+ result= -1;
+ }
+ else
+ {
+ enum enum_acl_lists what= handle_as_role ? ROLE_ACL : USER_ACL;
+ if (((handle_grant_struct(what, drop, user_from, user_to)) || found) && !result)
+ {
+ result= 1; /* At least one record/element found. */
+ DBUG_ASSERT(! search_only);
+ }
+ }
+
+end:
DBUG_RETURN(result);
}
-
static void append_user(String *str, LEX_USER *user)
{
if (str->length())
str->append(',');
str->append('\'');
str->append(user->user.str);
- str->append(STRING_WITH_LEN("'@'"));
- str->append(user->host.str);
+ /* hostname part is not relevant for roles, it is always empty */
+ if (!user->is_role())
+ {
+ str->append(STRING_WITH_LEN("'@'"));
+ str->append(user->host.str);
+ }
str->append('\'');
}
-
/*
Create a list of users.
@@ -6482,21 +9072,26 @@ static void append_user(String *str, LEX_USER *user)
mysql_create_user()
thd The current thread.
list The users to create.
+ handle_as_role Handle the user list as roles if true
RETURN
FALSE OK.
TRUE Error.
*/
-bool mysql_create_user(THD *thd, List <LEX_USER> &list)
+bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
{
int result;
String wrong_users;
- LEX_USER *user_name, *tmp_user_name;
+ LEX_USER *user_name;
List_iterator <LEX_USER> user_list(list);
TABLE_LIST tables[GRANT_TABLES];
bool some_users_created= FALSE;
DBUG_ENTER("mysql_create_user");
+ DBUG_PRINT("entry", ("Handle as %s", handle_as_role ? "role" : "user"));
+
+ if (handle_as_role && sp_process_definer(thd))
+ DBUG_RETURN(TRUE);
/* CREATE USER may be skipped on replication client. */
if ((result= open_grant_tables(thd, tables)))
@@ -6505,21 +9100,26 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list)
mysql_rwlock_wrlock(&LOCK_grant);
mysql_mutex_lock(&acl_cache->lock);
- while ((tmp_user_name= user_list++))
+ while ((user_name= user_list++))
{
- if (!(user_name= get_current_user(thd, tmp_user_name)))
+ if (handle_as_role && is_invalid_role_name(user_name->user.str))
{
+ append_user(&wrong_users, user_name);
result= TRUE;
continue;
}
+ if (!user_name->host.str)
+ user_name->host= host_not_specified;
+
/*
Search all in-memory structures and grant tables
- for a mention of the new user name.
+ for a mention of the new user/role name.
*/
if (handle_grant_data(tables, 0, user_name, NULL))
{
append_user(&wrong_users, user_name);
+
result= TRUE;
continue;
}
@@ -6529,13 +9129,43 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list)
{
append_user(&wrong_users, user_name);
result= TRUE;
+ continue;
+ }
+
+ // every created role is automatically granted to its creator-admin
+ if (handle_as_role)
+ {
+ ACL_USER_BASE *grantee= find_acl_user_base(thd->lex->definer->user.str,
+ thd->lex->definer->host.str);
+ ACL_ROLE *role= find_acl_role(user_name->user.str);
+
+ /*
+ just like with routines, views, triggers, and events we allow
+ non-existant definers here with a warning (see sp_process_definer())
+ */
+ if (grantee)
+ add_role_user_mapping(grantee, role);
+
+ if (replace_roles_mapping_table(tables[6].table,
+ &thd->lex->definer->user,
+ &thd->lex->definer->host,
+ &user_name->user, true,
+ NULL, false))
+ {
+ append_user(&wrong_users, user_name);
+ if (grantee)
+ undo_add_role_user_mapping(grantee, role);
+ result= TRUE;
+ }
}
}
mysql_mutex_unlock(&acl_cache->lock);
if (result)
- my_error(ER_CANNOT_USER, MYF(0), "CREATE USER", wrong_users.c_ptr_safe());
+ my_error(ER_CANNOT_USER, MYF(0),
+ (handle_as_role) ? "CREATE ROLE" : "CREATE USER",
+ wrong_users.c_ptr_safe());
if (some_users_created)
result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
@@ -6544,7 +9174,6 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list)
DBUG_RETURN(result);
}
-
/*
Drop a list of users and all their privileges.
@@ -6558,7 +9187,7 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list)
TRUE Error.
*/
-bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
+bool mysql_drop_user(THD *thd, List <LEX_USER> &list, bool handle_as_role)
{
int result;
String wrong_users;
@@ -6568,6 +9197,7 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
bool some_users_deleted= FALSE;
ulonglong old_sql_mode= thd->variables.sql_mode;
DBUG_ENTER("mysql_drop_user");
+ DBUG_PRINT("entry", ("Handle as %s", handle_as_role ? "role" : "user"));
/* DROP USER may be skipped on replication client. */
if ((result= open_grant_tables(thd, tables)))
@@ -6580,27 +9210,42 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
while ((tmp_user_name= user_list++))
{
- if (!(user_name= get_current_user(thd, tmp_user_name)))
+ user_name= get_current_user(thd, tmp_user_name, false);
+ if (!user_name || handle_as_role != user_name->is_role())
{
+ append_user(&wrong_users, tmp_user_name);
result= TRUE;
continue;
- }
+ }
+
if (handle_grant_data(tables, 1, user_name, NULL) <= 0)
{
append_user(&wrong_users, user_name);
result= TRUE;
continue;
}
+
some_users_deleted= TRUE;
}
- /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
- rebuild_check_host();
+ if (!handle_as_role)
+ {
+ /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
+ rebuild_check_host();
+
+ /*
+ Rebuild every user's role_grants since 'acl_users' has been sorted
+ and old pointers to ACL_USER elements are no longer valid
+ */
+ rebuild_role_grants();
+ }
mysql_mutex_unlock(&acl_cache->lock);
if (result)
- my_error(ER_CANNOT_USER, MYF(0), "DROP USER", wrong_users.c_ptr_safe());
+ my_error(ER_CANNOT_USER, MYF(0),
+ (handle_as_role) ? "DROP ROLE" : "DROP USER",
+ wrong_users.c_ptr_safe());
if (some_users_deleted)
result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
@@ -6610,7 +9255,6 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list)
DBUG_RETURN(result);
}
-
/*
Rename a user.
@@ -6646,18 +9290,21 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
while ((tmp_user_from= user_list++))
{
- if (!(user_from= get_current_user(thd, tmp_user_from)))
+ tmp_user_to= user_list++;
+ if (!(user_from= get_current_user(thd, tmp_user_from, false)) ||
+ user_from->is_role())
{
+ append_user(&wrong_users, user_from);
result= TRUE;
continue;
- }
- tmp_user_to= user_list++;
- if (!(user_to= get_current_user(thd, tmp_user_to)))
+ }
+ if (!(user_to= get_current_user(thd, tmp_user_to, false)) ||
+ user_to->is_role())
{
+ append_user(&wrong_users, user_to);
result= TRUE;
continue;
- }
- DBUG_ASSERT(user_to != 0); /* Syntax enforces pairs of users. */
+ }
/*
Search all in-memory structures and grant tables
@@ -6666,21 +9313,28 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list)
if (handle_grant_data(tables, 0, user_to, NULL) ||
handle_grant_data(tables, 0, user_from, user_to) <= 0)
{
+ /* NOTE TODO renaming roles is not yet implemented */
append_user(&wrong_users, user_from);
result= TRUE;
continue;
}
some_users_renamed= TRUE;
}
-
+
/* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */
rebuild_check_host();
+ /*
+ Rebuild every user's role_grants since 'acl_users' has been sorted
+ and old pointers to ACL_USER elements are no longer valid
+ */
+ rebuild_role_grants();
+
mysql_mutex_unlock(&acl_cache->lock);
if (result)
my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr_safe());
-
+
if (some_users_renamed && mysql_bin_log.is_open())
result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length());
@@ -6723,19 +9377,21 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
List_iterator <LEX_USER> user_list(list);
while ((tmp_lex_user= user_list++))
{
- if (!(lex_user= get_current_user(thd, tmp_lex_user)))
+ if (!(lex_user= get_current_user(thd, tmp_lex_user, false)))
{
result= -1;
continue;
- }
- if (!find_acl_user(lex_user->host.str, lex_user->user.str, TRUE))
+ }
+
+ /* This is not a role and the user could not be found */
+ if (!lex_user->is_role() &&
+ !find_user_exact(lex_user->host.str, lex_user->user.str))
{
result= -1;
continue;
}
- if (replace_user_table(thd, tables[0].table,
- *lex_user, ~(ulong)0, 1, 0, 0))
+ if (replace_user_table(thd, tables[0].table, *lex_user, ~(ulong)0, 1, 0, 0))
{
result= -1;
continue;
@@ -6754,12 +9410,11 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
const char *user,*host;
acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
- if (!(user=acl_db->user))
- user= "";
- if (!(host=acl_db->host.hostname))
- host= "";
- if (!strcmp(lex_user->user.str,user) &&
+ user= safe_str(acl_db->user);
+ host= safe_str(acl_db->host.hostname);
+
+ if (!strcmp(lex_user->user.str, user) &&
!strcmp(lex_user->host.str, host))
{
if (!replace_db_table(tables[1].table, acl_db->db, *lex_user,
@@ -6786,10 +9441,8 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
const char *user,*host;
GRANT_TABLE *grant_table=
(GRANT_TABLE*) my_hash_element(&column_priv_hash, counter);
- if (!(user=grant_table->user))
- user= "";
- if (!(host=grant_table->host.hostname))
- host= "";
+ user= safe_str(grant_table->user);
+ host= safe_str(grant_table->host.hostname);
if (!strcmp(lex_user->user.str,user) &&
!strcmp(lex_user->host.str, host))
@@ -6832,10 +9485,8 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
{
const char *user,*host;
GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter);
- if (!(user=grant_proc->user))
- user= "";
- if (!(host=grant_proc->host.hostname))
- host= "";
+ user= safe_str(grant_proc->user);
+ host= safe_str(grant_proc->host.hostname);
if (!strcmp(lex_user->user.str,user) &&
!strcmp(lex_user->host.str, host))
@@ -6854,6 +9505,59 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list)
counter++;
}
} while (revoked);
+
+ ACL_USER_BASE *user_or_role;
+ /* remove role grants */
+ if (lex_user->is_role())
+ {
+ /* this can not fail due to get_current_user already having searched for it */
+ user_or_role= find_acl_role(lex_user->user.str);
+ }
+ else
+ {
+ user_or_role= find_user_exact(lex_user->host.str, lex_user->user.str);
+ }
+ /*
+ Find every role grant pair matching the role_grants array and remove it,
+ both from the acl_roles_mappings and the roles_mapping table
+ */
+ for (counter= 0; counter < user_or_role->role_grants.elements; counter++)
+ {
+ ACL_ROLE *role_grant= *dynamic_element(&user_or_role->role_grants,
+ counter, ACL_ROLE**);
+ ROLE_GRANT_PAIR *pair = find_role_grant_pair(&lex_user->user,
+ &lex_user->host,
+ &role_grant->user);
+ if (replace_roles_mapping_table(tables[6].table,
+ &lex_user->user,
+ &lex_user->host,
+ &role_grant->user, false, pair, true))
+ {
+ result= -1; //Something went wrong
+ }
+ /*
+ Delete from the parent_grantee array of the roles granted,
+ the entry pointing to this user_or_role
+ */
+ remove_ptr_from_dynarray(&role_grant->parent_grantee, user_or_role);
+ }
+ /* TODO
+ How to handle an error in the replace_roles_mapping_table, in
+ regards to the privileges held in memory
+ */
+
+ /* Finally, clear the role_grants array */
+ if (counter == user_or_role->role_grants.elements)
+ {
+ reset_dynamic(&user_or_role->role_grants);
+ }
+ /*
+ If we are revoking from a role, we need to update all the parent grantees
+ */
+ if (lex_user->is_role())
+ {
+ propagate_role_grants((ACL_ROLE *)user_or_role, PRIVS_TO_MERGE::ALL, 0, 0);
+ }
}
mysql_mutex_unlock(&acl_cache->lock);
@@ -6981,11 +9685,8 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name,
LEX_USER lex_user;
lex_user.user.str= grant_proc->user;
lex_user.user.length= strlen(grant_proc->user);
- lex_user.host.str= grant_proc->host.hostname ?
- grant_proc->host.hostname : (char*)"";
- lex_user.host.length= grant_proc->host.hostname ?
- strlen(grant_proc->host.hostname) : 0;
-
+ lex_user.host.str= safe_str(grant_proc->host.hostname);
+ lex_user.host.length= strlen(lex_user.host.str);
if (replace_routine_table(thd,grant_proc,tables[4].table,lex_user,
grant_proc->db, grant_proc->tname,
is_proc, ~(ulong)0, 1) == 0)
@@ -7040,13 +9741,13 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
mysql_mutex_lock(&acl_cache->lock);
- if ((au= find_acl_user(combo->host.str=(char*)sctx->host_or_ip,combo->user.str,FALSE)))
+ if ((au= find_user_wild(combo->host.str=(char*)sctx->host_or_ip, combo->user.str)))
goto found_acl;
- if ((au= find_acl_user(combo->host.str=(char*)sctx->host, combo->user.str,FALSE)))
+ if ((au= find_user_wild(combo->host.str=(char*)sctx->host, combo->user.str)))
goto found_acl;
- if ((au= find_acl_user(combo->host.str=(char*)sctx->ip, combo->user.str,FALSE)))
+ if ((au= find_user_wild(combo->host.str=(char*)sctx->ip, combo->user.str)))
goto found_acl;
- if((au= find_acl_user(combo->host.str=(char*)"%", combo->user.str, FALSE)))
+ if ((au= find_user_wild(combo->host.str=(char*)"%", combo->user.str)))
goto found_acl;
mysql_mutex_unlock(&acl_cache->lock);
@@ -7123,7 +9824,7 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
@thd current thread
@param user the logged in user (proxy user)
- @param authenticated_as the effective user a plugin is trying to
+ @param authenticated_as the effective user a plugin is trying to
impersonate as (proxied user)
@return proxy user definition
@retval NULL proxy user definition not found or not applicable
@@ -7131,7 +9832,7 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
*/
static ACL_PROXY_USER *
-acl_find_proxy_user(const char *user, const char *host, const char *ip,
+acl_find_proxy_user(const char *user, const char *host, const char *ip,
const char *authenticated_as, bool *proxy_used)
{
uint i;
@@ -7146,10 +9847,10 @@ acl_find_proxy_user(const char *user, const char *host, const char *ip,
DBUG_RETURN (NULL);
}
- *proxy_used= TRUE;
+ *proxy_used= TRUE;
for (i=0; i < acl_proxy_users.elements; i++)
{
- ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
+ ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
ACL_PROXY_USER *);
if (proxy->matches(host, user, ip, authenticated_as))
DBUG_RETURN(proxy);
@@ -7164,7 +9865,7 @@ acl_check_proxy_grant_access(THD *thd, const char *host, const char *user,
bool with_grant)
{
DBUG_ENTER("acl_check_proxy_grant_access");
- DBUG_PRINT("info", ("user=%s host=%s with_grant=%d", user, host,
+ DBUG_PRINT("info", ("user=%s host=%s with_grant=%d", user, host,
(int) with_grant));
if (!initialized)
{
@@ -7195,7 +9896,7 @@ acl_check_proxy_grant_access(THD *thd, const char *host, const char *user,
!my_strcasecmp(system_charset_info, host,
thd->security_ctx->priv_host))
{
- DBUG_PRINT("info", ("strcmp (%s, %s) my_casestrcmp (%s, %s) equal",
+ DBUG_PRINT("info", ("strcmp (%s, %s) my_casestrcmp (%s, %s) equal",
thd->security_ctx->priv_user, user,
host, thd->security_ctx->priv_host));
DBUG_RETURN(FALSE);
@@ -7204,7 +9905,7 @@ acl_check_proxy_grant_access(THD *thd, const char *host, const char *user,
/* check for matching WITH PROXY rights */
for (uint i=0; i < acl_proxy_users.elements; i++)
{
- ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
+ ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
ACL_PROXY_USER *);
if (proxy->matches(thd->security_ctx->host,
thd->security_ctx->user,
@@ -7225,7 +9926,8 @@ acl_check_proxy_grant_access(THD *thd, const char *host, const char *user,
static bool
-show_proxy_grants(THD *thd, LEX_USER *user, char *buff, size_t buffsize)
+show_proxy_grants(THD *thd, const char *username, const char *hostname,
+ char *buff, size_t buffsize)
{
Protocol *protocol= thd->protocol;
int error= 0;
@@ -7234,7 +9936,7 @@ show_proxy_grants(THD *thd, LEX_USER *user, char *buff, size_t buffsize)
{
ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i,
ACL_PROXY_USER *);
- if (proxy->granted_on(user->host.str, user->user.str))
+ if (proxy->granted_on(hostname, username))
{
String global(buff, buffsize, system_charset_info);
global.length(0);
@@ -7251,9 +9953,119 @@ show_proxy_grants(THD *thd, LEX_USER *user, char *buff, size_t buffsize)
return error;
}
+static int enabled_roles_insert(ACL_USER_BASE *role, void *context_data)
+{
+ TABLE *table= (TABLE*) context_data;
+ DBUG_ASSERT(role->flags & IS_ROLE);
+
+ restore_record(table, s->default_values);
+ table->field[0]->set_notnull();
+ table->field[0]->store(role->user.str, role->user.length,
+ system_charset_info);
+ if (schema_table_store_record(table->in_use, table))
+ return -1;
+ return 0;
+}
+
+struct APPLICABLE_ROLES_DATA
+{
+ TABLE *table;
+ const LEX_STRING host;
+ const LEX_STRING user_and_host;
+ ACL_USER_BASE *user;
+};
+
+static int
+applicable_roles_insert(ACL_USER_BASE *grantee, ACL_ROLE *role, void *ptr)
+{
+ APPLICABLE_ROLES_DATA *data= (APPLICABLE_ROLES_DATA *)ptr;
+ CHARSET_INFO *cs= system_charset_info;
+ TABLE *table= data->table;
+ bool is_role= grantee != data->user;
+ const LEX_STRING *user_and_host= is_role ? &grantee->user
+ : &data->user_and_host;
+ const LEX_STRING *host= is_role ? &empty_lex_str : &data->host;
+
+ restore_record(table, s->default_values);
+ table->field[0]->store(user_and_host->str, user_and_host->length, cs);
+ table->field[1]->store(role->user.str, role->user.length, cs);
+
+ ROLE_GRANT_PAIR *pair=
+ find_role_grant_pair(&grantee->user, host, &role->user);
+ DBUG_ASSERT(pair);
+
+ if (pair->with_admin)
+ table->field[2]->store(STRING_WITH_LEN("YES"), cs);
+ else
+ table->field[2]->store(STRING_WITH_LEN("NO"), cs);
+
+ if (schema_table_store_record(table->in_use, table))
+ return -1;
+ return 0;
+}
#endif /*NO_EMBEDDED_ACCESS_CHECKS */
+int fill_schema_enabled_roles(THD *thd, TABLE_LIST *tables, COND *cond)
+{
+ TABLE *table= tables->table;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (thd->security_ctx->priv_role[0])
+ {
+ mysql_rwlock_rdlock(&LOCK_grant);
+ mysql_mutex_lock(&acl_cache->lock);
+ ACL_ROLE *acl_role= find_acl_role(thd->security_ctx->priv_role);
+ if (acl_role)
+ traverse_role_graph_down(acl_role, table, enabled_roles_insert, NULL);
+ mysql_mutex_unlock(&acl_cache->lock);
+ mysql_rwlock_unlock(&LOCK_grant);
+ if (acl_role)
+ return 0;
+ }
+#endif
+
+ restore_record(table, s->default_values);
+ table->field[0]->set_null();
+ return schema_table_store_record(table->in_use, table);
+}
+
+
+/*
+ This shows all roles granted to current user
+ and recursively all roles granted to those roles
+*/
+int fill_schema_applicable_roles(THD *thd, TABLE_LIST *tables, COND *cond)
+{
+ int res= 0;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (initialized)
+ {
+ TABLE *table= tables->table;
+ Security_context *sctx= thd->security_ctx;
+ mysql_rwlock_rdlock(&LOCK_grant);
+ mysql_mutex_lock(&acl_cache->lock);
+ ACL_USER *user= find_user_exact(sctx->priv_host, sctx->priv_user);
+ if (user)
+ {
+ char buff[USER_HOST_BUFF_SIZE+10];
+ DBUG_ASSERT(user->user.length + user->hostname_length +2 < sizeof(buff));
+ char *end= strxmov(buff, user->user.str, "@", user->host.hostname, NULL);
+ APPLICABLE_ROLES_DATA data= { table,
+ { user->host.hostname, user->hostname_length },
+ { buff, (size_t)(end - buff) }, user
+ };
+
+ res= traverse_role_graph_down(user, &data, 0, applicable_roles_insert);
+ }
+
+ mysql_mutex_unlock(&acl_cache->lock);
+ mysql_rwlock_unlock(&LOCK_grant);
+ }
+#endif
+
+ return res;
+}
+
int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr)
{
@@ -7346,16 +10158,14 @@ int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
{
const char *user,*host, *is_grantable="YES";
acl_user=dynamic_element(&acl_users,counter,ACL_USER*);
- if (!(user=acl_user->user))
- user= "";
- if (!(host=acl_user->host.hostname))
- host= "";
+ user= safe_str(acl_user->user.str);
+ host= safe_str(acl_user->host.hostname);
if (no_global_access &&
(strcmp(thd->security_ctx->priv_user, user) ||
my_strcasecmp(system_charset_info, curr_host, host)))
continue;
-
+
want_access= acl_user->access;
if (!(want_access & GRANT_ACL))
is_grantable= "NO";
@@ -7378,7 +10188,7 @@ int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
{
if (test_access & j)
{
- if (update_schema_privilege(thd, table, buff, 0, 0, 0, 0,
+ if (update_schema_privilege(thd, table, buff, 0, 0, 0, 0,
command_array[priv_id],
command_lengths[priv_id], is_grantable))
{
@@ -7422,10 +10232,8 @@ int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
const char *user, *host, *is_grantable="YES";
acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
- if (!(user=acl_db->user))
- user= "";
- if (!(host=acl_db->host.hostname))
- host= "";
+ user= safe_str(acl_db->user);
+ host= safe_str(acl_db->host.hostname);
if (no_global_access &&
(strcmp(thd->security_ctx->priv_user, user) ||
@@ -7495,11 +10303,9 @@ int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
{
const char *user, *host, *is_grantable= "YES";
GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
- index);
- if (!(user=grant_table->user))
- user= "";
- if (!(host= grant_table->host.hostname))
- host= "";
+ index);
+ user= safe_str(grant_table->user);
+ host= safe_str(grant_table->host.hostname);
if (no_global_access &&
(strcmp(thd->security_ctx->priv_user, user) ||
@@ -7549,7 +10355,7 @@ int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
}
}
}
- }
+ }
}
err:
mysql_rwlock_unlock(&LOCK_grant);
@@ -7579,11 +10385,9 @@ int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, COND *cond)
{
const char *user, *host, *is_grantable= "YES";
GRANT_TABLE *grant_table= (GRANT_TABLE*) my_hash_element(&column_priv_hash,
- index);
- if (!(user=grant_table->user))
- user= "";
- if (!(host= grant_table->host.hostname))
- host= "";
+ index);
+ user= safe_str(grant_table->user);
+ host= safe_str(grant_table->host.hostname);
if (no_global_access &&
(strcmp(thd->security_ctx->priv_user, user) ||
@@ -7661,9 +10465,7 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
Security_context *sctx= thd->security_ctx;
DBUG_ENTER("fill_effective_table_privileges");
DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', table: `%s`.`%s`",
- sctx->priv_host, (sctx->ip ? sctx->ip : "(NULL)"),
- (sctx->priv_user ? sctx->priv_user : "(NULL)"),
- db, table));
+ sctx->priv_host, sctx->ip, sctx->priv_user, db, table));
/* --skip-grants */
if (!initialized)
{
@@ -7682,22 +10484,40 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
DBUG_VOID_RETURN; // it is slave
}
- /* db privileges */
- grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0);
+ if (!thd->db || strcmp(db, thd->db))
+ {
+ /* db privileges */
+ grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0);
+ /* db privileges for role */
+ if (sctx->priv_role[0])
+ grant->privilege|= acl_get("", "", sctx->priv_role, db, 0);
+ }
+ else
+ {
+ grant->privilege|= sctx->db_access;
+ }
/* table privileges */
mysql_rwlock_rdlock(&LOCK_grant);
if (grant->version != grant_version)
{
- grant->grant_table=
+ grant->grant_table_user=
table_hash_search(sctx->host, sctx->ip, db,
- sctx->priv_user,
- table, 0); /* purecov: inspected */
+ sctx->priv_user,
+ table, 0); /* purecov: inspected */
+ grant->grant_table_role=
+ sctx->priv_role[0] ? table_hash_search("", "", db,
+ sctx->priv_role,
+ table, TRUE) : NULL;
grant->version= grant_version; /* purecov: inspected */
}
- if (grant->grant_table != 0)
+ if (grant->grant_table_user != 0)
+ {
+ grant->privilege|= grant->grant_table_user->privs;
+ }
+ if (grant->grant_table_role != 0)
{
- grant->privilege|= grant->grant_table->privs;
+ grant->privilege|= grant->grant_table_role->privs;
}
mysql_rwlock_unlock(&LOCK_grant);
@@ -7719,6 +10539,54 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name,
#endif
+/**
+ Return information about user or current user.
+
+ @param[in] thd thread handler
+ @param[in] user user
+ @param[in] lock whether &acl_cache->lock mutex needs to be locked
+
+ @return
+ - On success, return a valid pointer to initialized
+ LEX_USER, which contains user information.
+ - On error, return 0.
+*/
+
+LEX_USER *get_current_user(THD *thd, LEX_USER *user, bool lock)
+{
+ if (user->user.str == current_user.str) // current_user
+ return create_default_definer(thd, false);
+
+ if (user->user.str == current_role.str) // current_role
+ return create_default_definer(thd, true);
+
+ if (user->host.str == NULL) // Possibly a role
+ {
+ // to be reexecution friendly we have to make a copy
+ LEX_USER *dup= (LEX_USER*) thd->memdup(user, sizeof(*user));
+ if (!dup)
+ return 0;
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (is_invalid_role_name(user->user.str))
+ return 0;
+
+ if (lock)
+ mysql_mutex_lock(&acl_cache->lock);
+ if (find_acl_role(dup->user.str))
+ dup->host= empty_lex_str;
+ else
+ dup->host= host_not_specified;
+ if (lock)
+ mysql_mutex_unlock(&acl_cache->lock);
+#endif
+
+ return dup;
+ }
+
+ return user;
+}
+
struct ACL_internal_schema_registry_entry
{
const LEX_STRING *m_name;
@@ -7883,9 +10751,9 @@ static void login_failed_error(THD *thd)
thd->main_security_ctx.host_or_ip,
thd->password ? ER(ER_YES) : ER(ER_NO));
status_var_increment(thd->status_var.access_denied_errors);
- /*
+ /*
Log access denied messages to the error log when log-warnings = 2
- so that the overhead of the general query log is not required to track
+ so that the overhead of the general query log is not required to track
failed connections.
*/
if (global_system_variables.log_warnings > 1)
@@ -7893,7 +10761,7 @@ static void login_failed_error(THD *thd)
sql_print_warning(ER(access_denied_error_code(thd->password)),
thd->main_security_ctx.user,
thd->main_security_ctx.host_or_ip,
- thd->password ? ER(ER_YES) : ER(ER_NO));
+ thd->password ? ER(ER_YES) : ER(ER_NO));
}
}
@@ -7902,7 +10770,7 @@ static void login_failed_error(THD *thd)
after the connection was established
Packet format:
-
+
Bytes Content
----- ----
1 protocol version (always 10)
@@ -7996,7 +10864,7 @@ static bool send_server_handshake_packet(MPVIO_EXT *mpvio,
end= (char*) memcpy(end, data, SCRAMBLE_LENGTH_323);
end+= SCRAMBLE_LENGTH_323;
*end++= 0;
-
+
int2store(end, thd->client_capabilities);
/* write server characteristics: up to 16 bytes allowed */
end[2]= (char) default_charset_info->number;
@@ -8026,7 +10894,7 @@ static bool secure_auth(THD *thd)
return 0;
/*
- If the server is running in secure auth mode, short scrambles are
+ If the server is running in secure auth mode, short scrambles are
forbidden. Extra juggling to report the same error as the old code.
*/
if (thd->client_capabilities & CLIENT_PROTOCOL_41)
@@ -8051,7 +10919,7 @@ static bool secure_auth(THD *thd)
using a different authentication plugin
Packet format:
-
+
Bytes Content
----- ----
1 byte with the value 254
@@ -8117,7 +10985,7 @@ static bool send_plugin_request_packet(MPVIO_EXT *mpvio,
DBUG_RETURN (1);
}
- DBUG_PRINT("info", ("requesting client to use the %s plugin",
+ DBUG_PRINT("info", ("requesting client to use the %s plugin",
client_auth_plugin));
DBUG_RETURN(net_write_command(net, switch_plugin_request_buf[0],
(uchar*) client_auth_plugin,
@@ -8128,13 +10996,10 @@ static bool send_plugin_request_packet(MPVIO_EXT *mpvio,
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/**
Finds acl entry in user database for authentication purposes.
-
+
Finds a user and copies it into mpvio. Creates a fake user
if no matching user account is found.
- @note find_acl_user is not the same, because it doesn't take into
- account the case when user is not empty, but acl_user->user is empty
-
@retval 0 found
@retval 1 error
*/
@@ -8145,16 +11010,11 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio)
DBUG_ASSERT(mpvio->acl_user == 0);
mysql_mutex_lock(&acl_cache->lock);
- for (uint i=0; i < acl_users.elements; i++)
- {
- ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*);
- if ((!acl_user_tmp->user || !strcmp(sctx->user, acl_user_tmp->user)) &&
- compare_hostname(&acl_user_tmp->host, sctx->host, sctx->ip))
- {
- mpvio->acl_user= acl_user_tmp->copy(mpvio->thd->mem_root);
- break;
- }
- }
+
+ ACL_USER *user= find_user_or_anon(sctx->host, sctx->user, sctx->ip);
+ if (user)
+ mpvio->acl_user= user->copy(&mem);
+
mysql_mutex_unlock(&acl_cache->lock);
if (!mpvio->acl_user)
@@ -8206,8 +11066,7 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio)
mpvio->auth_info.user_name_length= strlen(sctx->user);
mpvio->auth_info.auth_string= mpvio->acl_user->auth_string.str;
mpvio->auth_info.auth_string_length= (unsigned long) mpvio->acl_user->auth_string.length;
- strmake_buf(mpvio->auth_info.authenticated_as, mpvio->acl_user->user ?
- mpvio->acl_user->user : "");
+ strmake_buf(mpvio->auth_info.authenticated_as, safe_str(mpvio->acl_user->user.str));
DBUG_PRINT("info", ("exit: user=%s, auth_string=%s, authenticated as=%s"
"plugin=%s",
@@ -8387,7 +11246,7 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
/*
For a passwordless accounts we use native_password_plugin.
But when an old 4.0 client connects to it, we change it to
- old_password_plugin, otherwise MySQL will think that server
+ old_password_plugin, otherwise MySQL will think that server
and client plugins don't match.
*/
if (mpvio->acl_user->auth_string.length == 0)
@@ -8404,9 +11263,9 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
}
DBUG_PRINT("info", ("client_plugin=%s, restart", client_plugin));
- /*
- Remember the data part of the packet, to present it to plugin in
- read_packet()
+ /*
+ Remember the data part of the packet, to present it to plugin in
+ read_packet()
*/
mpvio->cached_client_reply.pkt= passwd;
mpvio->cached_client_reply.pkt_len= passwd_len;
@@ -8624,7 +11483,7 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
/*
For a passwordless accounts we use native_password_plugin.
But when an old 4.0 client connects to it, we change it to
- old_password_plugin, otherwise MySQL will think that server
+ old_password_plugin, otherwise MySQL will think that server
and client plugins don't match.
*/
if (mpvio->acl_user->auth_string.length == 0)
@@ -8941,7 +11800,7 @@ static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user)
#else /* HAVE_OPENSSL */
default:
/*
- If we don't have SSL but SSL is required for this user the
+ If we don't have SSL but SSL is required for this user the
authentication should fail.
*/
return 1;
@@ -9050,7 +11909,7 @@ bool acl_authenticate(THD *thd, uint connect_errors,
mpvio.status= MPVIO_EXT::FAILURE;
mpvio.make_it_fail= false;
mpvio.auth_info.host_or_ip= thd->security_ctx->host_or_ip;
- mpvio.auth_info.host_or_ip_length=
+ mpvio.auth_info.host_or_ip_length=
(unsigned int) strlen(thd->security_ctx->host_or_ip);
DBUG_PRINT("info", ("com_change_user_pkt_len=%u", com_change_user_pkt_len));
@@ -9078,7 +11937,7 @@ bool acl_authenticate(THD *thd, uint connect_errors,
the correct plugin.
*/
- res= do_auth_once(thd, auth_plugin_name, &mpvio);
+ res= do_auth_once(thd, auth_plugin_name, &mpvio);
}
/*
@@ -9098,7 +11957,7 @@ bool acl_authenticate(THD *thd, uint connect_errors,
Security_context *sctx= thd->security_ctx;
const ACL_USER *acl_user= mpvio.acl_user;
- thd->password= mpvio.auth_info.password_used; // remember for error messages
+ thd->password= mpvio.auth_info.password_used; // remember for error messages
/*
Log the command here so that the user can check the log
@@ -9113,12 +11972,12 @@ bool acl_authenticate(THD *thd, uint connect_errors,
general_log_print(thd, command, "%s@%s as %s on %s",
sctx->user, sctx->host_or_ip,
sctx->priv_user[0] ? sctx->priv_user : "anonymous",
- mpvio.db.str ? mpvio.db.str : (char*) "");
+ safe_str(mpvio.db.str));
}
else
general_log_print(thd, command, (char*) "%s@%s on %s",
sctx->user, sctx->host_or_ip,
- mpvio.db.str ? mpvio.db.str : (char*) "");
+ safe_str(mpvio.db.str));
}
if (res > CR_OK && mpvio.status != MPVIO_EXT::SUCCESS)
@@ -9154,7 +12013,7 @@ bool acl_authenticate(THD *thd, uint connect_errors,
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
bool is_proxy_user= FALSE;
- const char *auth_user = acl_user->user ? acl_user->user : "";
+ const char *auth_user = safe_str(acl_user->user.str);
ACL_PROXY_USER *proxy_user;
/* check if the user is allowed to proxy as another user */
proxy_user= acl_find_proxy_user(auth_user, sctx->host, sctx->ip,
@@ -9177,13 +12036,12 @@ bool acl_authenticate(THD *thd, uint connect_errors,
my_snprintf(sctx->proxy_user, sizeof(sctx->proxy_user) - 1,
"'%s'@'%s'", auth_user,
- acl_user->host.hostname ? acl_user->host.hostname : "");
+ safe_str(acl_user->host.hostname));
/* we're proxying : find the proxy user definition */
mysql_mutex_lock(&acl_cache->lock);
- acl_proxy_user= find_acl_user(proxy_user->get_proxied_host() ?
- proxy_user->get_proxied_host() : "",
- mpvio.auth_info.authenticated_as, TRUE);
+ acl_proxy_user= find_user_exact(safe_str(proxy_user->get_proxied_host()),
+ mpvio.auth_info.authenticated_as);
if (!acl_proxy_user)
{
Host_errors errors;
@@ -9200,8 +12058,8 @@ bool acl_authenticate(THD *thd, uint connect_errors,
#endif
sctx->master_access= acl_user->access;
- if (acl_user->user)
- strmake_buf(sctx->priv_user, acl_user->user);
+ if (acl_user->user.str)
+ strmake_buf(sctx->priv_user, acl_user->user.str);
else
*sctx->priv_user= 0;
@@ -9234,10 +12092,10 @@ bool acl_authenticate(THD *thd, uint connect_errors,
acl_user->user_resource.updates ||
acl_user->user_resource.conn_per_hour ||
acl_user->user_resource.user_conn || max_user_connections_checking) &&
- get_or_create_user_conn(thd,
- (opt_old_style_user_limits ? sctx->user : sctx->priv_user),
- (opt_old_style_user_limits ? sctx->host_or_ip : sctx->priv_host),
- &acl_user->user_resource))
+ get_or_create_user_conn(thd,
+ (opt_old_style_user_limits ? sctx->user : sctx->priv_user),
+ (opt_old_style_user_limits ? sctx->host_or_ip : sctx->priv_host),
+ &acl_user->user_resource))
DBUG_RETURN(1); // The error is set by get_or_create_user_conn()
}
else
@@ -9247,7 +12105,7 @@ bool acl_authenticate(THD *thd, uint connect_errors,
(thd->user_connect->user_resources.conn_per_hour ||
thd->user_connect->user_resources.user_conn ||
max_user_connections_checking) &&
- check_for_max_user_connections(thd, thd->user_connect))
+ check_for_max_user_connections(thd, thd->user_connect))
{
/* Ensure we don't decrement thd->user_connections->connections twice */
thd->user_connect= 0;
@@ -9407,7 +12265,7 @@ static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
DBUG_RETURN(CR_AUTH_HANDSHAKE);
}
-static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
+static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
MYSQL_SERVER_AUTH_INFO *info)
{
uchar *pkt;
diff --git a/sql/sql_acl.h b/sql/sql_acl.h
index d519279e9c2..df523fae1ca 100644
--- a/sql/sql_acl.h
+++ b/sql/sql_acl.h
@@ -20,30 +20,30 @@
#include "violite.h" /* SSL_type */
#include "sql_class.h" /* LEX_COLUMN */
-#define SELECT_ACL (1L << 0)
-#define INSERT_ACL (1L << 1)
-#define UPDATE_ACL (1L << 2)
-#define DELETE_ACL (1L << 3)
-#define CREATE_ACL (1L << 4)
-#define DROP_ACL (1L << 5)
-#define RELOAD_ACL (1L << 6)
-#define SHUTDOWN_ACL (1L << 7)
-#define PROCESS_ACL (1L << 8)
-#define FILE_ACL (1L << 9)
-#define GRANT_ACL (1L << 10)
-#define REFERENCES_ACL (1L << 11)
-#define INDEX_ACL (1L << 12)
-#define ALTER_ACL (1L << 13)
-#define SHOW_DB_ACL (1L << 14)
-#define SUPER_ACL (1L << 15)
-#define CREATE_TMP_ACL (1L << 16)
-#define LOCK_TABLES_ACL (1L << 17)
-#define EXECUTE_ACL (1L << 18)
-#define REPL_SLAVE_ACL (1L << 19)
-#define REPL_CLIENT_ACL (1L << 20)
-#define CREATE_VIEW_ACL (1L << 21)
-#define SHOW_VIEW_ACL (1L << 22)
-#define CREATE_PROC_ACL (1L << 23)
+#define SELECT_ACL (1L << 0)
+#define INSERT_ACL (1L << 1)
+#define UPDATE_ACL (1L << 2)
+#define DELETE_ACL (1L << 3)
+#define CREATE_ACL (1L << 4)
+#define DROP_ACL (1L << 5)
+#define RELOAD_ACL (1L << 6)
+#define SHUTDOWN_ACL (1L << 7)
+#define PROCESS_ACL (1L << 8)
+#define FILE_ACL (1L << 9)
+#define GRANT_ACL (1L << 10)
+#define REFERENCES_ACL (1L << 11)
+#define INDEX_ACL (1L << 12)
+#define ALTER_ACL (1L << 13)
+#define SHOW_DB_ACL (1L << 14)
+#define SUPER_ACL (1L << 15)
+#define CREATE_TMP_ACL (1L << 16)
+#define LOCK_TABLES_ACL (1L << 17)
+#define EXECUTE_ACL (1L << 18)
+#define REPL_SLAVE_ACL (1L << 19)
+#define REPL_CLIENT_ACL (1L << 20)
+#define CREATE_VIEW_ACL (1L << 21)
+#define SHOW_VIEW_ACL (1L << 22)
+#define CREATE_PROC_ACL (1L << 23)
#define ALTER_PROC_ACL (1L << 24)
#define CREATE_USER_ACL (1L << 25)
#define EVENT_ACL (1L << 26)
@@ -57,7 +57,7 @@
4. acl_init() or whatever - to define behaviour for old privilege tables
5. sql_yacc.yy - for GRANT/REVOKE to work
*/
-#define NO_ACCESS (1L << 30)
+#define NO_ACCESS (1L << 30)
#define DB_ACLS \
(UPDATE_ACL | SELECT_ACL | INSERT_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \
GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL | \
@@ -114,21 +114,21 @@
#define DB_CHUNK1 (GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL)
#define DB_CHUNK2 (CREATE_TMP_ACL | LOCK_TABLES_ACL)
#define DB_CHUNK3 (CREATE_VIEW_ACL | SHOW_VIEW_ACL | \
- CREATE_PROC_ACL | ALTER_PROC_ACL )
+ CREATE_PROC_ACL | ALTER_PROC_ACL )
#define DB_CHUNK4 (EXECUTE_ACL)
#define DB_CHUNK5 (EVENT_ACL | TRIGGER_ACL)
#define fix_rights_for_db(A) (((A) & DB_CHUNK0) | \
- (((A) << 4) & DB_CHUNK1) | \
- (((A) << 6) & DB_CHUNK2) | \
- (((A) << 9) & DB_CHUNK3) | \
- (((A) << 2) & DB_CHUNK4))| \
+ (((A) << 4) & DB_CHUNK1) | \
+ (((A) << 6) & DB_CHUNK2) | \
+ (((A) << 9) & DB_CHUNK3) | \
+ (((A) << 2) & DB_CHUNK4))| \
(((A) << 9) & DB_CHUNK5)
#define get_rights_for_db(A) (((A) & DB_CHUNK0) | \
- (((A) & DB_CHUNK1) >> 4) | \
- (((A) & DB_CHUNK2) >> 6) | \
- (((A) & DB_CHUNK3) >> 9) | \
- (((A) & DB_CHUNK4) >> 2))| \
+ (((A) & DB_CHUNK1) >> 4) | \
+ (((A) & DB_CHUNK2) >> 6) | \
+ (((A) & DB_CHUNK3) >> 9) | \
+ (((A) & DB_CHUNK4) >> 2))| \
(((A) & DB_CHUNK5) >> 9)
#define TBL_CHUNK0 DB_CHUNK0
#define TBL_CHUNK1 DB_CHUNK1
@@ -145,11 +145,11 @@
#define fix_rights_for_column(A) (((A) & 7) | (((A) & ~7) << 8))
#define get_rights_for_column(A) (((A) & 7) | ((A) >> 8))
#define fix_rights_for_procedure(A) ((((A) << 18) & EXECUTE_ACL) | \
- (((A) << 23) & ALTER_PROC_ACL) | \
- (((A) << 8) & GRANT_ACL))
+ (((A) << 23) & ALTER_PROC_ACL) | \
+ (((A) << 8) & GRANT_ACL))
#define get_rights_for_procedure(A) ((((A) & EXECUTE_ACL) >> 18) | \
- (((A) & ALTER_PROC_ACL) >> 23) | \
- (((A) & GRANT_ACL) >> 8))
+ (((A) & ALTER_PROC_ACL) >> 23) | \
+ (((A) & GRANT_ACL) >> 8))
enum mysql_db_table_field
{
@@ -181,6 +181,11 @@ enum mysql_db_table_field
extern const TABLE_FIELD_DEF mysql_db_table_def;
extern bool mysql_user_table_is_in_short_password_format;
+extern LEX_STRING host_not_specified;
+extern LEX_STRING current_user;
+extern LEX_STRING current_role;
+extern LEX_STRING current_user_and_current_role;
+
static inline int access_denied_error_code(int passwd_used)
{
@@ -196,7 +201,7 @@ my_bool acl_init(bool dont_read_acl_tables);
my_bool acl_reload(THD *thd);
void acl_free(bool end=0);
ulong acl_get(const char *host, const char *ip,
- const char *user, const char *db, my_bool db_is_pattern);
+ const char *user, const char *db, my_bool db_is_pattern);
bool acl_authenticate(THD *thd, uint connect_errors, uint com_change_user_pkt_len);
bool acl_getroot(Security_context *sctx, char *user, char *host,
char *ip, char *db);
@@ -204,39 +209,43 @@ bool acl_check_host(const char *host, const char *ip);
int check_change_password(THD *thd, const char *host, const char *user,
char *password, uint password_len);
bool change_password(THD *thd, const char *host, const char *user,
- char *password);
+ char *password);
+
+bool mysql_grant_role(THD *thd, List<LEX_USER> &user_list, bool revoke);
bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &user_list,
ulong rights, bool revoke, bool is_proxy);
int mysql_table_grant(THD *thd, TABLE_LIST *table, List <LEX_USER> &user_list,
List <LEX_COLUMN> &column_list, ulong rights,
bool revoke);
bool mysql_routine_grant(THD *thd, TABLE_LIST *table, bool is_proc,
- List <LEX_USER> &user_list, ulong rights,
- bool revoke, bool write_to_binlog);
+ List <LEX_USER> &user_list, ulong rights,
+ bool revoke, bool write_to_binlog);
my_bool grant_init();
void grant_free(void);
my_bool grant_reload(THD *thd);
bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables,
bool any_combination_will_do, uint number, bool no_errors);
bool check_grant_column (THD *thd, GRANT_INFO *grant,
- const char *db_name, const char *table_name,
- const char *name, uint length, Security_context *sctx);
+ const char *db_name, const char *table_name,
+ const char *name, uint length, Security_context *sctx);
bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref,
const char *name, uint length);
-bool check_grant_all_columns(THD *thd, ulong want_access,
+bool check_grant_all_columns(THD *thd, ulong want_access,
Field_iterator_table_ref *fields);
bool check_grant_routine(THD *thd, ulong want_access,
- TABLE_LIST *procs, bool is_proc, bool no_error);
+ TABLE_LIST *procs, bool is_proc, bool no_error);
bool check_grant_db(THD *thd,const char *db);
ulong get_table_grant(THD *thd, TABLE_LIST *table);
ulong get_column_grant(THD *thd, GRANT_INFO *grant,
const char *db_name, const char *table_name,
const char *field_name);
bool mysql_show_grants(THD *thd, LEX_USER *user);
+int fill_schema_enabled_roles(THD *thd, TABLE_LIST *tables, COND *cond);
+int fill_schema_applicable_roles(THD *thd, TABLE_LIST *tables, COND *cond);
void get_privilege_desc(char *to, uint max_length, ulong access);
void get_mqh(const char *user, const char *host, USER_CONN *uc);
-bool mysql_create_user(THD *thd, List <LEX_USER> &list);
-bool mysql_drop_user(THD *thd, List <LEX_USER> &list);
+bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role);
+bool mysql_drop_user(THD *thd, List <LEX_USER> &list, bool handle_as_role);
bool mysql_rename_user(THD *thd, List <LEX_USER> &list);
bool mysql_revoke_all(THD *thd, List <LEX_USER> &list);
void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant,
@@ -390,4 +399,11 @@ get_cached_table_access(GRANT_INTERNAL_INFO *grant_internal_info,
bool acl_check_proxy_grant_access (THD *thd, const char *host, const char *user,
bool with_grant);
+int acl_setrole(THD *thd, char *rolename, ulonglong access);
+int acl_check_setrole(THD *thd, char *rolename, ulonglong *access);
+
+#ifndef DBUG_OFF
+extern ulong role_global_merges, role_db_merges, role_table_merges,
+ role_column_merges, role_routine_merges;
+#endif
#endif /* SQL_ACL_INCLUDED */
diff --git a/sql/sql_array.h b/sql/sql_array.h
index b527f26b186..8202e94ce41 100644
--- a/sql/sql_array.h
+++ b/sql/sql_array.h
@@ -126,32 +126,28 @@ public:
return *(((Elem*)array.buffer) + idx);
}
- /// @returns pointer to first element; undefined behaviour if array is empty
+ /// @returns pointer to first element
Elem *front()
{
- DBUG_ASSERT(array.elements >= 1);
return (Elem*)array.buffer;
}
- /// @returns pointer to first element; undefined behaviour if array is empty
+ /// @returns pointer to first element
const Elem *front() const
{
- DBUG_ASSERT(array.elements >= 1);
return (const Elem*)array.buffer;
}
- /// @returns pointer to last element; undefined behaviour if array is empty.
+ /// @returns pointer to last element
Elem *back()
{
- DBUG_ASSERT(array.elements >= 1);
- return ((Elem*)array.buffer) + (array.elements - 1);
+ return ((Elem*)array.buffer) + array.elements - 1;
}
- /// @returns pointer to last element; undefined behaviour if array is empty.
+ /// @returns pointer to last element
const Elem *back() const
{
- DBUG_ASSERT(array.elements >= 1);
- return ((const Elem*)array.buffer) + (array.elements - 1);
+ return ((const Elem*)array.buffer) + array.elements - 1;
}
/**
@@ -168,6 +164,11 @@ public:
return (insert_dynamic(&array, (uchar*)&el));
}
+ bool push(Elem &el)
+ {
+ return append(el);
+ }
+
/// Pops the last element. Does nothing if array is empty.
Elem& pop()
{
@@ -228,6 +229,12 @@ public:
{
my_qsort(array.buffer, array.elements, sizeof(Elem), (qsort_cmp)cmp_func);
}
+
+ typedef int (*CMP_FUNC2)(const Elem *el1, const Elem *el2, void *);
+ void sort(CMP_FUNC2 cmp_func, void *data)
+ {
+ my_qsort2(array.buffer, array.elements, sizeof(Elem), (qsort2_cmp)cmp_func, data);
+ }
};
#endif /* SQL_ARRAY_INCLUDED */
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index e4700507886..1b196e4e637 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -3908,10 +3908,9 @@ end:
DBUG_RETURN(error);
}
-extern "C" uchar *schema_set_get_key(const uchar *record, size_t *length,
+extern "C" uchar *schema_set_get_key(const TABLE_LIST *table, size_t *length,
my_bool not_used __attribute__((unused)))
{
- TABLE_LIST *table=(TABLE_LIST*) record;
*length= table->db_length;
return (uchar*) table->db;
}
@@ -3952,7 +3951,7 @@ lock_table_names(THD *thd,
MDL_request_list mdl_requests;
TABLE_LIST *table;
MDL_request global_request;
- Hash_set<TABLE_LIST, schema_set_get_key> schema_set;
+ Hash_set<TABLE_LIST> schema_set(schema_set_get_key);
ulong org_lock_wait_timeout= lock_wait_timeout;
/* Check if we are using CREATE TABLE ... IF NOT EXISTS */
bool create_table;
@@ -3998,7 +3997,7 @@ lock_table_names(THD *thd,
Scoped locks: Take intention exclusive locks on all involved
schemas.
*/
- Hash_set<TABLE_LIST, schema_set_get_key>::Iterator it(schema_set);
+ Hash_set<TABLE_LIST>::Iterator it(schema_set);
while ((table= it++))
{
MDL_request *schema_request= new (thd->mem_root) MDL_request;
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 31ce21509a9..1fe55d6af99 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -1001,7 +1001,7 @@ THD::THD()
thr_lock_info_init(&lock_info); /* safety: will be reset after start */
m_internal_handler= NULL;
- m_binlog_invoker= FALSE;
+ m_binlog_invoker= INVOKER_NONE;
arena_for_cached_items= 0;
memset(&invoker_user, 0, sizeof(invoker_user));
memset(&invoker_host, 0, sizeof(invoker_host));
@@ -2025,7 +2025,7 @@ void THD::cleanup_after_query()
where= THD::DEFAULT_WHERE;
/* reset table map for multi-table update */
table_map_for_update= 0;
- m_binlog_invoker= FALSE;
+ m_binlog_invoker= INVOKER_NONE;
#ifndef EMBEDDED_LIBRARY
if (rli_slave)
@@ -3782,7 +3782,7 @@ void Security_context::init()
{
host= user= ip= external_user= 0;
host_or_ip= "connecting host";
- priv_user[0]= priv_host[0]= proxy_user[0]= '\0';
+ priv_user[0]= priv_host[0]= proxy_user[0]= priv_role[0]= '\0';
master_access= 0;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
db_access= NO_ACCESS;
@@ -4652,9 +4652,9 @@ void THD::leave_locked_tables_mode()
locked_tables_mode= LTM_NONE;
}
-void THD::get_definer(LEX_USER *definer)
+void THD::get_definer(LEX_USER *definer, bool role)
{
- binlog_invoker();
+ binlog_invoker(role);
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
if (slave_thread && has_invoker())
{
@@ -4666,7 +4666,7 @@ void THD::get_definer(LEX_USER *definer)
}
else
#endif
- get_default_definer(this, definer);
+ get_default_definer(this, definer, role);
}
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 06edfd8c334..3c2cf24b43c 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1096,6 +1096,8 @@ public:
char proxy_user[USERNAME_LENGTH + MAX_HOSTNAME + 5];
/* The host privilege we are using */
char priv_host[MAX_HOSTNAME];
+ /* The role privilege we are using */
+ char priv_role[USERNAME_LENGTH];
/* The external user (if available) */
char *external_user;
/* points to host if host is available, otherwise points to ip */
@@ -3390,9 +3392,11 @@ public:
}
void leave_locked_tables_mode();
int decide_logging_format(TABLE_LIST *tables);
- void binlog_invoker() { m_binlog_invoker= TRUE; }
- bool need_binlog_invoker() { return m_binlog_invoker; }
- void get_definer(LEX_USER *definer);
+
+ enum need_invoker { INVOKER_NONE=0, INVOKER_USER, INVOKER_ROLE};
+ void binlog_invoker(bool role) { m_binlog_invoker= role ? INVOKER_ROLE : INVOKER_USER; }
+ enum need_invoker need_binlog_invoker() { return m_binlog_invoker; }
+ void get_definer(LEX_USER *definer, bool role);
void set_invoker(const LEX_STRING *user, const LEX_STRING *host)
{
invoker_user= *user;
@@ -3467,14 +3471,15 @@ private:
Diagnostics_area *m_stmt_da;
/**
- It will be set TURE if CURRENT_USER() is called in account management
- statements or default definer is set in CREATE/ALTER SP, SF, Event,
- TRIGGER or VIEW statements.
+ It will be set if CURRENT_USER() or CURRENT_ROLE() is called in account
+ management statements or default definer is set in CREATE/ALTER SP, SF,
+ Event, TRIGGER or VIEW statements.
- Current user will be binlogged into Query_log_event if m_binlog_invoker
- is TRUE; It will be stored into invoker_host and invoker_user by SQL thread.
+ Current user or role will be binlogged into Query_log_event if
+ m_binlog_invoker is not NONE; It will be stored into invoker_host and
+ invoker_user by SQL thread.
*/
- bool m_binlog_invoker;
+ enum need_invoker m_binlog_invoker;
/**
It points to the invoker in the Query_log_event.
diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h
index de7ef5fc832..231db2a1d8c 100644
--- a/sql/sql_cmd.h
+++ b/sql/sql_cmd.h
@@ -93,6 +93,7 @@ enum enum_sql_command {
SQLCOM_SHOW_CLIENT_STATS,
SQLCOM_SLAVE_ALL_START, SQLCOM_SLAVE_ALL_STOP,
SQLCOM_SHOW_EXPLAIN, SQLCOM_SHUTDOWN,
+ SQLCOM_CREATE_ROLE, SQLCOM_DROP_ROLE, SQLCOM_GRANT_ROLE, SQLCOM_REVOKE_ROLE,
/*
When a command is added here, be sure it's also added in mysqld.cc
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index 9e30ed4513e..b649ddc1900 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -1471,14 +1471,18 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
DBUG_PRINT("info",("Use database: %s", new_db_file_name.str));
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- db_access=
- test_all_bits(sctx->master_access, DB_ACLS) ?
- DB_ACLS :
- acl_get(sctx->host,
- sctx->ip,
- sctx->priv_user,
- new_db_file_name.str,
- FALSE) | sctx->master_access;
+ if (test_all_bits(sctx->master_access, DB_ACLS))
+ db_access= DB_ACLS;
+ else
+ {
+ db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user,
+ new_db_file_name.str, FALSE) | sctx->master_access;
+ if (sctx->priv_role[0])
+ {
+ /* include a possible currently set role for access */
+ db_access|= acl_get("", "", sctx->priv_role, new_db_file_name.str, FALSE);
+ }
+ }
if (!force_switch &&
!(db_access & DB_ACLS) &&
diff --git a/sql/sql_hset.h b/sql/sql_hset.h
index f3a1467737f..dc3bd487ce5 100644
--- a/sql/sql_hset.h
+++ b/sql/sql_hset.h
@@ -23,19 +23,19 @@
A type-safe wrapper around mysys HASH.
*/
-template <typename T, my_hash_get_key K>
+template <typename T>
class Hash_set
{
public:
- typedef T Value_type;
enum { START_SIZE= 8 };
/**
Constructs an empty hash. Does not allocate memory, it is done upon
the first insert. Thus does not cause or return errors.
*/
- Hash_set()
+ Hash_set(uchar *(*K)(const T *, size_t *, my_bool))
{
my_hash_clear(&m_hash);
+ m_hash.get_key= (my_hash_get_key)K;
}
/**
Destroy the hash by freeing the buckets table. Does
@@ -56,13 +56,19 @@ public:
*/
bool insert(T *value)
{
- my_hash_init_opt(&m_hash, &my_charset_bin, START_SIZE, 0, 0, K, 0, MYF(0));
+ my_hash_init_opt(&m_hash, &my_charset_bin, START_SIZE, 0, 0,
+ m_hash.get_key, 0, MYF(0));
size_t key_len;
- const uchar *key= K(reinterpret_cast<uchar*>(value), &key_len, FALSE);
- if (my_hash_search(&m_hash, key, key_len) == NULL)
- return my_hash_insert(&m_hash, reinterpret_cast<uchar *>(value));
+ uchar *v= reinterpret_cast<uchar *>(value);
+ const uchar *key= m_hash.get_key(v, &key_len, FALSE);
+ if (find(key, key_len) == NULL)
+ return my_hash_insert(&m_hash, v);
return FALSE;
}
+ T *find(const void *key, size_t klen) const
+ {
+ return (T*)my_hash_search(&m_hash, reinterpret_cast<const uchar *>(key), klen);
+ }
/** Is this hash set empty? */
bool is_empty() const { return m_hash.records == 0; }
/** Returns the number of unique elements. */
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index e27f3cbd4a6..f892f1bf3c7 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -2428,7 +2428,6 @@ struct LEX: public Query_tables_list
this command.
*/
bool parse_vcol_expr;
- bool with_persistent_for_clause; // uses PERSISTENT FOR clause (in ANALYZE)
enum SSL_type ssl_type; /* defined in violite.h */
enum enum_duplicates duplicates;
@@ -2437,6 +2436,8 @@ struct LEX: public Query_tables_list
union {
enum ha_rkey_function ha_rkey_mode;
enum xa_option_words xa_opt;
+ bool with_admin_option; // GRANT role
+ bool with_persistent_for_clause; // uses PERSISTENT FOR clause (in ANALYZE)
};
enum enum_var_type option_type;
enum enum_view_create_mode create_view_mode;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 0353ec7fde9..92d628bbfe1 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -401,8 +401,11 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_CREATE_USER]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_RENAME_USER]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_DROP_USER]= CF_CHANGES_DATA;
+ sql_command_flags[SQLCOM_CREATE_ROLE]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_GRANT]= CF_CHANGES_DATA;
+ sql_command_flags[SQLCOM_GRANT_ROLE]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_REVOKE]= CF_CHANGES_DATA;
+ sql_command_flags[SQLCOM_REVOKE_ROLE]= CF_CHANGES_DATA;
sql_command_flags[SQLCOM_OPTIMIZE]= CF_CHANGES_DATA;
/*
@todo SQLCOM_CREATE_FUNCTION should have CF_AUTO_COMMIT_TRANS
@@ -461,9 +464,13 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_CREATE_USER]|= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_DROP_USER]|= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_RENAME_USER]|= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_CREATE_ROLE]|= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_DROP_ROLE]|= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_REVOKE_ALL]= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_REVOKE]|= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_REVOKE_ROLE]|= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_GRANT]|= CF_AUTO_COMMIT_TRANS;
+ sql_command_flags[SQLCOM_GRANT_ROLE]|= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_ASSIGN_TO_KEYCACHE]= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_PRELOAD_KEYS]= CF_AUTO_COMMIT_TRANS;
@@ -664,7 +671,8 @@ static void handle_bootstrap_impl(THD *thd)
#endif /* EMBEDDED_LIBRARY */
thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME));
- thd->security_ctx->priv_user[0]= thd->security_ctx->priv_host[0]=0;
+ thd->security_ctx->priv_user[0]= thd->security_ctx->priv_host[0]=
+ thd->security_ctx->priv_role[0]= 0;
/*
Make the "client" handle multiple results. This is necessary
to enable stored procedures with SELECTs and Dynamic SQL
@@ -1878,8 +1886,8 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
DBUG_RETURN(1);
lex->query_tables_last= query_tables_last;
break;
- }
#endif
+ }
case SCH_PROFILES:
/*
Mark this current profiling record to be discarded. We don't
@@ -2006,7 +2014,6 @@ static void reset_one_shot_variables(THD *thd)
}
-static
bool sp_process_definer(THD *thd)
{
DBUG_ENTER("sp_process_definer");
@@ -2043,7 +2050,7 @@ bool sp_process_definer(THD *thd)
Query_arena original_arena;
Query_arena *ps_arena= thd->activate_stmt_arena_if_needed(&original_arena);
- lex->definer= create_default_definer(thd);
+ lex->definer= create_default_definer(thd, false);
if (ps_arena)
thd->restore_active_arena(ps_arena, &original_arena);
@@ -2057,20 +2064,24 @@ bool sp_process_definer(THD *thd)
}
else
{
+ LEX_USER *d= lex->definer= get_current_user(thd, lex->definer);
+ if (!d)
+ DBUG_RETURN(TRUE);
+
/*
- If the specified definer differs from the current user, we
+ If the specified definer differs from the current user or role, we
should check that the current user has SUPER privilege (in order
to create a stored routine under another user one must have
SUPER privilege).
*/
- if ((strcmp(lex->definer->user.str, thd->security_ctx->priv_user) ||
- my_strcasecmp(system_charset_info, lex->definer->host.str,
- thd->security_ctx->priv_host)) &&
- check_global_access(thd, SUPER_ACL, true))
- {
- my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
+ bool curuser= !strcmp(d->user.str, thd->security_ctx->priv_user);
+ bool currole= !curuser && !strcmp(d->user.str, thd->security_ctx->priv_role);
+ bool curuserhost= curuser && d->host.str &&
+ !my_strcasecmp(system_charset_info, d->host.str,
+ thd->security_ctx->priv_host);
+ if (!curuserhost && !currole &&
+ check_global_access(thd, SUPER_ACL, false))
DBUG_RETURN(TRUE);
- }
}
/* Check that the specified definer exists. Emit a warning if not. */
@@ -3991,22 +4002,26 @@ end_with_restore_list:
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
case SQLCOM_CREATE_USER:
+ case SQLCOM_CREATE_ROLE:
{
if (check_access(thd, INSERT_ACL, "mysql", NULL, NULL, 1, 1) &&
check_global_access(thd,CREATE_USER_ACL))
break;
/* Conditionally writes to binlog */
- if (!(res= mysql_create_user(thd, lex->users_list)))
+ if (!(res= mysql_create_user(thd, lex->users_list,
+ lex->sql_command == SQLCOM_CREATE_ROLE)))
my_ok(thd);
break;
}
case SQLCOM_DROP_USER:
+ case SQLCOM_DROP_ROLE:
{
if (check_access(thd, DELETE_ACL, "mysql", NULL, NULL, 1, 1) &&
check_global_access(thd,CREATE_USER_ACL))
break;
/* Conditionally writes to binlog */
- if (!(res= mysql_drop_user(thd, lex->users_list)))
+ if (!(res= mysql_drop_user(thd, lex->users_list,
+ lex->sql_command == SQLCOM_DROP_ROLE)))
my_ok(thd);
break;
}
@@ -4026,9 +4041,6 @@ end_with_restore_list:
check_global_access(thd,CREATE_USER_ACL))
break;
- /* Replicate current user as grantor */
- thd->binlog_invoker();
-
/* Conditionally writes to binlog */
if (!(res = mysql_revoke_all(thd, lex->users_list)))
my_ok(thd);
@@ -4046,42 +4058,58 @@ end_with_restore_list:
goto error;
/* Replicate current user as grantor */
- thd->binlog_invoker();
+ thd->binlog_invoker(false);
if (thd->security_ctx->user) // If not replication
{
- LEX_USER *user, *tmp_user;
+ LEX_USER *user;
bool first_user= TRUE;
List_iterator <LEX_USER> user_list(lex->users_list);
- while ((tmp_user= user_list++))
+ while ((user= user_list++))
{
- if (!(user= get_current_user(thd, tmp_user)))
- goto error;
if (specialflag & SPECIAL_NO_RESOLVE &&
hostname_requires_resolving(user->host.str))
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_HOSTNAME_WONT_WORK,
ER(ER_WARN_HOSTNAME_WONT_WORK));
- // Are we trying to change a password of another user
- DBUG_ASSERT(user->host.str != 0);
/*
GRANT/REVOKE PROXY has the target user as a first entry in the list.
*/
if (lex->type == TYPE_ENUM_PROXY && first_user)
{
+ if (!(user= get_current_user(thd, user)) || !user->host.str)
+ goto error;
+
first_user= FALSE;
if (acl_check_proxy_grant_access (thd, user->host.str, user->user.str,
lex->grant & GRANT_ACL))
goto error;
}
- else if (is_acl_user(user->host.str, user->user.str) &&
- user->password.str &&
- check_change_password (thd, user->host.str, user->user.str,
- user->password.str,
- user->password.length))
- goto error;
+ else if (user->password.str)
+ {
+ // Are we trying to change a password of another user?
+ const char *hostname= user->host.str, *username=user->user.str;
+ bool userok;
+ if (username == current_user.str)
+ {
+ username= thd->security_ctx->priv_user;
+ hostname= thd->security_ctx->priv_host;
+ userok= true;
+ }
+ else
+ {
+ if (!hostname)
+ hostname= host_not_specified.str;
+ userok= is_acl_user(hostname, username);
+ }
+
+ if (userok && check_change_password (thd, hostname, username,
+ user->password.str,
+ user->password.length))
+ goto error;
+ }
}
}
if (first_table)
@@ -4125,9 +4153,9 @@ end_with_restore_list:
else
{
/* Conditionally writes to binlog */
- res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
- lex->sql_command == SQLCOM_REVOKE,
- lex->type == TYPE_ENUM_PROXY);
+ res= mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
+ lex->sql_command == SQLCOM_REVOKE,
+ lex->type == TYPE_ENUM_PROXY);
}
if (!res)
{
@@ -4146,6 +4174,14 @@ end_with_restore_list:
}
break;
}
+ case SQLCOM_REVOKE_ROLE:
+ case SQLCOM_GRANT_ROLE:
+ {
+ if (!(res= mysql_grant_role(thd, lex->users_list,
+ lex->sql_command != SQLCOM_GRANT_ROLE)))
+ my_ok(thd);
+ break;
+ }
#endif /*!NO_EMBEDDED_ACCESS_CHECKS*/
case SQLCOM_RESET:
/*
@@ -4243,11 +4279,17 @@ end_with_restore_list:
#ifndef NO_EMBEDDED_ACCESS_CHECKS
case SQLCOM_SHOW_GRANTS:
{
- LEX_USER *grant_user= get_current_user(thd, lex->grant_user);
+ LEX_USER *grant_user= lex->grant_user;
if (!grant_user)
goto error;
- if ((thd->security_ctx->priv_user &&
- !strcmp(thd->security_ctx->priv_user, grant_user->user.str)) ||
+
+ if (grant_user->user.str &&
+ !strcmp(thd->security_ctx->priv_user, grant_user->user.str))
+ grant_user->user= current_user;
+
+ if (grant_user->user.str == current_user.str ||
+ grant_user->user.str == current_role.str ||
+ grant_user->user.str == current_user_and_current_role.str ||
!check_access(thd, SELECT_ACL, "mysql", NULL, NULL, 1, 0))
{
res = mysql_show_grants(thd, grant_user);
@@ -5363,8 +5405,12 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
if (!(sctx->master_access & SELECT_ACL))
{
if (db && (!thd->db || db_is_pattern || strcmp(db, thd->db)))
+ {
db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
db_is_pattern);
+ if (sctx->priv_role[0])
+ db_access|= acl_get("", "", sctx->priv_role, db, db_is_pattern);
+ }
else
{
/* get access for current db */
@@ -5408,8 +5454,14 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
}
if (db && (!thd->db || db_is_pattern || strcmp(db,thd->db)))
+ {
db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db,
db_is_pattern);
+ if (sctx->priv_role[0])
+ {
+ db_access|= acl_get("", "", sctx->priv_role, db, db_is_pattern);
+ }
+ }
else
db_access= sctx->db_access;
DBUG_PRINT("info",("db_access: %lu want_access: %lu",
@@ -8016,16 +8068,23 @@ Item *negate_expression(THD *thd, Item *expr)
@param[out] definer definer
*/
-void get_default_definer(THD *thd, LEX_USER *definer)
+void get_default_definer(THD *thd, LEX_USER *definer, bool role)
{
const Security_context *sctx= thd->security_ctx;
- definer->user.str= (char *) sctx->priv_user;
+ if (role)
+ {
+ definer->user.str= const_cast<char*>(sctx->priv_role);
+ definer->host= empty_lex_str;
+ }
+ else
+ {
+ definer->user.str= const_cast<char*>(sctx->priv_user);
+ definer->host.str= const_cast<char*>(sctx->priv_host);
+ definer->host.length= strlen(definer->host.str);
+ }
definer->user.length= strlen(definer->user.str);
- definer->host.str= (char *) sctx->priv_host;
- definer->host.length= strlen(definer->host.str);
-
definer->password= null_lex_str;
definer->plugin= empty_lex_str;
definer->auth= empty_lex_str;
@@ -8043,16 +8102,22 @@ void get_default_definer(THD *thd, LEX_USER *definer)
- On error, return 0.
*/
-LEX_USER *create_default_definer(THD *thd)
+LEX_USER *create_default_definer(THD *thd, bool role)
{
LEX_USER *definer;
if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER))))
return 0;
- thd->get_definer(definer);
+ thd->get_definer(definer, role);
- return definer;
+ if (role && definer->user.length == 0)
+ {
+ my_error(ER_MALFORMED_DEFINER, MYF(0));
+ return 0;
+ }
+ else
+ return definer;
}
@@ -8088,27 +8153,6 @@ LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name)
/**
- Retuns information about user or current user.
-
- @param[in] thd thread handler
- @param[in] user user
-
- @return
- - On success, return a valid pointer to initialized
- LEX_USER, which contains user information.
- - On error, return 0.
-*/
-
-LEX_USER *get_current_user(THD *thd, LEX_USER *user)
-{
- if (!user->user.str) // current_user
- return create_default_definer(thd);
-
- return user;
-}
-
-
-/**
Check that byte length of a string does not exceed some limit.
@param str string to be checked
diff --git a/sql/sql_parse.h b/sql/sql_parse.h
index 84256aa2256..8240d2ba6a1 100644
--- a/sql/sql_parse.h
+++ b/sql/sql_parse.h
@@ -62,10 +62,11 @@ Comp_creator *comp_ne_creator(bool invert);
int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
enum enum_schema_tables schema_table_idx);
-void get_default_definer(THD *thd, LEX_USER *definer);
-LEX_USER *create_default_definer(THD *thd);
+void get_default_definer(THD *thd, LEX_USER *definer, bool role);
+LEX_USER *create_default_definer(THD *thd, bool role);
LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name);
-LEX_USER *get_current_user(THD *thd, LEX_USER *user);
+LEX_USER *get_current_user(THD *thd, LEX_USER *user, bool lock=true);
+bool sp_process_definer(THD *thd);
bool check_string_byte_length(LEX_STRING *str, const char *err_msg,
uint max_byte_length);
bool check_string_char_length(LEX_STRING *str, const char *err_msg,
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 35aac145e5e..eb0d16ff324 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -10541,6 +10541,86 @@ restart:
}
}
+/**
+ Remove pushdown conditions that are already checked by the scan phase
+ of BNL/BNLH joins.
+
+ @note
+ If the single-table condition for this table will be used by a
+ blocked join to pre-filter this table's rows, there is no need
+ to re-check the same single-table condition for each joined record.
+
+ This method removes from JOIN_TAB::select_cond and JOIN_TAB::select::cond
+ all top-level conjuncts that also appear in in JOIN_TAB::cache_select::cond.
+*/
+
+void JOIN_TAB::remove_redundant_bnl_scan_conds()
+{
+ if (!(select_cond && cache_select && cache &&
+ (cache->get_join_alg() == JOIN_CACHE::BNL_JOIN_ALG ||
+ cache->get_join_alg() == JOIN_CACHE::BNLH_JOIN_ALG)))
+ return;
+
+ /*
+ select->cond is not processed separately. This method assumes it is always
+ the same as select_cond.
+ */
+ DBUG_ASSERT(!select || !select->cond ||
+ (select->cond == select_cond));
+
+ if (is_cond_and(select_cond))
+ {
+ List_iterator<Item> pushed_cond_li(*((Item_cond*) select_cond)->argument_list());
+ Item *pushed_item;
+ Item_cond_and *reduced_select_cond= new Item_cond_and;
+
+ if (is_cond_and(cache_select->cond))
+ {
+ List_iterator<Item> scan_cond_li(*((Item_cond*) cache_select->cond)->argument_list());
+ Item *scan_item;
+ while ((pushed_item= pushed_cond_li++))
+ {
+ bool found= false;
+ scan_cond_li.rewind();
+ while ((scan_item= scan_cond_li++))
+ {
+ if (pushed_item->eq(scan_item, 0))
+ {
+ found= true;
+ break;
+ }
+ }
+ if (!found)
+ reduced_select_cond->add(pushed_item);
+ }
+ }
+ else
+ {
+ while ((pushed_item= pushed_cond_li++))
+ {
+ if (!pushed_item->eq(cache_select->cond, 0))
+ reduced_select_cond->add(pushed_item);
+ }
+ }
+
+ /*
+ JOIN_CACHE::check_match uses JOIN_TAB::select->cond instead of
+ JOIN_TAB::select_cond. set_cond() sets both pointers.
+ */
+ if (reduced_select_cond->argument_list()->is_empty())
+ set_cond(NULL);
+ else if (reduced_select_cond->argument_list()->elements == 1)
+ set_cond(reduced_select_cond->argument_list()->head());
+ else
+ {
+ reduced_select_cond->quick_fix_field();
+ set_cond(reduced_select_cond);
+ }
+ }
+ else if (select_cond->eq(cache_select->cond, 0))
+ set_cond(NULL);
+}
+
/*
Plan refinement stage: do various setup things for the executor
@@ -10790,6 +10870,15 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after)
abort();
/* purecov: end */
}
+
+ tab->remove_redundant_bnl_scan_conds();
+ DBUG_EXECUTE("where",
+ char buff[256];
+ String str(buff,sizeof(buff),system_charset_info);
+ str.length(0);
+ str.append(tab->table? tab->table->alias.c_ptr() :"<no_table_name>");
+ str.append(" final_pushdown_cond");
+ print_where(tab->select_cond, str.c_ptr_safe(), QT_ORDINARY););
}
uint n_top_tables= join->join_tab_ranges.head()->end -
join->join_tab_ranges.head()->start;
@@ -23177,7 +23266,8 @@ int JOIN::save_explain_data_intern(Explain_query *output, bool need_tmp_table,
eta->push_extra(ET_RANGE_CHECKED_FOR_EACH_RECORD);
eta->range_checked_map= tab->keys;
}
- else if (tab->select->cond)
+ else if (tab->select->cond ||
+ (tab->cache_select && tab->cache_select->cond))
{
const COND *pushed_cond= tab->table->file->pushed_cond;
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 9db553e8ceb..c335836e19b 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -535,6 +535,7 @@ typedef struct st_join_table {
!(used_sjm_lookup_tables & ~emb_sj_nest->sj_inner_tables));
}
+ void remove_redundant_bnl_scan_conds();
} JOIN_TAB;
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 446069c14e9..504f1a94b37 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -90,6 +90,8 @@ enum enum_i_s_events_fields
ISE_DB_CL
};
+#define USERNAME_WITH_HOST_CHAR_LENGTH (USERNAME_CHAR_LENGTH + HOSTNAME_LENGTH + 2)
+
#ifndef NO_EMBEDDED_ACCESS_CHECKS
static const char *grant_names[]={
"select","insert","update","delete","create","drop","reload","shutdown",
@@ -2077,8 +2079,11 @@ void append_definer(THD *thd, String *buffer, const LEX_STRING *definer_user,
{
buffer->append(STRING_WITH_LEN("DEFINER="));
append_identifier(thd, buffer, definer_user->str, definer_user->length);
- buffer->append('@');
- append_identifier(thd, buffer, definer_host->str, definer_host->length);
+ if (definer_host->str[0])
+ {
+ buffer->append('@');
+ append_identifier(thd, buffer, definer_host->str, definer_host->length);
+ }
buffer->append(' ');
}
@@ -8399,6 +8404,22 @@ ST_FIELD_INFO collation_fields_info[]=
};
+ST_FIELD_INFO applicable_roles_fields_info[]=
+{
+ {"GRANTEE", USERNAME_WITH_HOST_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"ROLE_NAME", USERNAME_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+};
+
+
+ST_FIELD_INFO enabled_roles_fields_info[]=
+{
+ {"ROLE_NAME", USERNAME_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, MY_I_S_MAYBE_NULL, 0, SKIP_OPEN_TABLE},
+ {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}
+};
+
+
ST_FIELD_INFO engines_fields_info[]=
{
{"ENGINE", 64, MYSQL_TYPE_STRING, 0, 0, "Engine", SKIP_OPEN_TABLE},
@@ -8552,7 +8573,7 @@ ST_FIELD_INFO view_fields_info[]=
ST_FIELD_INFO user_privileges_fields_info[]=
{
- {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"GRANTEE", USERNAME_WITH_HOST_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"PRIVILEGE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"IS_GRANTABLE", 3, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
@@ -8562,7 +8583,7 @@ ST_FIELD_INFO user_privileges_fields_info[]=
ST_FIELD_INFO schema_privileges_fields_info[]=
{
- {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"GRANTEE", USERNAME_WITH_HOST_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"PRIVILEGE_TYPE", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
@@ -8573,7 +8594,7 @@ ST_FIELD_INFO schema_privileges_fields_info[]=
ST_FIELD_INFO table_privileges_fields_info[]=
{
- {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"GRANTEE", USERNAME_WITH_HOST_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
@@ -8585,7 +8606,7 @@ ST_FIELD_INFO table_privileges_fields_info[]=
ST_FIELD_INFO column_privileges_fields_info[]=
{
- {"GRANTEE", 81, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
+ {"GRANTEE", USERNAME_WITH_HOST_CHAR_LENGTH, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_CATALOG", FN_REFLEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_SCHEMA", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
{"TABLE_NAME", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE},
@@ -8994,6 +9015,10 @@ ST_FIELD_INFO show_explain_fields_info[]=
ST_SCHEMA_TABLE schema_tables[]=
{
+ {"ALL_PLUGINS", plugin_fields_info, create_schema_table,
+ fill_all_plugins, make_old_format, 0, 5, -1, 0, 0},
+ {"APPLICABLE_ROLES", applicable_roles_fields_info, create_schema_table,
+ fill_schema_applicable_roles, 0, 0, -1, -1, 0, 0},
{"CHARACTER_SETS", charsets_fields_info, create_schema_table,
fill_schema_charsets, make_character_sets_old_format, 0, -1, -1, 0, 0},
{"CLIENT_STATISTICS", client_stats_fields_info, create_schema_table,
@@ -9007,6 +9032,8 @@ ST_SCHEMA_TABLE schema_tables[]=
OPTIMIZE_I_S_TABLE|OPEN_VIEW_FULL},
{"COLUMN_PRIVILEGES", column_privileges_fields_info, create_schema_table,
fill_schema_column_privileges, 0, 0, -1, -1, 0, 0},
+ {"ENABLED_ROLES", enabled_roles_fields_info, create_schema_table,
+ fill_schema_enabled_roles, 0, 0, -1, -1, 0, 0},
{"ENGINES", engines_fields_info, create_schema_table,
fill_schema_engines, make_old_format, 0, -1, -1, 0, 0},
#ifdef HAVE_EVENT_SCHEDULER
@@ -9040,8 +9067,6 @@ ST_SCHEMA_TABLE schema_tables[]=
OPTIMIZE_I_S_TABLE|OPEN_TABLE_ONLY},
{"PLUGINS", plugin_fields_info, create_schema_table,
fill_plugins, make_old_format, 0, -1, -1, 0, 0},
- {"ALL_PLUGINS", plugin_fields_info, create_schema_table,
- fill_all_plugins, make_old_format, 0, 5, -1, 0, 0},
{"PROCESSLIST", processlist_fields_info, create_schema_table,
fill_schema_processlist, make_old_format, 0, -1, -1, 0, 0},
{"PROFILING", query_profile_statistics_info, create_schema_table,
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index bc4986bebee..b38670bd03c 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -663,46 +663,8 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
return 1;
}
- if (!lex->definer)
- {
- /*
- DEFINER-clause is missing.
-
- If we are in slave thread, this means that we received CREATE TRIGGER
- from the master, that does not support definer in triggers. So, we
- should mark this trigger as non-SUID. Note that this does not happen
- when we parse triggers' definitions during opening .TRG file.
- LEX::definer is ignored in that case.
-
- Otherwise, we should use CURRENT_USER() as definer.
-
- NOTE: when CREATE TRIGGER statement is allowed to be executed in PS/SP,
- it will be required to create the definer below in persistent MEM_ROOT
- of PS/SP.
- */
-
- if (!thd->slave_thread)
- {
- if (!(lex->definer= create_default_definer(thd)))
- return 1;
- }
- }
-
- /*
- If the specified definer differs from the current user, we should check
- that the current user has SUPER privilege (in order to create trigger
- under another user one must have SUPER privilege).
- */
-
- if (lex->definer &&
- (strcmp(lex->definer->user.str, thd->security_ctx->priv_user) ||
- my_strcasecmp(system_charset_info,
- lex->definer->host.str,
- thd->security_ctx->priv_host)))
- {
- if (check_global_access(thd, SUPER_ACL))
- return TRUE;
- }
+ if (sp_process_definer(thd))
+ return 1;
/*
Let us check if all references to fields in old/new versions of row in
@@ -794,29 +756,14 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
*trg_sql_mode= thd->variables.sql_mode;
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (lex->definer && !is_acl_user(lex->definer->host.str,
- lex->definer->user.str))
- {
- push_warning_printf(thd,
- Sql_condition::WARN_LEVEL_NOTE,
- ER_NO_SUCH_USER,
- ER(ER_NO_SUCH_USER),
- lex->definer->user.str,
- lex->definer->host.str);
- }
-#endif /* NO_EMBEDDED_ACCESS_CHECKS */
-
- if (lex->definer)
+ if (lex->sphead->m_chistics->suid != SP_IS_NOT_SUID)
{
/* SUID trigger. */
definer_user= lex->definer->user;
definer_host= lex->definer->host;
- trg_definer->str= trg_definer_holder;
- trg_definer->length= strxmov(trg_definer->str, definer_user.str, "@",
- definer_host.str, NullS) - trg_definer->str;
+ lex->definer->set_lex_string(trg_definer, trg_definer_holder);
}
else
{
@@ -854,7 +801,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
stmt_query->append(STRING_WITH_LEN("CREATE "));
- if (trg_definer)
+ if (lex->sphead->m_chistics->suid != SP_IS_NOT_SUID)
{
/*
Append definer-clause if the trigger is SUID (a usual trigger in
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 6f030d28678..505b8b25f89 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -39,8 +39,7 @@
const LEX_STRING view_type= { C_STRING_WITH_LEN("VIEW") };
-static int mysql_register_view(THD *thd, TABLE_LIST *view,
- enum_view_create_mode mode);
+static int mysql_register_view(THD *, TABLE_LIST *, enum_view_create_mode);
/*
Make a unique name for an anonymous view column
@@ -467,60 +466,9 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
}
sp_cache_invalidate();
+ if (sp_process_definer(thd))
+ goto err;
- if (!lex->definer)
- {
- /*
- DEFINER-clause is missing; we have to create default definer in
- persistent arena to be PS/SP friendly.
- If this is an ALTER VIEW then the current user should be set as
- the definer.
- */
- Query_arena original_arena;
- Query_arena *ps_arena = thd->activate_stmt_arena_if_needed(&original_arena);
-
- if (!(lex->definer= create_default_definer(thd)))
- res= TRUE;
-
- if (ps_arena)
- thd->restore_active_arena(ps_arena, &original_arena);
-
- if (res)
- goto err;
- }
-
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- /*
- check definer of view:
- - same as current user
- - current user has SUPER_ACL
- */
- if (lex->definer &&
- (strcmp(lex->definer->user.str, thd->security_ctx->priv_user) != 0 ||
- my_strcasecmp(system_charset_info,
- lex->definer->host.str,
- thd->security_ctx->priv_host) != 0))
- {
- if (!(thd->security_ctx->master_access & SUPER_ACL))
- {
- my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER");
- res= TRUE;
- goto err;
- }
- else
- {
- if (!is_acl_user(lex->definer->host.str,
- lex->definer->user.str))
- {
- push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE,
- ER_NO_SUCH_USER,
- ER(ER_NO_SUCH_USER),
- lex->definer->user.str,
- lex->definer->host.str);
- }
- }
- }
-#endif
/*
check that tables are not temporary and this VIEW do not used in query
(it is possible with ALTERing VIEW).
@@ -884,7 +832,11 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
goto err;
}
- view->file_version= 1;
+ /*
+ version 1 - before 10.0.5
+ version 2 - empty definer_host means a role
+ */
+ view->file_version= 2;
view->calc_md5(md5);
if (!(view->md5.str= (char*) thd->memdup(md5, 32)))
{
@@ -1079,19 +1031,16 @@ err:
bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
uint flags)
{
- SELECT_LEX *end, *view_select;
+ SELECT_LEX *end, *UNINIT_VAR(view_select);
LEX *old_lex, *lex;
Query_arena *arena, backup;
TABLE_LIST *top_view= table->top_table();
- bool parse_status;
+ bool UNINIT_VAR(parse_status);
bool result, view_is_mergeable;
TABLE_LIST *UNINIT_VAR(view_main_select_tables);
DBUG_ENTER("mysql_make_view");
DBUG_PRINT("info", ("table: 0x%lx (%s)", (ulong) table, table->table_name));
- LINT_INIT(parse_status);
- LINT_INIT(view_select);
-
if (table->view)
{
/*
@@ -1178,8 +1127,16 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_VIEW_FRM_NO_USER, ER(ER_VIEW_FRM_NO_USER),
table->db, table->table_name);
- get_default_definer(thd, &table->definer);
+ get_default_definer(thd, &table->definer, false);
}
+
+ /*
+ since 10.0.5 definer.host can never be "" for a User, but it's
+ always "" for a Role. Before 10.0.5 it could be "" for a User,
+ but roles didn't exist. file_version helps.
+ */
+ if (!table->definer.host.str[0] && table->file_version < 2)
+ table->definer.host= host_not_specified; // User, not Role
/*
Initialize view definition context by character set names loaded from
@@ -1303,7 +1260,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
TABLE_LIST *view_tables= lex->query_tables;
TABLE_LIST *view_tables_tail= 0;
TABLE_LIST *tbl;
- Security_context *security_ctx;
+ Security_context *security_ctx= 0;
/*
Check rights to run commands (EXPLAIN SELECT & SHOW CREATE) which show
@@ -1478,6 +1435,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
if (view_select->options & OPTION_TO_QUERY_CACHE)
old_lex->select_lex.options|= OPTION_TO_QUERY_CACHE;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (table->view_suid)
{
/*
@@ -1498,6 +1456,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table,
*/
security_ctx= table->security_ctx;
}
+#endif
/* Assign the context to the tables referenced in the view */
if (view_tables)
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 43d178b7a90..0e81fee0056 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -972,7 +972,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
MYSQL-FUNC : MySQL extention, function
INTERNAL : Not a real token, lex optimization
OPERATOR : SQL operator
- FUTURE-USE : Reserved for futur use
+ FUTURE-USE : Reserved for future use
This makes the code grep-able, and helps maintenance.
*/
@@ -981,6 +981,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token ACCESSIBLE_SYM
%token ACTION /* SQL-2003-N */
%token ADD /* SQL-2003-R */
+%token ADMIN_SYM /* SQL-2003-N */
%token ADDDATE_SYM /* MYSQL-FUNC */
%token AFTER_SYM /* SQL-2003-N */
%token AGAINST
@@ -1083,6 +1084,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token CURDATE /* MYSQL-FUNC */
%token CURRENT_SYM /* SQL-2003-R */
%token CURRENT_USER /* SQL-2003-R */
+%token CURRENT_ROLE /* SQL-2003-R */
%token CURRENT_POS_SYM
%token CURSOR_SYM /* SQL-2003-R */
%token CURSOR_NAME_SYM /* SQL-2003-N */
@@ -1441,6 +1443,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token REVERSE_SYM
%token REVOKE /* SQL-2003-R */
%token RIGHT /* SQL-2003-R */
+%token ROLE_SYM
%token ROLLBACK_SYM /* SQL-2003-R */
%token ROLLUP_SYM /* SQL-2003-R */
%token ROUTINE_SYM /* SQL-2003-N */
@@ -1767,7 +1770,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <symbol> keyword keyword_sp
-%type <lex_user> user grant_user
+%type <lex_user> user grant_user grant_role user_or_role current_role
+ admin_option_for_role
%type <charset>
opt_collate
@@ -1798,7 +1802,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
show describe load alter optimize keycache preload flush
reset purge begin commit rollback savepoint release
slave master_def master_defs master_file_def slave_until_opts
- repair analyze
+ repair analyze opt_with_admin opt_with_admin_option
analyze_table_list analyze_table_elem_spec
opt_persistent_stat_clause persistent_stat_spec
persistent_column_stat_spec persistent_index_stat_spec
@@ -1821,7 +1825,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
opt_place
opt_attribute opt_attribute_list attribute column_list column_list_id
opt_column_list grant_privileges grant_ident grant_list grant_option
- object_privilege object_privilege_list user_list rename_list
+ object_privilege object_privilege_list user_list user_and_role_list
+ rename_list
clear_privileges flush_options flush_option
opt_with_read_lock flush_options_list
equal optional_braces
@@ -1895,6 +1900,8 @@ END_OF_INPUT
%type <is_not_empty> opt_union_order_or_limit
+%type <NONE> ROLE_SYM
+
%%
@@ -2433,6 +2440,10 @@ create:
{
Lex->sql_command = SQLCOM_CREATE_USER;
}
+ | CREATE ROLE_SYM clear_privileges role_list opt_with_admin
+ {
+ Lex->sql_command = SQLCOM_CREATE_ROLE;
+ }
| CREATE LOGFILE_SYM GROUP_SYM logfile_group_info
{
Lex->alter_tablespace_info->ts_cmd_type= CREATE_LOGFILE_GROUP;
@@ -9277,6 +9288,14 @@ function_call_keyword:
Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
Lex->safe_to_cache_query= 0;
}
+ | CURRENT_ROLE optional_braces
+ {
+ $$= new (thd->mem_root) Item_func_current_role(Lex->current_context());
+ if ($$ == NULL)
+ MYSQL_YYABORT;
+ Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION);
+ Lex->safe_to_cache_query= 0;
+ }
| DATE_SYM '(' expr ')'
{
$$= new (thd->mem_root) Item_date_typecast($3);
@@ -11691,6 +11710,10 @@ drop:
{
Lex->sql_command = SQLCOM_DROP_USER;
}
+ | DROP ROLE_SYM clear_privileges role_list
+ {
+ Lex->sql_command = SQLCOM_DROP_ROLE;
+ }
| DROP VIEW_SYM opt_if_exists
{
LEX *lex= Lex;
@@ -12451,20 +12474,16 @@ show_param:
}
| GRANTS
{
- LEX *lex=Lex;
- lex->sql_command= SQLCOM_SHOW_GRANTS;
- LEX_USER *curr_user;
- if (!(curr_user= (LEX_USER*) lex->thd->alloc(sizeof(st_lex_user))))
+ Lex->sql_command= SQLCOM_SHOW_GRANTS;
+ if (!(Lex->grant_user= (LEX_USER*)thd->alloc(sizeof(LEX_USER))))
MYSQL_YYABORT;
- bzero(curr_user, sizeof(st_lex_user));
- lex->grant_user= curr_user;
+ Lex->grant_user->user= current_user_and_current_role;
}
- | GRANTS FOR_SYM user
+ | GRANTS FOR_SYM user_or_role
{
LEX *lex=Lex;
lex->sql_command= SQLCOM_SHOW_GRANTS;
lex->grant_user=$3;
- lex->grant_user->password=null_lex_str;
}
| CREATE DATABASE opt_if_not_exists ident
{
@@ -13885,8 +13904,7 @@ user:
if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
MYSQL_YYABORT;
$$->user = $1;
- $$->host.str= (char *) "%";
- $$->host.length= 1;
+ $$->host= null_lex_str; // User or Role, see get_current_user()
$$->password= null_lex_str;
$$->plugin= empty_lex_str;
$$->auth= empty_lex_str;
@@ -13910,26 +13928,36 @@ user:
system_charset_info, 0) ||
check_host_name(&$$->host))
MYSQL_YYABORT;
- /*
- Convert hostname part of username to lowercase.
- It's OK to use in-place lowercase as long as
- the character set is utf8.
- */
- my_casedn_str(system_charset_info, $$->host.str);
+ if ($$->host.str[0])
+ {
+ /*
+ Convert hostname part of username to lowercase.
+ It's OK to use in-place lowercase as long as
+ the character set is utf8.
+ */
+ my_casedn_str(system_charset_info, $$->host.str);
+ }
+ else
+ {
+ /*
+ fix historical undocumented convention that empty host is the
+ same as '%'
+ */
+ $$->host= host_not_specified;
+ }
}
| CURRENT_USER optional_braces
{
- if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
+ if (!($$=(LEX_USER*)thd->calloc(sizeof(LEX_USER))))
MYSQL_YYABORT;
- /*
- empty LEX_USER means current_user and
- will be handled in the get_current_user() function
- later
- */
- bzero($$, sizeof(LEX_USER));
+ $$->user= current_user;
+ $$->plugin= empty_lex_str;
+ $$->auth= empty_lex_str;
}
;
+user_or_role: user | current_role;
+
/* Keyword that we allow for identifiers (except SP labels) */
keyword:
keyword_sp {}
@@ -14001,6 +14029,7 @@ keyword:
keyword_sp:
ACTION {}
| ADDDATE_SYM {}
+ | ADMIN_SYM {}
| AFTER_SYM {}
| AGAINST {}
| AGGREGATE_SYM {}
@@ -14230,6 +14259,7 @@ keyword_sp:
| RETURNED_SQLSTATE_SYM {}
| RETURNS_SYM {}
| REVERSE_SYM {}
+ | ROLE_SYM {}
| ROLLUP_SYM {}
| ROUTINE_SYM {}
| ROWS_SYM {}
@@ -14556,6 +14586,12 @@ option_value_no_option_type:
MYSQL_YYABORT;
lex->var_list.push_back(var);
}
+ | ROLE_SYM ident_or_text
+ {
+ LEX *lex = Lex;
+ set_var_role *var= new set_var_role($2);
+ lex->var_list.push_back(var);
+ }
| PASSWORD equal text_or_password
{
LEX *lex= thd->lex;
@@ -14570,11 +14606,9 @@ option_value_no_option_type:
my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), pw.str);
MYSQL_YYABORT;
}
- if (!(user=(LEX_USER*) thd->alloc(sizeof(LEX_USER))))
+ if (!(user=(LEX_USER*) thd->calloc(sizeof(LEX_USER))))
MYSQL_YYABORT;
- user->host=null_lex_str;
- user->user.str=thd->security_ctx->user;
- user->user.length= strlen(thd->security_ctx->user);
+ user->user= current_user;
set_var_password *var= new set_var_password(user, $3);
if (var == NULL)
MYSQL_YYABORT;
@@ -14959,13 +14993,13 @@ revoke:
;
revoke_command:
- grant_privileges ON opt_table grant_ident FROM user_list
+ grant_privileges ON opt_table grant_ident FROM user_and_role_list
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_REVOKE;
lex->type= 0;
}
- | grant_privileges ON FUNCTION_SYM grant_ident FROM user_list
+ | grant_privileges ON FUNCTION_SYM grant_ident FROM user_and_role_list
{
LEX *lex= Lex;
if (lex->columns.elements)
@@ -14976,7 +15010,7 @@ revoke_command:
lex->sql_command= SQLCOM_REVOKE;
lex->type= TYPE_ENUM_FUNCTION;
}
- | grant_privileges ON PROCEDURE_SYM grant_ident FROM user_list
+ | grant_privileges ON PROCEDURE_SYM grant_ident FROM user_and_role_list
{
LEX *lex= Lex;
if (lex->columns.elements)
@@ -14987,7 +15021,7 @@ revoke_command:
lex->sql_command= SQLCOM_REVOKE;
lex->type= TYPE_ENUM_PROCEDURE;
}
- | ALL opt_privileges ',' GRANT OPTION FROM user_list
+ | ALL opt_privileges ',' GRANT OPTION FROM user_and_role_list
{
Lex->sql_command = SQLCOM_REVOKE_ALL;
}
@@ -14997,9 +15031,22 @@ revoke_command:
lex->users_list.push_front ($3);
lex->sql_command= SQLCOM_REVOKE;
lex->type= TYPE_ENUM_PROXY;
- }
+ }
+ | admin_option_for_role FROM user_and_role_list
+ {
+ Lex->sql_command= SQLCOM_REVOKE_ROLE;
+ if (Lex->users_list.push_front($1))
+ MYSQL_YYABORT;
+ }
;
+admin_option_for_role:
+ ADMIN_SYM OPTION FOR_SYM grant_role
+ { Lex->with_admin_option= true; $$= $4; }
+ | grant_role
+ { Lex->with_admin_option= false; $$= $1; }
+ ;
+
grant:
GRANT clear_privileges grant_command
{}
@@ -15043,7 +15090,65 @@ grant_command:
lex->users_list.push_front ($3);
lex->sql_command= SQLCOM_GRANT;
lex->type= TYPE_ENUM_PROXY;
- }
+ }
+ | grant_role TO_SYM grant_list opt_with_admin_option
+ {
+ LEX *lex= Lex;
+ lex->sql_command= SQLCOM_GRANT_ROLE;
+ /* The first role is the one that is granted */
+ if (Lex->users_list.push_front($1))
+ MYSQL_YYABORT;
+ }
+
+ ;
+
+opt_with_admin:
+ /* nothing */ { Lex->definer = 0; }
+ | WITH ADMIN_SYM user_or_role { Lex->definer = $3; }
+
+opt_with_admin_option:
+ /* nothing */ { Lex->with_admin_option= false; }
+ | WITH ADMIN_SYM OPTION { Lex->with_admin_option= true; }
+
+role_list:
+ grant_role
+ {
+ if (Lex->users_list.push_back($1))
+ MYSQL_YYABORT;
+ }
+ | role_list ',' grant_role
+ {
+ if (Lex->users_list.push_back($3))
+ MYSQL_YYABORT;
+ }
+ ;
+
+current_role:
+ CURRENT_ROLE optional_braces
+ {
+ if (!($$=(LEX_USER*) thd->alloc(sizeof(LEX_USER))))
+ MYSQL_YYABORT;
+ $$->user= current_role;
+ }
+ ;
+
+grant_role:
+ ident_or_text
+ {
+ if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
+ MYSQL_YYABORT;
+ $$->user = $1;
+ $$->host= empty_lex_str;
+ $$->password= null_lex_str;
+ $$->plugin= empty_lex_str;
+ $$->auth= empty_lex_str;
+
+ if (check_string_char_length(&$$->user, ER(ER_USERNAME),
+ username_char_length,
+ system_charset_info, 0))
+ MYSQL_YYABORT;
+ }
+ | current_role
;
opt_table:
@@ -15233,6 +15338,19 @@ grant_list:
}
;
+user_and_role_list:
+ user_or_role
+ {
+ if (Lex->users_list.push_back($1))
+ MYSQL_YYABORT;
+ }
+ | user_and_role_list ',' user_or_role
+ {
+ if (Lex->users_list.push_back($3))
+ MYSQL_YYABORT;
+ }
+ ;
+
via_or_with: VIA_SYM | WITH ;
using_or_as: USING | AS ;
@@ -15283,7 +15401,7 @@ grant_user:
$1->plugin= $4;
$1->auth= $6;
}
- | user
+ | user_or_role
{ $$= $1; $1->password= null_lex_str; }
;
@@ -15715,9 +15833,9 @@ no_definer:
;
definer:
- DEFINER_SYM EQ user
+ DEFINER_SYM EQ user_or_role
{
- thd->lex->definer= get_current_user(thd, $3);
+ thd->lex->definer= $3;
}
;
diff --git a/sql/structs.h b/sql/structs.h
index e5e65e01064..2de7abb666d 100644
--- a/sql/structs.h
+++ b/sql/structs.h
@@ -190,6 +190,14 @@ typedef int *(*update_var)(THD *, struct st_mysql_show_var *);
typedef struct st_lex_user {
LEX_STRING user, host, password, plugin, auth;
+ bool is_role() { return user.str[0] && !host.str[0]; }
+ void set_lex_string(LEX_STRING *l, char *buf)
+ {
+ if (is_role())
+ *l= user;
+ else
+ l->length= strxmov(l->str= buf, user.str, "@", host.str, NullS) - buf;
+ }
} LEX_USER;
/*
diff --git a/sql/table.h b/sql/table.h
index 24035b9dfa8..ce7032b4c60 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -251,7 +251,8 @@ typedef struct st_grant_info
@details The version of this copy is found in GRANT_INFO::version.
*/
- GRANT_TABLE *grant_table;
+ GRANT_TABLE *grant_table_user;
+ GRANT_TABLE *grant_table_role;
/**
@brief Used for cache invalidation when caching privilege information.
diff --git a/storage/myisam/mi_write.c b/storage/myisam/mi_write.c
index cdf4f5d2bf8..5ac5a128918 100644
--- a/storage/myisam/mi_write.c
+++ b/storage/myisam/mi_write.c
@@ -55,6 +55,10 @@ int mi_write(MI_INFO *info, uchar *record)
DBUG_EXECUTE_IF("myisam_pretend_crashed_table_on_usage",
mi_print_error(info->s, HA_ERR_CRASHED);
DBUG_RETURN(my_errno= HA_ERR_CRASHED););
+
+ /* it's always a bug to try to write a record with the deleted flag set */
+ DBUG_ASSERT(info->s->data_file_type != STATIC_RECORD || *record);
+
if (share->options & HA_OPTION_READ_ONLY_DATA)
{
DBUG_RETURN(my_errno=EACCES);