@@ -179,6 +179,8 @@ static uint my_end_arg;
179179static char *opt_mysql_unix_port = nullptr ;
180180static char *opt_bind_addr = nullptr ;
181181static int first_error = 0 ;
182+ static bool opt_dump_users = false ;
183+ static bool opt_add_drop_user = false ;
182184#include " client/include/authentication_kerberos_clientopt-vars.h"
183185#include " client/include/caching_sha2_passwordopt-vars.h"
184186#include " client/include/multi_factor_passwordopt-vars.h"
@@ -250,6 +252,7 @@ const char *default_dbug_option = "d:t:o,/tmp/mysqldump.trace";
250252bool seen_views = false ;
251253
252254collation_unordered_set<string> *ignore_table;
255+ collation_unordered_set<string> *exclude_user, *include_user;
253256
254257static struct my_option my_long_options[] = {
255258 {" all-databases" , ' A' ,
@@ -272,6 +275,9 @@ static struct my_option my_long_options[] = {
272275 {" add-drop-trigger" , 0 , " Add a DROP TRIGGER before each create." ,
273276 &opt_drop_trigger, &opt_drop_trigger, nullptr , GET_BOOL, NO_ARG, 0 , 0 , 0 ,
274277 nullptr , 0 , nullptr },
278+ {" add-drop-user" , 0 , " Add DROP USER when dumping the user definitions" ,
279+ &opt_add_drop_user, &opt_add_drop_user, nullptr , GET_BOOL, OPT_ARG, 0 , 0 ,
280+ 0 , nullptr , 0 , nullptr },
275281 {" add-locks" , OPT_LOCKS, " Add locks around INSERT statements." , &opt_lock,
276282 &opt_lock, nullptr , GET_BOOL, NO_ARG, 1 , 0 , 0 , nullptr , 0 , nullptr },
277283 {" allow-keywords" , OPT_KEYWORDS,
@@ -453,6 +459,21 @@ static struct my_option my_long_options[] = {
453459 " --ignore-table=database.table." ,
454460 nullptr , nullptr , nullptr , GET_STR, REQUIRED_ARG, 0 , 0 , 0 , nullptr , 0 ,
455461 nullptr },
462+ {" exclude-user" , OPT_MYSQLDUMP_EXCLUDE_USER,
463+ " Do not dump the specified user account. To specify more than one user "
464+ " account to exclude, use the directive multiple times, once for each user "
465+ " account. Each user account must be specified with both user and host "
466+ " part, e.g., --exclude-user=foo@localhost." ,
467+ nullptr , nullptr , nullptr , GET_STR, REQUIRED_ARG, 0 , 0 , 0 , nullptr , 0 ,
468+ nullptr },
469+ {" include-user" , OPT_MYSQLDUMP_INCLUDE_USER,
470+ " Dump the specified user account. If no --include-user is specified, dump "
471+ " all user accounts by default. To specify more than one user account to "
472+ " dump, use the directive multiple times, once for each user account. "
473+ " Each user account must be specified with both user and host part, e.g., "
474+ " --exclude-users=foo@localhost." ,
475+ nullptr , nullptr , nullptr , GET_STR, REQUIRED_ARG, 0 , 0 , 0 , nullptr , 0 ,
476+ nullptr },
456477 {" include-source-host-port" , OPT_MYSQLDUMP_INCLUDE_SOURCE_HOST_PORT,
457478 " Adds 'SOURCE_HOST=<host>, SOURCE_PORT=<port>' to 'CHANGE REPLICATION "
458479 " SOURCE TO..' "
@@ -666,6 +687,11 @@ static struct my_option my_long_options[] = {
666687 {" user" , ' u' , " User for login if not current user." , ¤t_user,
667688 ¤t_user, nullptr , GET_STR, REQUIRED_ARG, 0 , 0 , 0 , nullptr , 0 ,
668689 nullptr },
690+ {" users" , 0 ,
691+ " Dump user accounts as logical definitions in the form of CREATE USER and "
692+ " GRANT statements. Not compatible with --flush-privileges!" ,
693+ &opt_dump_users, &opt_dump_users, nullptr , GET_BOOL, OPT_ARG, 0 , 0 , 0 ,
694+ nullptr , 0 , nullptr },
669695 {" verbose" , ' v' , " Print info about the various stages." , &verbose, &verbose,
670696 nullptr , GET_BOOL, NO_ARG, 0 , 0 , 0 , nullptr , 0 , nullptr },
671697 {" version" , ' V' , " Output version information and exit." , nullptr , nullptr ,
@@ -767,6 +793,9 @@ static void verbose_msg(const char *fmt, ...)
767793 MY_ATTRIBUTE((format(printf, 1 , 2 )));
768794static char const *fix_identifier_with_newline (char const *object_name,
769795 bool *freemem);
796+ static bool dump_users (FILE *sql_file);
797+ static bool dump_grants (FILE *sql_file);
798+ static bool fetch_users_list_if_include_is_empty ();
770799
771800static std::unordered_map<string, string> compatibility_rpl_replica_commands = {
772801 {" SHOW REPLICA STATUS" , " SHOW SLAVE STATUS" },
@@ -1095,7 +1124,23 @@ static bool get_one_option(int optid, const struct my_option *opt,
10951124 " Illegal use of option --ignore-table=<database>.<table>\n " );
10961125 exit (1 );
10971126 }
1098- ignore_table->insert (argument);
1127+ ignore_table->emplace (argument);
1128+ break ;
1129+ }
1130+ case (int )OPT_MYSQLDUMP_EXCLUDE_USER: {
1131+ if (!strchr (argument, ' @' )) {
1132+ fprintf (stderr, " Illegal use of option --exclude-user=<user>@<host>\n " );
1133+ exit (1 );
1134+ }
1135+ exclude_user->emplace (argument);
1136+ break ;
1137+ }
1138+ case (int )OPT_MYSQLDUMP_INCLUDE_USER: {
1139+ if (!strchr (argument, ' @' )) {
1140+ fprintf (stderr, " Illegal use of option --include-user=<user>@<host>\n " );
1141+ exit (1 );
1142+ }
1143+ include_user->emplace (argument);
10991144 break ;
11001145 }
11011146 case (int )OPT_COMPATIBLE: {
@@ -1177,6 +1222,10 @@ static int get_options(int *argc, char ***argv) {
11771222
11781223 ignore_table =
11791224 new collation_unordered_set<string>(charset_info, PSI_NOT_INSTRUMENTED);
1225+ exclude_user =
1226+ new collation_unordered_set<string>(charset_info, PSI_NOT_INSTRUMENTED);
1227+ include_user =
1228+ new collation_unordered_set<string>(charset_info, PSI_NOT_INSTRUMENTED);
11801229 /* Don't copy internal log tables */
11811230 ignore_table->insert (" mysql.apply_status" );
11821231 ignore_table->insert (" mysql.schema" );
@@ -1192,6 +1241,47 @@ static int get_options(int *argc, char ***argv) {
11921241 &opt_net_buffer_length)) {
11931242 exit (1 );
11941243 }
1244+ if (opt_dump_users) {
1245+ if (flush_privileges) {
1246+ fprintf (
1247+ stderr,
1248+ " %s: The --users option is incompatible with --flush-privileges\n " ,
1249+ my_progname);
1250+ return (EX_USAGE);
1251+ }
1252+ if (opt_xml) {
1253+ fprintf (stderr, " %s: The --users option is incompatible with --xml\n " ,
1254+ my_progname);
1255+ return (EX_USAGE);
1256+ }
1257+ /* ignore the ACL tables if we are dumping logical ACL */
1258+ ignore_table->insert (" mysql.user" );
1259+ ignore_table->insert (" mysql.global_grants" );
1260+ ignore_table->insert (" mysql.db" );
1261+ ignore_table->insert (" mysql.tables_priv" );
1262+ ignore_table->insert (" mysql.columns_priv" );
1263+ ignore_table->insert (" mysql.procs_priv" );
1264+ ignore_table->insert (" mysql.proxies_priv" );
1265+ ignore_table->insert (" mysql.default_roles" );
1266+ ignore_table->insert (" mysql.role_edges" );
1267+ ignore_table->insert (" mysql.password_history" );
1268+ } else {
1269+ if (include_user->size () > 0 ) {
1270+ fprintf (stderr,
1271+ " %s: The --include-user option is a no-op without --users\n " ,
1272+ my_progname);
1273+ }
1274+ if (exclude_user->size () > 0 ) {
1275+ fprintf (stderr,
1276+ " %s: The --exclude-user option is a no-op without --users\n " ,
1277+ my_progname);
1278+ }
1279+ if (opt_add_drop_user) {
1280+ fprintf (stderr,
1281+ " %s: The --add-drop-user option is a no-op without --users\n " ,
1282+ my_progname);
1283+ }
1284+ }
11951285
11961286 if (debug_info_flag) my_end_arg = MY_CHECK_ERROR | MY_GIVE_INFO;
11971287 if (debug_check_flag) my_end_arg = MY_CHECK_ERROR;
@@ -1242,7 +1332,8 @@ static int get_options(int *argc, char ***argv) {
12421332 !(charset_info =
12431333 get_charset_by_csname (default_charset, MY_CS_PRIMARY, MYF (MY_WME))))
12441334 exit (1 );
1245- if ((*argc < 1 && !opt_alldbs) || (*argc > 0 && opt_alldbs)) {
1335+ if ((*argc < 1 && !opt_alldbs && !opt_dump_users) ||
1336+ (*argc > 0 && opt_alldbs)) {
12461337 short_usage ();
12471338 return EX_USAGE;
12481339 }
@@ -1625,10 +1716,12 @@ static void free_resources() {
16251716 if (md_result_file && md_result_file != stdout)
16261717 my_fclose (md_result_file, MYF (0 ));
16271718 free_passwords ();
1628- if (ignore_table != nullptr ) {
1629- delete ignore_table;
1630- ignore_table = nullptr ;
1631- }
1719+ delete ignore_table;
1720+ ignore_table = nullptr ;
1721+ delete include_user;
1722+ include_user = nullptr ;
1723+ delete exclude_user;
1724+ exclude_user = nullptr ;
16321725 if (insert_pat_inited) dynstr_free (&insert_pat);
16331726 if (opt_ignore_error) my_free (opt_ignore_error);
16341727 opt_init_commands.free ();
@@ -4564,6 +4657,8 @@ static int dump_tablespaces_for_databases(char **databases) {
45644657 int r;
45654658 int i;
45664659
4660+ if (databases[0 ] == nullptr ) return 0 ;
4661+
45674662 init_dynamic_string_checked (&where,
45684663 " AND TABLESPACE_NAME IN ("
45694664 " SELECT DISTINCT TABLESPACE_NAME FROM"
@@ -5151,10 +5246,10 @@ static int dump_all_tables_in_db(char *database) {
51515246 " -- Warning: get_table_structure() failed with some internal "
51525247 " error for 'slow_log' table\n " );
51535248 }
5154- }
5155- if (flush_privileges && using_mysql_db) {
5156- fprintf (md_result_file, " \n -- \n -- Flush Grant Tables \n -- \n " );
5157- fprintf (md_result_file, " \n /*! FLUSH PRIVILEGES */; \n " );
5249+ if (flush_privileges) {
5250+ fprintf (md_result_file, " \n -- \n -- Flush Grant Tables \n -- \n " );
5251+ fprintf (md_result_file, " \n /*! FLUSH PRIVILEGES */; \n " );
5252+ }
51585253 }
51595254 return 0 ;
51605255} /* dump_all_tables_in_db */
@@ -6372,6 +6467,86 @@ static bool get_view_structure(char *table, char *db) {
63726467 return false ;
63736468}
63746469
6470+ static bool fetch_users_list_if_include_is_empty () {
6471+ if (include_user->size () != 0 ) return false ;
6472+
6473+ const char *enum_users_query =
6474+ " SELECT CONCAT('\\ '',user,'\\ '@\\ '',host,'\\ ''),"
6475+ " CONCAT(QUOTE(user),'@',QUOTE(host)), CONCAT(user,'@',host)"
6476+ " FROM mysql.user" ;
6477+ MYSQL_RES *enum_users_res;
6478+ MYSQL_ROW enum_users_row;
6479+ if (mysql_query_with_error_report (mysql, &enum_users_res, enum_users_query))
6480+ return true ;
6481+ if (!enum_users_res) return true ;
6482+ auto enum_users_res_guard =
6483+ create_scope_guard ([&] { mysql_free_result (enum_users_res); });
6484+
6485+ while ((enum_users_row = mysql_fetch_row (enum_users_res)) != nullptr ) {
6486+ const char *user_name;
6487+ if (strcmp (enum_users_row[0 ], enum_users_row[1 ]) ||
6488+ strpbrk (enum_users_row[0 ], " .%- " ))
6489+ user_name = enum_users_row[1 ];
6490+ else
6491+ user_name = enum_users_row[2 ];
6492+ if (exclude_user->find (user_name) == exclude_user->end ()) {
6493+ include_user->emplace (user_name);
6494+ }
6495+ }
6496+ return false ;
6497+ }
6498+
6499+ static void retract_excluded_users () {
6500+ for (auto user : *exclude_user) include_user->erase (user);
6501+ }
6502+
6503+ /* *
6504+ @brief Do a logical dump of all ACL data: users, roles, grants
6505+
6506+ @param sql_file The output SQL file to write to
6507+ @retval false success
6508+ @retval true failure
6509+ */
6510+ static bool dump_users (FILE *sql_file) {
6511+ for (auto user : *include_user) {
6512+ if (opt_add_drop_user) fprintf (sql_file, " DROP USER %s;\n " , user.c_str ());
6513+
6514+ /* execute and dump SHOW CREATE USER */
6515+ MYSQL_RES *res;
6516+ MYSQL_ROW row;
6517+ std::string query = " SHOW CREATE USER " + user;
6518+ if (mysql_query_with_error_report (mysql, &res, query.c_str ())) return true ;
6519+ if (!res) return true ;
6520+ auto res_guard = create_scope_guard ([&] { mysql_free_result (res); });
6521+ if (nullptr == (row = mysql_fetch_row (res))) {
6522+ DB_error (mysql, " retrieving SHOW CREATE USER result" );
6523+ return true ;
6524+ }
6525+ fprintf (sql_file, " %s;\n " , row[0 ]);
6526+ }
6527+ return false ;
6528+ }
6529+
6530+ static bool dump_grants (FILE *sql_file) {
6531+ for (auto user : *include_user) {
6532+ /* execute and dump SHOW GRANTS */
6533+ MYSQL_RES *res;
6534+ MYSQL_ROW row;
6535+ std::string query = " SHOW GRANTS FOR " + user;
6536+ if (mysql_query_with_error_report (mysql, &res, query.c_str ())) return true ;
6537+ if (!res) return true ;
6538+ auto res_guard = create_scope_guard ([&] { mysql_free_result (res); });
6539+ while (nullptr != (row = mysql_fetch_row (res))) {
6540+ fprintf (sql_file, " %s;\n " , row[0 ]);
6541+ }
6542+ if (mysql_errno (mysql) != 0 ) {
6543+ DB_error (mysql, " retrieving SHOW CREATE USER result" );
6544+ return true ;
6545+ }
6546+ }
6547+ return false ;
6548+ }
6549+
63756550/*
63766551 The following functions are wrappers for the dynamic string functions
63776552 and if they fail, the wrappers will terminate the current process.
@@ -6503,6 +6678,12 @@ int main(int argc, char **argv) {
65036678 */
65046679 if (process_set_gtid_purged (mysql, server_has_gtid_enabled)) goto err;
65056680
6681+ if (opt_dump_users) {
6682+ retract_excluded_users ();
6683+ fetch_users_list_if_include_is_empty ();
6684+ dump_users (md_result_file);
6685+ }
6686+
65066687 if (opt_source_data && do_show_binary_log_status (mysql)) goto err;
65076688 if (opt_replica_data && do_show_replica_status (mysql)) goto err;
65086689 if (opt_single_transaction &&
@@ -6548,6 +6729,8 @@ int main(int argc, char **argv) {
65486729 }
65496730 }
65506731
6732+ if (opt_dump_users) dump_grants (md_result_file);
6733+
65516734 /* if --dump-replica , start the replica sql thread */
65526735 if (opt_replica_data && do_start_replica_sql (mysql)) goto err;
65536736
0 commit comments