diff options
Diffstat (limited to 'client/mysqldump.c')
-rw-r--r-- | client/mysqldump.c | 600 |
1 files changed, 464 insertions, 136 deletions
diff --git a/client/mysqldump.c b/client/mysqldump.c index 07afc3119b5..260a6e0113b 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -39,7 +39,7 @@ ** 10 Jun 2003: SET NAMES and --no-set-names by Alexander Barkov */ -#define DUMP_VERSION "10.14" +#define DUMP_VERSION "10.15" #include <my_global.h> #include <my_sys.h> @@ -87,14 +87,16 @@ /* Chars needed to store LONGLONG, excluding trailing '\0'. */ #define LONGLONG_LEN 20 +/* Max length GTID position that we will output. */ +#define MAX_GTID_LENGTH 1024 + static void add_load_option(DYNAMIC_STRING *str, const char *option, const char *option_value); -static ulong find_set(TYPELIB *lib, const char *x, size_t length, - char **err_pos, uint *err_len); +static ulong find_set(TYPELIB *, const char *, size_t, char **, uint *); static char *alloc_query_str(ulong size); static void field_escape(DYNAMIC_STRING* in, const char *from); -static my_bool verbose= 0, opt_no_create_info= 0, opt_no_data= 0, +static my_bool verbose= 0, opt_no_create_info= 0, opt_no_data= 0, opt_no_data_med= 1, quick= 1, extended_insert= 1, lock_tables=1,ignore_errors=0,flush_logs=0,flush_privileges=0, opt_drop=1,opt_keywords=0,opt_lock=1,opt_compress=0, @@ -134,10 +136,23 @@ static ulong opt_compatible_mode= 0; #define MYSQL_OPT_SLAVE_DATA_COMMENTED_SQL 2 static uint opt_mysql_port= 0, opt_master_data; static uint opt_slave_data; +static uint opt_use_gtid; static uint my_end_arg; static char * opt_mysql_unix_port=0; static int first_error=0; +/* + multi_source is 0 if old server or 2 if server that support multi source + This is choosen this was as multi_source has 2 extra columns first in + SHOW ALL SLAVES STATUS. +*/ +static uint multi_source= 0; static DYNAMIC_STRING extended_row; +static DYNAMIC_STRING dynamic_where; +static MYSQL_RES *get_table_name_result= NULL; +static MEM_ROOT glob_root; +static MYSQL_RES *routine_res, *routine_list_res; + + #include <sslopt-vars.h> FILE *md_result_file= 0; FILE *stderror_file=0; @@ -193,6 +208,8 @@ const char *compatible_mode_names[]= TYPELIB compatible_mode_typelib= {array_elements(compatible_mode_names) - 1, "", compatible_mode_names, NULL}; +#define MED_ENGINES "MRG_MyISAM, MRG_ISAM, CONNECT, OQGRAPH, SPIDER, VP, FEDERATED" + HASH ignore_table; static struct my_option my_long_options[] = @@ -226,8 +243,8 @@ static struct my_option my_long_options[] = &opt_slave_apply, &opt_slave_apply, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"character-sets-dir", OPT_CHARSETS_DIR, - "Directory for character set files.", (char**) &charsets_dir, - (char**) &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + "Directory for character set files.", &charsets_dir, + &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"comments", 'i', "Write additional information.", &opt_comments, &opt_comments, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, @@ -264,8 +281,8 @@ static struct my_option my_long_options[] = {"debug", '#', "This is a non-debug version. Catch this and exit.", 0,0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0}, #else - {"debug", '#', "Output debug log.", (char**) &default_dbug_option, - (char**) &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, + {"debug", '#', "Output debug log.", &default_dbug_option, + &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, #endif {"debug-check", OPT_DEBUG_CHECK, "Check memory and open file usage at exit.", &debug_check_flag, &debug_check_flag, 0, @@ -340,6 +357,13 @@ static struct my_option my_long_options[] = {"force", 'f', "Continue even if we get an SQL error.", &ignore_errors, &ignore_errors, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"gtid", OPT_USE_GTID, "Used together with --master-data=1 or --dump-slave=1." + "When enabled, the output from those options will set the GTID position " + "instead of the binlog file and offset; the file/offset will appear only as " + "a comment. When disabled, the GTID position will still appear in the " + "output, but only commented.", + &opt_use_gtid, &opt_use_gtid, 0, GET_BOOL, NO_ARG, + 0, 0, 0, 0, 0, 0}, {"help", '?', "Display this help message and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"hex-blob", OPT_HEXBLOB, "Dump binary strings (BINARY, " @@ -412,6 +436,9 @@ static struct my_option my_long_options[] = NO_ARG, 0, 0, 0, 0, 0, 0}, {"no-data", 'd', "No row information.", &opt_no_data, &opt_no_data, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"no-data-med", 0, "No row information for engines that " + "Manage External Data (" MED_ENGINES ").", &opt_no_data_med, + &opt_no_data_med, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, {"no-set-names", 'N', "Same as --skip-set-charset.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"opt", OPT_OPTIMIZE, @@ -889,7 +916,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), &err_ptr, &err_len); if (err_len) { - strmake(buff, err_ptr, min(sizeof(buff) - 1, err_len)); + strmake(buff, err_ptr, MY_MIN(sizeof(buff) - 1, err_len)); fprintf(stderr, "Invalid mode to --compatible: %s\n", buff); exit(1); } @@ -1215,6 +1242,90 @@ check_consistent_binlog_pos(char *binlog_pos_file, char *binlog_pos_offset) return (found == 2); } + +/* + Get the GTID position corresponding to a given old-style binlog position. + This uses BINLOG_GTID_POS(). The advantage is that the GTID position can + be obtained completely non-blocking in this way (without the need for + FLUSH TABLES WITH READ LOCK), as the old-style position can be obtained + with START TRANSACTION WITH CONSISTENT SNAPSHOT. + + Returns 0 if ok, non-zero if error. +*/ +static int +get_binlog_gtid_pos(char *binlog_pos_file, char *binlog_pos_offset, + char *out_gtid_pos) +{ + DYNAMIC_STRING query; + MYSQL_RES *res; + MYSQL_ROW row; + int err; + char file_buf[FN_REFLEN*2+1], offset_buf[LONGLONG_LEN*2+1]; + size_t len_pos_file= strlen(binlog_pos_file); + size_t len_pos_offset= strlen(binlog_pos_offset); + + if (len_pos_file >= FN_REFLEN || len_pos_offset > LONGLONG_LEN) + return 0; + mysql_real_escape_string(mysql, file_buf, binlog_pos_file, len_pos_file); + mysql_real_escape_string(mysql, offset_buf, binlog_pos_offset, len_pos_offset); + init_dynamic_string_checked(&query, "SELECT BINLOG_GTID_POS('", 256, 1024); + dynstr_append_checked(&query, file_buf); + dynstr_append_checked(&query, "', '"); + dynstr_append_checked(&query, offset_buf); + dynstr_append_checked(&query, "')"); + + err= mysql_query_with_error_report(mysql, &res, query.str); + dynstr_free(&query); + if (err) + return err; + + err= 1; + if ((row= mysql_fetch_row(res))) + { + strmake(out_gtid_pos, row[0], MAX_GTID_LENGTH-1); + err= 0; + } + mysql_free_result(res); + + return err; +} + + +/* + Get the GTID position on a master or slave. + The parameter MASTER is non-zero to get the position on a master + (@@gtid_binlog_pos) or zero for a slave (@@gtid_slave_pos). + + This uses the @@gtid_binlog_pos or @@gtid_slave_pos, so requires FLUSH TABLES + WITH READ LOCK or similar to be consistent. + + Returns 0 if ok, non-zero for error. +*/ +static int +get_gtid_pos(char *out_gtid_pos, int master) +{ + MYSQL_RES *res; + MYSQL_ROW row; + int found; + + if (mysql_query_with_error_report(mysql, &res, + (master ? + "SELECT @@GLOBAL.gtid_binlog_pos" : + "SELECT @@GLOBAL.gtid_slave_pos"))) + return 1; + + found= 0; + if ((row= mysql_fetch_row(res))) + { + strmake(out_gtid_pos, row[0], MAX_GTID_LENGTH-1); + found++; + } + mysql_free_result(res); + + return (found != 1); +} + + static char *my_case_str(const char *str, size_t str_len, const char *token, @@ -1500,14 +1611,26 @@ static void free_resources() { if (md_result_file && md_result_file != stdout) my_fclose(md_result_file, MYF(0)); + if (get_table_name_result) + mysql_free_result(get_table_name_result); + if (routine_res) + mysql_free_result(routine_res); + if (routine_list_res) + mysql_free_result(routine_list_res); + if (mysql) + { + mysql_close(mysql); + mysql= 0; + } + my_free(order_by); my_free(opt_password); my_free(current_host); + free_root(&glob_root, MYF(0)); if (my_hash_inited(&ignore_table)) my_hash_free(&ignore_table); - if (extended_insert) - dynstr_free(&extended_row); - if (insert_pat_inited) - dynstr_free(&insert_pat); + dynstr_free(&extended_row); + dynstr_free(&dynamic_where); + dynstr_free(&insert_pat); if (defaults_argv) free_defaults(defaults_argv); mysql_library_end(); @@ -1524,8 +1647,6 @@ static void maybe_exit(int error) ignore_errors= 1; /* don't want to recurse, if something fails below */ if (opt_slave_data) do_start_slave_sql(mysql); - if (mysql) - mysql_close(mysql); free_resources(); exit(error); } @@ -1546,8 +1667,12 @@ static int connect_to_db(char *host, char *user,char *passwd) mysql_options(&mysql_connection,MYSQL_OPT_COMPRESS,NullS); #ifdef HAVE_OPENSSL if (opt_use_ssl) + { mysql_ssl_set(&mysql_connection, opt_ssl_key, opt_ssl_cert, opt_ssl_ca, opt_ssl_capath, opt_ssl_cipher); + mysql_options(&mysql_connection, MYSQL_OPT_SSL_CRL, opt_ssl_crl); + mysql_options(&mysql_connection, MYSQL_OPT_SSL_CRLPATH, opt_ssl_crlpath); + } mysql_options(&mysql_connection,MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (char*)&opt_ssl_verify_server_cert); #endif @@ -1565,6 +1690,9 @@ static int connect_to_db(char *host, char *user,char *passwd) if (opt_default_auth && *opt_default_auth) mysql_options(&mysql_connection, MYSQL_DEFAULT_AUTH, opt_default_auth); + mysql_options(&mysql_connection, MYSQL_OPT_CONNECT_ATTR_RESET, 0); + mysql_options4(&mysql_connection, MYSQL_OPT_CONNECT_ATTR_ADD, + "program_name", "mysqldump"); mysql= &mysql_connection; /* So we can mysql_close() it properly */ if (!mysql_real_connect(&mysql_connection,host,user,passwd, NULL,opt_mysql_port,opt_mysql_unix_port, 0)) @@ -1611,6 +1739,7 @@ static void dbDisconnect(char *host) { verbose_msg("-- Disconnecting from %s...\n", host ? host : "localhost"); mysql_close(mysql); + mysql= 0; } /* dbDisconnect */ @@ -2318,7 +2447,6 @@ static uint dump_routines_for_db(char *db) char *routine_name; int i; FILE *sql_file= md_result_file; - MYSQL_RES *routine_res, *routine_list_res; MYSQL_ROW row, routine_list_row; char db_cl_name[MY_CS_NAME_SIZE]; @@ -2374,7 +2502,11 @@ static uint dump_routines_for_db(char *db) routine_type[i], routine_name); if (mysql_query_with_error_report(mysql, &routine_res, query_buff)) + { + mysql_free_result(routine_list_res); + routine_list_res= 0; DBUG_RETURN(1); + } while ((row= mysql_fetch_row(routine_res))) { @@ -2392,7 +2524,8 @@ static uint dump_routines_for_db(char *db) print_comment(sql_file, 1, "-- does %s have permissions on mysql.proc?\n\n", fix_for_comment(current_user)); - maybe_die(EX_MYSQLERR,"%s has insufficent privileges to %s!", current_user, query_buff); + maybe_die(EX_MYSQLERR,"%s has insufficent privileges to %s!", + current_user, query_buff); } else if (strlen(row[2])) { @@ -2415,6 +2548,9 @@ static uint dump_routines_for_db(char *db) if (switch_db_collation(sql_file, db, ";", db_cl_name, row[5], &db_cl_altered)) { + mysql_free_result(routine_res); + mysql_free_result(routine_list_res); + routine_res= routine_list_res= 0; DBUG_RETURN(1); } @@ -2460,17 +2596,24 @@ static uint dump_routines_for_db(char *db) if (db_cl_altered) { if (restore_db_collation(sql_file, db, ";", db_cl_name)) + { + mysql_free_result(routine_res); + mysql_free_result(routine_list_res); + routine_res= routine_list_res= 0; DBUG_RETURN(1); + } } } } } /* end of routine printing */ mysql_free_result(routine_res); + routine_res= 0; } /* end of list of routines */ } mysql_free_result(routine_list_res); + routine_list_res= 0; } /* end of for i (0 .. 1) */ if (opt_xml) @@ -2592,13 +2735,20 @@ static uint get_table_structure(char *table, char *db, char *table_type, if (switch_character_set_results(mysql, "binary") || mysql_query_with_error_report(mysql, &result, buff) || switch_character_set_results(mysql, default_charset)) + { + my_free(order_by); + order_by= 0; DBUG_RETURN(0); + } if (path) { if (!(sql_file= open_sql_file_for_table(table, O_WRONLY))) + { + my_free(order_by); + order_by= 0; DBUG_RETURN(0); - + } write_header(sql_file, db); } @@ -2667,6 +2817,8 @@ static uint get_table_structure(char *table, char *db, char *table_type, my_free(scv_buff); + if (path) + my_fclose(sql_file, MYF(MY_WME)); DBUG_RETURN(0); } else @@ -3168,10 +3320,6 @@ static int dump_trigger(FILE *sql_file, MYSQL_RES *show_create_trigger_rs, continue; } - query_str= cover_definer_clause(row[2], strlen(row[2]), - C_STRING_WITH_LEN("50017"), - C_STRING_WITH_LEN("50003"), - C_STRING_WITH_LEN(" TRIGGER")); if (switch_db_collation(sql_file, db_name, ";", db_cl_name, row[5], &db_cl_altered)) DBUG_RETURN(TRUE); @@ -3183,12 +3331,18 @@ static int dump_trigger(FILE *sql_file, MYSQL_RES *show_create_trigger_rs, switch_sql_mode(sql_file, ";", row[1]); + query_str= cover_definer_clause(row[2], strlen(row[2]), + C_STRING_WITH_LEN("50017"), + C_STRING_WITH_LEN("50003"), + C_STRING_WITH_LEN(" TRIGGER")); fprintf(sql_file, "DELIMITER ;;\n" "/*!50003 %s */;;\n" "DELIMITER ;\n", (const char *) (query_str != NULL ? query_str : row[2])); + my_free(query_str); + restore_sql_mode(sql_file, ";"); restore_cs_variables(sql_file, ";"); @@ -3197,8 +3351,6 @@ static int dump_trigger(FILE *sql_file, MYSQL_RES *show_create_trigger_rs, if (restore_db_collation(sql_file, db_name, ";", db_cl_name)) DBUG_RETURN(TRUE); } - - my_free(query_str); } DBUG_RETURN(FALSE); @@ -3289,13 +3441,14 @@ static int dump_triggers_for_table(char *table_name, char *db_name) { MYSQL_RES *show_create_trigger_rs= mysql_store_result(mysql); - if (!show_create_trigger_rs || - dump_trigger(sql_file, show_create_trigger_rs, db_name, db_cl_name)) - goto done; - + int error= (!show_create_trigger_rs || + dump_trigger(sql_file, show_create_trigger_rs, db_name, + db_cl_name)); mysql_free_result(show_create_trigger_rs); + if (error) + goto done; } - + } if (opt_xml) @@ -3542,12 +3695,14 @@ static void dump_table(char *table, char *db) { dynstr_append_checked(&query_string, " ORDER BY "); dynstr_append_checked(&query_string, order_by); + my_free(order_by); + order_by= 0; } if (mysql_real_query(mysql, query_string.str, query_string.length)) { - DB_error(mysql, "when executing 'SELECT INTO OUTFILE'"); dynstr_free(&query_string); + DB_error(mysql, "when executing 'SELECT INTO OUTFILE'"); DBUG_VOID_RETURN; } } @@ -3573,6 +3728,8 @@ static void dump_table(char *table, char *db) dynstr_append_checked(&query_string, " ORDER BY "); dynstr_append_checked(&query_string, order_by); + my_free(order_by); + order_by= 0; } if (!opt_xml && !opt_compact) @@ -3582,6 +3739,7 @@ static void dump_table(char *table, char *db) } if (mysql_query_with_error_report(mysql, 0, query_string.str)) { + dynstr_free(&query_string); DB_error(mysql, "when retrieving data from server"); goto err; } @@ -3591,6 +3749,7 @@ static void dump_table(char *table, char *db) res=mysql_store_result(mysql); if (!res) { + dynstr_free(&query_string); DB_error(mysql, "when retrieving data from server"); goto err; } @@ -3906,23 +4065,22 @@ err: static char *getTableName(int reset) { - static MYSQL_RES *res= NULL; - MYSQL_ROW row; + MYSQL_ROW row; - if (!res) + if (!get_table_name_result) { - if (!(res= mysql_list_tables(mysql,NullS))) + if (!(get_table_name_result= mysql_list_tables(mysql,NullS))) return(NULL); } - if ((row= mysql_fetch_row(res))) + if ((row= mysql_fetch_row(get_table_name_result))) return((char*) row[0]); if (reset) - mysql_data_seek(res,0); /* We want to read again */ + mysql_data_seek(get_table_name_result,0); /* We want to read again */ else { - mysql_free_result(res); - res= NULL; + mysql_free_result(get_table_name_result); + get_table_name_result= NULL; } return(NULL); } /* getTableName */ @@ -3939,46 +4097,44 @@ static int dump_all_tablespaces() static int dump_tablespaces_for_tables(char *db, char **table_names, int tables) { - DYNAMIC_STRING where; int r; int i; char name_buff[NAME_LEN*2+3]; mysql_real_escape_string(mysql, name_buff, db, (ulong)strlen(db)); - init_dynamic_string_checked(&where, " AND TABLESPACE_NAME IN (" + init_dynamic_string_checked(&dynamic_where, " AND TABLESPACE_NAME IN (" "SELECT DISTINCT TABLESPACE_NAME FROM" " INFORMATION_SCHEMA.PARTITIONS" " WHERE" " TABLE_SCHEMA='", 256, 1024); - dynstr_append_checked(&where, name_buff); - dynstr_append_checked(&where, "' AND TABLE_NAME IN ("); + dynstr_append_checked(&dynamic_where, name_buff); + dynstr_append_checked(&dynamic_where, "' AND TABLE_NAME IN ("); for (i=0 ; i<tables ; i++) { mysql_real_escape_string(mysql, name_buff, table_names[i], (ulong)strlen(table_names[i])); - dynstr_append_checked(&where, "'"); - dynstr_append_checked(&where, name_buff); - dynstr_append_checked(&where, "',"); + dynstr_append_checked(&dynamic_where, "'"); + dynstr_append_checked(&dynamic_where, name_buff); + dynstr_append_checked(&dynamic_where, "',"); } - dynstr_trunc(&where, 1); - dynstr_append_checked(&where,"))"); + dynstr_trunc(&dynamic_where, 1); + dynstr_append_checked(&dynamic_where,"))"); - DBUG_PRINT("info",("Dump TS for Tables where: %s",where.str)); - r= dump_tablespaces(where.str); - dynstr_free(&where); + DBUG_PRINT("info",("Dump TS for Tables where: %s",dynamic_where.str)); + r= dump_tablespaces(dynamic_where.str); + dynstr_free(&dynamic_where); return r; } static int dump_tablespaces_for_databases(char** databases) { - DYNAMIC_STRING where; int r; int i; - init_dynamic_string_checked(&where, " AND TABLESPACE_NAME IN (" + init_dynamic_string_checked(&dynamic_where, " AND TABLESPACE_NAME IN (" "SELECT DISTINCT TABLESPACE_NAME FROM" " INFORMATION_SCHEMA.PARTITIONS" " WHERE" @@ -3989,16 +4145,16 @@ static int dump_tablespaces_for_databases(char** databases) char db_name_buff[NAME_LEN*2+3]; mysql_real_escape_string(mysql, db_name_buff, databases[i], (ulong)strlen(databases[i])); - dynstr_append_checked(&where, "'"); - dynstr_append_checked(&where, db_name_buff); - dynstr_append_checked(&where, "',"); + dynstr_append_checked(&dynamic_where, "'"); + dynstr_append_checked(&dynamic_where, db_name_buff); + dynstr_append_checked(&dynamic_where, "',"); } - dynstr_trunc(&where, 1); - dynstr_append_checked(&where,"))"); + dynstr_trunc(&dynamic_where, 1); + dynstr_append_checked(&dynamic_where,"))"); - DBUG_PRINT("info",("Dump TS for DBs where: %s",where.str)); - r= dump_tablespaces(where.str); - dynstr_free(&where); + DBUG_PRINT("info",("Dump TS for DBs where: %s",dynamic_where.str)); + r= dump_tablespaces(dynamic_where.str); + dynstr_free(&dynamic_where); return r; } @@ -4408,9 +4564,12 @@ static int dump_all_tables_in_db(char *database) } } if (numrows && mysql_real_query(mysql, query.str, query.length-1)) + { + dynstr_free(&query); DB_error(mysql, "when using LOCK TABLES"); - /* We shall continue here, if --force was given */ - dynstr_free(&query); + /* We shall continue here, if --force was given */ + } + dynstr_free(&query); /* Safe to call twice */ } if (flush_logs) { @@ -4420,6 +4579,14 @@ static int dump_all_tables_in_db(char *database) else verbose_msg("-- dump_all_tables_in_db : logs flushed successfully!\n"); } + if (opt_single_transaction && mysql_get_server_version(mysql) >= 50500) + { + verbose_msg("-- Setting savepoint...\n"); + if (mysql_query_with_error_report(mysql, 0, "SAVEPOINT sp")) + { + DBUG_RETURN(1); + } + } while ((table= getTableName(0))) { char *end= strmov(afterdot, table); @@ -4437,6 +4604,23 @@ static int dump_all_tables_in_db(char *database) maybe_exit(EX_MYSQLERR); } } + + /** + ROLLBACK TO SAVEPOINT in --single-transaction mode to release metadata + lock on table which was already dumped. This allows to avoid blocking + concurrent DDL on this table without sacrificing correctness, as we + won't access table second time and dumps created by --single-transaction + mode have validity point at the start of transaction anyway. + Note that this doesn't make --single-transaction mode with concurrent + DDL safe in general case. It just improves situation for people for whom + it might be working. + */ + if (opt_single_transaction && mysql_get_server_version(mysql) >= 50500) + { + verbose_msg("-- Rolling back to savepoint sp...\n"); + if (mysql_query_with_error_report(mysql, 0, "ROLLBACK TO SAVEPOINT sp")) + maybe_exit(EX_MYSQLERR); + } } else { @@ -4459,6 +4643,14 @@ static int dump_all_tables_in_db(char *database) } } } + + if (opt_single_transaction && mysql_get_server_version(mysql) >= 50500) + { + verbose_msg("-- Releasing savepoint...\n"); + if (mysql_query_with_error_report(mysql, 0, "RELEASE SAVEPOINT sp")) + DBUG_RETURN(1); + } + if (opt_events && mysql_get_server_version(mysql) >= 50106) { DBUG_PRINT("info", ("Dumping events for database %s", database)); @@ -4628,22 +4820,22 @@ static int dump_selected_tables(char *db, char **table_names, int tables) { char table_buff[NAME_LEN*2+3]; DYNAMIC_STRING lock_tables_query; - MEM_ROOT root; char **dump_tables, **pos, **end; DBUG_ENTER("dump_selected_tables"); if (init_dumping(db, init_dumping_tables)) DBUG_RETURN(1); - init_alloc_root(&root, 8192, 0); - if (!(dump_tables= pos= (char**) alloc_root(&root, tables * sizeof(char *)))) + init_alloc_root(&glob_root, 8192, 0, MYF(0)); + if (!(dump_tables= pos= (char**) alloc_root(&glob_root, + tables * sizeof(char *)))) die(EX_EOM, "alloc_root failure."); init_dynamic_string_checked(&lock_tables_query, "LOCK TABLES ", 256, 1024); for (; tables > 0 ; tables-- , table_names++) { /* the table name passed on commandline may be wrong case */ - if ((*pos= get_actual_table_name(*table_names, &root))) + if ((*pos= get_actual_table_name(*table_names, &glob_root))) { /* Add found table name to lock_tables_query */ if (lock_tables) @@ -4658,7 +4850,7 @@ static int dump_selected_tables(char *db, char **table_names, int tables) if (!ignore_errors) { dynstr_free(&lock_tables_query); - free_root(&root, MYF(0)); + free_root(&glob_root, MYF(0)); } maybe_die(EX_ILLEGAL_TABLE, "Couldn't find table: \"%s\"", *table_names); /* We shall countinue here, if --force was given */ @@ -4679,7 +4871,7 @@ static int dump_selected_tables(char *db, char **table_names, int tables) if (!ignore_errors) { dynstr_free(&lock_tables_query); - free_root(&root, MYF(0)); + free_root(&glob_root, MYF(0)); } DB_error(mysql, "when doing LOCK TABLES"); /* We shall countinue here, if --force was given */ @@ -4691,7 +4883,7 @@ static int dump_selected_tables(char *db, char **table_names, int tables) if (mysql_refresh(mysql, REFRESH_LOG)) { if (!ignore_errors) - free_root(&root, MYF(0)); + free_root(&glob_root, MYF(0)); DB_error(mysql, "when doing refresh"); } /* We shall countinue here, if --force was given */ @@ -4701,12 +4893,23 @@ static int dump_selected_tables(char *db, char **table_names, int tables) if (opt_xml) print_xml_tag(md_result_file, "", "\n", "database", "name=", db, NullS); + /* obtain dump of routines (procs/functions) */ if (opt_routines && mysql_get_server_version(mysql) >= 50009) { DBUG_PRINT("info", ("Dumping routines for database %s", db)); dump_routines_for_db(db); } + + if (opt_single_transaction && mysql_get_server_version(mysql) >= 50500) + { + verbose_msg("-- Setting savepoint...\n"); + if (mysql_query_with_error_report(mysql, 0, "SAVEPOINT sp")) + { + free_root(&glob_root, MYF(0)); + DBUG_RETURN(1); + } + } /* Dump each selected table */ for (pos= dump_tables; pos < end; pos++) { @@ -4719,9 +4922,42 @@ static int dump_selected_tables(char *db, char **table_names, int tables) { if (path) my_fclose(md_result_file, MYF(MY_WME)); + if (!ignore_errors) + free_root(&glob_root, MYF(0)); maybe_exit(EX_MYSQLERR); } } + + /** + ROLLBACK TO SAVEPOINT in --single-transaction mode to release metadata + lock on table which was already dumped. This allows to avoid blocking + concurrent DDL on this table without sacrificing correctness, as we + won't access table second time and dumps created by --single-transaction + mode have validity point at the start of transaction anyway. + Note that this doesn't make --single-transaction mode with concurrent + DDL safe in general case. It just improves situation for people for whom + it might be working. + */ + if (opt_single_transaction && mysql_get_server_version(mysql) >= 50500) + { + verbose_msg("-- Rolling back to savepoint sp...\n"); + if (mysql_query_with_error_report(mysql, 0, "ROLLBACK TO SAVEPOINT sp")) + { + if (!ignore_errors) + free_root(&glob_root, MYF(0)); + maybe_exit(EX_MYSQLERR); + } + } + } + + if (opt_single_transaction && mysql_get_server_version(mysql) >= 50500) + { + verbose_msg("-- Releasing savepoint...\n"); + if (mysql_query_with_error_report(mysql, 0, "RELEASE SAVEPOINT sp")) + { + free_root(&glob_root, MYF(0)); + DBUG_RETURN(1); + } } /* Dump each selected view */ @@ -4735,9 +4971,7 @@ static int dump_selected_tables(char *db, char **table_names, int tables) DBUG_PRINT("info", ("Dumping events for database %s", db)); dump_events_for_db(db); } - free_root(&root, MYF(0)); - my_free(order_by); - order_by= 0; + free_root(&glob_root, MYF(0)); if (opt_xml) { fputs("</database>\n", md_result_file); @@ -4749,12 +4983,14 @@ static int dump_selected_tables(char *db, char **table_names, int tables) } /* dump_selected_tables */ -static int do_show_master_status(MYSQL *mysql_con, int consistent_binlog_pos) +static int do_show_master_status(MYSQL *mysql_con, int consistent_binlog_pos, + int have_mariadb_gtid, int use_gtid) { MYSQL_ROW row; MYSQL_RES *UNINIT_VAR(master); char binlog_pos_file[FN_REFLEN]; char binlog_pos_offset[LONGLONG_LEN+1]; + char gtid_pos[MAX_GTID_LENGTH]; char *file, *offset; const char *comment_prefix= (opt_master_data == MYSQL_OPT_MASTER_DATA_COMMENTED_SQL) ? "-- " : ""; @@ -4765,10 +5001,14 @@ static int do_show_master_status(MYSQL *mysql_con, int consistent_binlog_pos) return 1; file= binlog_pos_file; offset= binlog_pos_offset; + if (have_mariadb_gtid && + get_binlog_gtid_pos(binlog_pos_file, binlog_pos_offset, gtid_pos)) + return 1; } else { - if (mysql_query_with_error_report(mysql_con, &master, "SHOW MASTER STATUS")) + if (mysql_query_with_error_report(mysql_con, &master, + "SHOW MASTER STATUS")) return 1; row= mysql_fetch_row(master); @@ -4793,6 +5033,9 @@ static int do_show_master_status(MYSQL *mysql_con, int consistent_binlog_pos) return 0; } } + + if (have_mariadb_gtid && get_gtid_pos(gtid_pos, 1)) + return 1; } /* SHOW MASTER STATUS reports file and position */ @@ -4801,7 +5044,19 @@ static int do_show_master_status(MYSQL *mysql_con, int consistent_binlog_pos) "recovery from\n--\n\n"); fprintf(md_result_file, "%sCHANGE MASTER TO MASTER_LOG_FILE='%s', MASTER_LOG_POS=%s;\n", - comment_prefix, file, offset); + (use_gtid ? "-- " : comment_prefix), file, offset); + if (have_mariadb_gtid) + { + print_comment(md_result_file, 0, + "\n--\n-- GTID to start replication from\n--\n\n"); + if (use_gtid) + fprintf(md_result_file, + "%sCHANGE MASTER TO MASTER_USE_GTID=slave_pos;\n", + comment_prefix); + fprintf(md_result_file, + "%sSET GLOBAL gtid_slave_pos='%s';\n", + (!use_gtid ? "-- " : comment_prefix), gtid_pos); + } check_io(md_result_file); if (!consistent_binlog_pos) @@ -4813,29 +5068,37 @@ static int do_show_master_status(MYSQL *mysql_con, int consistent_binlog_pos) static int do_stop_slave_sql(MYSQL *mysql_con) { MYSQL_RES *slave; - /* We need to check if the slave sql is running in the first place */ - if (mysql_query_with_error_report(mysql_con, &slave, "SHOW SLAVE STATUS")) + MYSQL_ROW row; + + if (mysql_query_with_error_report(mysql_con, &slave, + multi_source ? + "SHOW ALL SLAVES STATUS" : + "SHOW SLAVE STATUS")) return(1); - else + + /* Loop over all slaves */ + while ((row= mysql_fetch_row(slave))) { - MYSQL_ROW row= mysql_fetch_row(slave); - if (row && row[11]) + if (row[11 + multi_source]) { /* if SLAVE SQL is not running, we don't stop it */ - if (!strcmp(row[11],"No")) + if (strcmp(row[11 + multi_source], "No")) { - mysql_free_result(slave); - /* Silently assume that they don't have the slave running */ - return(0); + char query[160]; + if (multi_source) + sprintf(query, "STOP SLAVE '%.80s' SQL_THREAD", row[0]); + else + strmov(query, "STOP SLAVE SQL_THREAD"); + + if (mysql_query_with_error_report(mysql_con, 0, query)) + { + mysql_free_result(slave); + return 1; + } } } } mysql_free_result(slave); - - /* now, stop slave if running */ - if (mysql_query_with_error_report(mysql_con, 0, "STOP SLAVE SQL_THREAD")) - return(1); - return(0); } @@ -4844,7 +5107,10 @@ static int add_stop_slave(void) if (opt_comments) fprintf(md_result_file, "\n--\n-- stop slave statement to make a recovery dump)\n--\n\n"); - fprintf(md_result_file, "STOP SLAVE;\n"); + if (multi_source) + fprintf(md_result_file, "STOP ALL SLAVES;\n"); + else + fprintf(md_result_file, "STOP SLAVE;\n"); return(0); } @@ -4853,16 +5119,28 @@ static int add_slave_statements(void) if (opt_comments) fprintf(md_result_file, "\n--\n-- start slave statement to make a recovery dump)\n--\n\n"); - fprintf(md_result_file, "START SLAVE;\n"); + if (multi_source) + fprintf(md_result_file, "START ALL SLAVES;\n"); + else + fprintf(md_result_file, "START SLAVE;\n"); return(0); } -static int do_show_slave_status(MYSQL *mysql_con) +static int do_show_slave_status(MYSQL *mysql_con, int use_gtid, + int have_mariadb_gtid) { - MYSQL_RES *slave= 0; + MYSQL_RES *UNINIT_VAR(slave); + MYSQL_ROW row; const char *comment_prefix= (opt_slave_data == MYSQL_OPT_SLAVE_DATA_COMMENTED_SQL) ? "-- " : ""; - if (mysql_query_with_error_report(mysql_con, &slave, "SHOW SLAVE STATUS")) + const char *gtid_comment_prefix= (use_gtid ? comment_prefix : "-- "); + const char *nogtid_comment_prefix= (!use_gtid ? comment_prefix : "-- "); + int set_gtid_done= 0; + + if (mysql_query_with_error_report(mysql_con, &slave, + multi_source ? + "SHOW ALL SLAVES STATUS" : + "SHOW SLAVE STATUS")) { if (!ignore_errors) { @@ -4872,65 +5150,103 @@ static int do_show_slave_status(MYSQL *mysql_con) mysql_free_result(slave); return 1; } - else + + while ((row= mysql_fetch_row(slave))) { - MYSQL_ROW row= mysql_fetch_row(slave); - if (row && row[9] && row[21]) + if (multi_source && !set_gtid_done) { + char gtid_pos[MAX_GTID_LENGTH]; + if (have_mariadb_gtid && get_gtid_pos(gtid_pos, 0)) + return 1; + if (opt_comments) + fprintf(md_result_file, "\n--\n-- Gtid position to start replication " + "from\n--\n\n"); + fprintf(md_result_file, "%sSET GLOBAL gtid_slave_pos='%s';\n", + gtid_comment_prefix, gtid_pos); + set_gtid_done= 1; + } + if (row[9 + multi_source] && row[21 + multi_source]) + { + if (use_gtid) + { + if (multi_source) + fprintf(md_result_file, "%sCHANGE MASTER '%.80s' TO " + "MASTER_USE_GTID=slave_pos;\n", gtid_comment_prefix, row[0]); + else + fprintf(md_result_file, "%sCHANGE MASTER TO " + "MASTER_USE_GTID=slave_pos;\n", gtid_comment_prefix); + } + /* SHOW MASTER STATUS reports file and position */ if (opt_comments) fprintf(md_result_file, "\n--\n-- Position to start replication or point-in-time " "recovery from (the master of this slave)\n--\n\n"); - fprintf(md_result_file, "%sCHANGE MASTER TO ", comment_prefix); - + if (multi_source) + fprintf(md_result_file, "%sCHANGE MASTER '%.80s' TO ", + nogtid_comment_prefix, row[0]); + else + fprintf(md_result_file, "%sCHANGE MASTER TO ", nogtid_comment_prefix); + if (opt_include_master_host_port) { - if (row[1]) - fprintf(md_result_file, "MASTER_HOST='%s', ", row[1]); + if (row[1 + multi_source]) + fprintf(md_result_file, "MASTER_HOST='%s', ", row[1 + multi_source]); if (row[3]) - fprintf(md_result_file, "MASTER_PORT=%s, ", row[3]); + fprintf(md_result_file, "MASTER_PORT=%s, ", row[3 + multi_source]); } fprintf(md_result_file, - "MASTER_LOG_FILE='%s', MASTER_LOG_POS=%s;\n", row[9], row[21]); + "MASTER_LOG_FILE='%s', MASTER_LOG_POS=%s;\n", + row[9 + multi_source], row[21 + multi_source]); check_io(md_result_file); } - mysql_free_result(slave); } + mysql_free_result(slave); return 0; } static int do_start_slave_sql(MYSQL *mysql_con) { MYSQL_RES *slave; + MYSQL_ROW row; + int error= 0; + DBUG_ENTER("do_start_slave_sql"); + /* We need to check if the slave sql is stopped in the first place */ - if (mysql_query_with_error_report(mysql_con, &slave, "SHOW SLAVE STATUS")) - return(1); - else + if (mysql_query_with_error_report(mysql_con, &slave, + multi_source ? + "SHOW ALL SLAVES STATUS" : + "SHOW SLAVE STATUS")) + DBUG_RETURN(1); + + while ((row= mysql_fetch_row(slave))) { - MYSQL_ROW row= mysql_fetch_row(slave); - if (row && row[11]) + DBUG_PRINT("info", ("Connection: '%s' status: '%s'", + multi_source ? row[0] : "", row[11 + multi_source])); + if (row[11 + multi_source]) { /* if SLAVE SQL is not running, we don't start it */ - if (!strcmp(row[11],"Yes")) + if (strcmp(row[11 + multi_source], "Yes")) { - mysql_free_result(slave); - /* Silently assume that they don't have the slave running */ - return(0); + char query[160]; + if (multi_source) + sprintf(query, "START SLAVE '%.80s'", row[0]); + else + strmov(query, "START SLAVE"); + + if (mysql_query_with_error_report(mysql_con, 0, query)) + { + fprintf(stderr, "%s: Error: Unable to start slave '%s'\n", + my_progname_short, multi_source ? row[0] : ""); + error= 1; + } } } } mysql_free_result(slave); - - /* now, start slave if stopped */ - if (mysql_query_with_error_report(mysql_con, 0, "START SLAVE")) - { - fprintf(stderr, "%s: Error: Unable to start slave\n", my_progname_short); - return 1; - } - return(0); + DBUG_RETURN(error); } @@ -4944,12 +5260,13 @@ static int do_flush_tables_read_lock(MYSQL *mysql_con) FLUSH TABLES is to lower the probability of a stage where both mysqldump and most client connections are stalled. Of course, if a second long update starts between the two FLUSHes, we have that bad stall. + + We use the LOCAL option, as we do not want the FLUSH TABLES replicated to + other servers. */ return - ( mysql_query_with_error_report(mysql_con, 0, - ((opt_master_data != 0) ? - "FLUSH /*!40101 LOCAL */ TABLES" : - "FLUSH TABLES")) || + ( mysql_query_with_error_report(mysql_con, 0, + "FLUSH /*!40101 LOCAL */ TABLES") || mysql_query_with_error_report(mysql_con, 0, "FLUSH TABLES WITH READ LOCK") ); } @@ -5054,9 +5371,9 @@ static ulong find_set(TYPELIB *lib, const char *x, size_t length, for (; pos != end && *pos != ','; pos++) ; var_len= (uint) (pos - start); - strmake(buff, start, min(sizeof(buff) - 1, var_len)); + strmake(buff, start, MY_MIN(sizeof(buff) - 1, var_len)); find= find_type(buff, lib, FIND_TYPE_BASIC); - if (!find) + if (find <= 0) { *err_pos= (char*) start; *err_len= var_len; @@ -5178,11 +5495,12 @@ char check_if_ignore_table(const char *table_name, char *table_type) /* If these two types, we do want to skip dumping the table */ - if (!opt_no_data && - (!my_strcasecmp(&my_charset_latin1, table_type, "MRG_MyISAM") || - !strcmp(table_type,"MRG_ISAM") || - !strcmp(table_type,"FEDERATED"))) - result= IGNORE_DATA; + if (!opt_no_data && opt_no_data_med) + { + const char *found= strstr(" " MED_ENGINES ",", table_type); + if (found && found[-1] == ' ' && found[strlen(table_type)] == ',') + result= IGNORE_DATA; + } } mysql_free_result(res); DBUG_RETURN(result); @@ -5511,8 +5829,7 @@ static my_bool get_view_structure(char *table, char* db) dynstr_free(&ds_view); } - if (switch_character_set_results(mysql, default_charset)) - DBUG_RETURN(1); + switch_character_set_results(mysql, default_charset); /* If a separate .sql file was opened, close it now */ if (sql_file != md_result_file) @@ -5569,6 +5886,7 @@ int main(int argc, char **argv) char bin_log_name[FN_REFLEN]; int exit_code; int consistent_binlog_pos= 0; + int have_mariadb_gtid= 0; MY_INIT(argv[0]); sf_leaking_memory=1; /* don't report memory leaks on early exits */ @@ -5607,6 +5925,13 @@ int main(int argc, char **argv) if (!path) write_header(md_result_file, *argv); + /* Check if the server support multi source */ + if (mysql_get_server_version(mysql) >= 100000) + { + multi_source= 2; + have_mariadb_gtid= 1; + } + if (opt_slave_data && do_stop_slave_sql(mysql)) goto err; @@ -5652,9 +5977,12 @@ int main(int argc, char **argv) /* Add 'STOP SLAVE to beginning of dump */ if (opt_slave_apply && add_stop_slave()) goto err; - if (opt_master_data && do_show_master_status(mysql, consistent_binlog_pos)) + + if (opt_master_data && do_show_master_status(mysql, consistent_binlog_pos, + have_mariadb_gtid, opt_use_gtid)) goto err; - if (opt_slave_data && do_show_slave_status(mysql)) + if (opt_slave_data && do_show_slave_status(mysql, opt_use_gtid, + have_mariadb_gtid)) goto err; if (opt_single_transaction && do_unlock_tables(mysql)) /* unlock but no commit! */ goto err; |