@@ -179,6 +179,8 @@ static uint my_end_arg;
179
179
static char *opt_mysql_unix_port = nullptr ;
180
180
static char *opt_bind_addr = nullptr ;
181
181
static int first_error = 0 ;
182
+ static bool opt_dump_users = false ;
183
+ static bool opt_add_drop_user = false ;
182
184
#include " client/include/authentication_kerberos_clientopt-vars.h"
183
185
#include " client/include/caching_sha2_passwordopt-vars.h"
184
186
#include " client/include/multi_factor_passwordopt-vars.h"
@@ -250,6 +252,7 @@ const char *default_dbug_option = "d:t:o,/tmp/mysqldump.trace";
250
252
bool seen_views = false ;
251
253
252
254
collation_unordered_set<string> *ignore_table;
255
+ collation_unordered_set<string> *exclude_user, *include_user;
253
256
254
257
static struct my_option my_long_options[] = {
255
258
{" all-databases" , ' A' ,
@@ -272,6 +275,9 @@ static struct my_option my_long_options[] = {
272
275
{" add-drop-trigger" , 0 , " Add a DROP TRIGGER before each create." ,
273
276
&opt_drop_trigger, &opt_drop_trigger, nullptr , GET_BOOL, NO_ARG, 0 , 0 , 0 ,
274
277
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 },
275
281
{" add-locks" , OPT_LOCKS, " Add locks around INSERT statements." , &opt_lock,
276
282
&opt_lock, nullptr , GET_BOOL, NO_ARG, 1 , 0 , 0 , nullptr , 0 , nullptr },
277
283
{" allow-keywords" , OPT_KEYWORDS,
@@ -453,6 +459,21 @@ static struct my_option my_long_options[] = {
453
459
" --ignore-table=database.table." ,
454
460
nullptr , nullptr , nullptr , GET_STR, REQUIRED_ARG, 0 , 0 , 0 , nullptr , 0 ,
455
461
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 },
456
477
{" include-source-host-port" , OPT_MYSQLDUMP_INCLUDE_SOURCE_HOST_PORT,
457
478
" Adds 'SOURCE_HOST=<host>, SOURCE_PORT=<port>' to 'CHANGE REPLICATION "
458
479
" SOURCE TO..' "
@@ -666,6 +687,11 @@ static struct my_option my_long_options[] = {
666
687
{" user" , ' u' , " User for login if not current user." , ¤t_user,
667
688
¤t_user, nullptr , GET_STR, REQUIRED_ARG, 0 , 0 , 0 , nullptr , 0 ,
668
689
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 },
669
695
{" verbose" , ' v' , " Print info about the various stages." , &verbose, &verbose,
670
696
nullptr , GET_BOOL, NO_ARG, 0 , 0 , 0 , nullptr , 0 , nullptr },
671
697
{" version" , ' V' , " Output version information and exit." , nullptr , nullptr ,
@@ -767,6 +793,9 @@ static void verbose_msg(const char *fmt, ...)
767
793
MY_ATTRIBUTE((format(printf, 1 , 2 )));
768
794
static char const *fix_identifier_with_newline (char const *object_name,
769
795
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 ();
770
799
771
800
static std::unordered_map<string, string> compatibility_rpl_replica_commands = {
772
801
{" SHOW REPLICA STATUS" , " SHOW SLAVE STATUS" },
@@ -1095,7 +1124,23 @@ static bool get_one_option(int optid, const struct my_option *opt,
1095
1124
" Illegal use of option --ignore-table=<database>.<table>\n " );
1096
1125
exit (1 );
1097
1126
}
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);
1099
1144
break ;
1100
1145
}
1101
1146
case (int )OPT_COMPATIBLE: {
@@ -1177,6 +1222,10 @@ static int get_options(int *argc, char ***argv) {
1177
1222
1178
1223
ignore_table =
1179
1224
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);
1180
1229
/* Don't copy internal log tables */
1181
1230
ignore_table->insert (" mysql.apply_status" );
1182
1231
ignore_table->insert (" mysql.schema" );
@@ -1192,6 +1241,47 @@ static int get_options(int *argc, char ***argv) {
1192
1241
&opt_net_buffer_length)) {
1193
1242
exit (1 );
1194
1243
}
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
+ }
1195
1285
1196
1286
if (debug_info_flag) my_end_arg = MY_CHECK_ERROR | MY_GIVE_INFO;
1197
1287
if (debug_check_flag) my_end_arg = MY_CHECK_ERROR;
@@ -1242,7 +1332,8 @@ static int get_options(int *argc, char ***argv) {
1242
1332
!(charset_info =
1243
1333
get_charset_by_csname (default_charset, MY_CS_PRIMARY, MYF (MY_WME))))
1244
1334
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)) {
1246
1337
short_usage ();
1247
1338
return EX_USAGE;
1248
1339
}
@@ -1625,10 +1716,12 @@ static void free_resources() {
1625
1716
if (md_result_file && md_result_file != stdout)
1626
1717
my_fclose (md_result_file, MYF (0 ));
1627
1718
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 ;
1632
1725
if (insert_pat_inited) dynstr_free (&insert_pat);
1633
1726
if (opt_ignore_error) my_free (opt_ignore_error);
1634
1727
opt_init_commands.free ();
@@ -4564,6 +4657,8 @@ static int dump_tablespaces_for_databases(char **databases) {
4564
4657
int r;
4565
4658
int i;
4566
4659
4660
+ if (databases[0 ] == nullptr ) return 0 ;
4661
+
4567
4662
init_dynamic_string_checked (&where,
4568
4663
" AND TABLESPACE_NAME IN ("
4569
4664
" SELECT DISTINCT TABLESPACE_NAME FROM"
@@ -5151,10 +5246,10 @@ static int dump_all_tables_in_db(char *database) {
5151
5246
" -- Warning: get_table_structure() failed with some internal "
5152
5247
" error for 'slow_log' table\n " );
5153
5248
}
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
+ }
5158
5253
}
5159
5254
return 0 ;
5160
5255
} /* dump_all_tables_in_db */
@@ -6372,6 +6467,86 @@ static bool get_view_structure(char *table, char *db) {
6372
6467
return false ;
6373
6468
}
6374
6469
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
+
6375
6550
/*
6376
6551
The following functions are wrappers for the dynamic string functions
6377
6552
and if they fail, the wrappers will terminate the current process.
@@ -6503,6 +6678,12 @@ int main(int argc, char **argv) {
6503
6678
*/
6504
6679
if (process_set_gtid_purged (mysql, server_has_gtid_enabled)) goto err;
6505
6680
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
+
6506
6687
if (opt_source_data && do_show_binary_log_status (mysql)) goto err;
6507
6688
if (opt_replica_data && do_show_replica_status (mysql)) goto err;
6508
6689
if (opt_single_transaction &&
@@ -6548,6 +6729,8 @@ int main(int argc, char **argv) {
6548
6729
}
6549
6730
}
6550
6731
6732
+ if (opt_dump_users) dump_grants (md_result_file);
6733
+
6551
6734
/* if --dump-replica , start the replica sql thread */
6552
6735
if (opt_replica_data && do_start_replica_sql (mysql)) goto err;
6553
6736
0 commit comments