summaryrefslogtreecommitdiffstats
path: root/client/mysqldump.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--client/mysqldump.c7292
1 files changed, 7292 insertions, 0 deletions
diff --git a/client/mysqldump.c b/client/mysqldump.c
new file mode 100644
index 00000000..0a6ebf0e
--- /dev/null
+++ b/client/mysqldump.c
@@ -0,0 +1,7292 @@
+/*
+ Copyright (c) 2000, 2013, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2020, MariaDB Corporation.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
+*/
+
+/* mysqldump.c - Dump a tables contents and format to an ASCII file
+**
+** The author's original notes follow :-
+**
+** AUTHOR: Igor Romanenko (igor@frog.kiev.ua)
+** DATE: December 3, 1994
+** WARRANTY: None, expressed, impressed, implied
+** or other
+** STATUS: Public domain
+** Adapted and optimized for MySQL by
+** Michael Widenius, Sinisa Milivojevic, Jani Tolonen
+** -w --where added 9/10/98 by Jim Faucette
+** slave code by David Saez Padros <david@ols.es>
+** master/autocommit code by Brian Aker <brian@tangent.org>
+** SSL by
+** Andrei Errapart <andreie@no.spam.ee>
+** Tõnu Samuel <tonu@please.do.not.remove.this.spam.ee>
+** XML by Gary Huntress <ghuntress@mediaone.net> 10/10/01, cleaned up
+** and adapted to mysqldump 05/11/01 by Jani Tolonen
+** Added --single-transaction option 06/06/2002 by Peter Zaitsev
+** 10 Jun 2003: SET NAMES and --no-set-names by Alexander Barkov
+*/
+
+/* on merge conflict, bump to a higher version again */
+#define DUMP_VERSION "10.19"
+
+/**
+ First mysql version supporting sequences.
+*/
+#define FIRST_SEQUENCE_VERSION 100300
+
+#include <my_global.h>
+#include <my_sys.h>
+#include <my_user.h>
+#include <m_string.h>
+#include <m_ctype.h>
+#include <hash.h>
+#include <stdarg.h>
+
+#include "client_priv.h"
+#include "mysql.h"
+#include "mysql_version.h"
+#include "mysqld_error.h"
+
+#include <welcome_copyright_notice.h> /* ORACLE_WELCOME_COPYRIGHT_NOTICE */
+
+/* Exit codes */
+
+#define EX_USAGE 1
+#define EX_MYSQLERR 2
+#define EX_CONSCHECK 3
+#define EX_EOM 4
+#define EX_EOF 5 /* ferror for output file was got */
+#define EX_ILLEGAL_TABLE 6
+
+/* index into 'show fields from table' */
+
+#define SHOW_FIELDNAME 0
+#define SHOW_TYPE 1
+#define SHOW_NULL 2
+#define SHOW_DEFAULT 4
+#define SHOW_EXTRA 5
+
+/* Size of buffer for dump's select query */
+#define QUERY_LENGTH 1536
+
+/* Size of comment buffer. */
+#define COMMENT_LENGTH 2048
+
+/* ignore table flags */
+#define IGNORE_NONE 0x00 /* no ignore */
+#define IGNORE_DATA 0x01 /* don't dump data for this table */
+#define IGNORE_INSERT_DELAYED 0x02 /* table doesn't support INSERT DELAYED */
+#define IGNORE_SEQUENCE_TABLE 0x04 /* catch the SEQUENCE*/
+#define IGNORE_S3_TABLE 0x08
+
+/* 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
+
+/* Dump sequence/tables control */
+#define DUMP_TABLE_ALL -1
+#define DUMP_TABLE_TABLE 0
+#define DUMP_TABLE_SEQUENCE 1
+
+static my_bool ignore_table_data(const uchar *hash_key, size_t len);
+static void add_load_option(DYNAMIC_STRING *str, const char *option,
+ const char *option_value);
+static ulong find_set(TYPELIB *, const char *, size_t, char **, uint *);
+static char *alloc_query_str(size_t 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, 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,
+ opt_copy_s3_tables=0,
+ opt_delayed=0,create_options=1,opt_quoted=0,opt_databases=0,
+ opt_alldbs=0,opt_create_db=0,opt_lock_all_tables=0,
+ opt_set_charset=0, opt_dump_date=1,
+ opt_autocommit=0,opt_disable_keys=1,opt_xml=0,
+ opt_delete_master_logs=0, tty_password=0,
+ opt_single_transaction=0, opt_comments= 0, opt_compact= 0,
+ opt_hex_blob=0, opt_order_by_primary=0, opt_order_by_size = 0,
+ opt_ignore=0, opt_complete_insert= 0, opt_drop_database= 0,
+ opt_replace_into= 0,
+ opt_dump_triggers= 0, opt_routines=0, opt_tz_utc=1,
+ opt_slave_apply= 0,
+ opt_include_master_host_port= 0,
+ opt_events= 0, opt_comments_used= 0,
+ opt_alltspcs=0, opt_notspcs= 0, opt_logging,
+ opt_header=0,
+ opt_drop_trigger= 0, opt_dump_history= 0;
+#define OPT_SYSTEM_ALL 1
+#define OPT_SYSTEM_USERS 2
+#define OPT_SYSTEM_PLUGINS 4
+#define OPT_SYSTEM_UDFS 8
+#define OPT_SYSTEM_SERVERS 16
+#define OPT_SYSTEM_STATS 32
+#define OPT_SYSTEM_TIMEZONES 64
+static const char *opt_system_type_values[]=
+ {"all", "users", "plugins", "udfs", "servers", "stats", "timezones"};
+static TYPELIB opt_system_types=
+{
+ array_elements(opt_system_type_values), "system dump options",
+ opt_system_type_values, NULL
+};
+static ulonglong opt_system= 0ULL;
+static my_bool insert_pat_inited= 0, debug_info_flag= 0, debug_check_flag= 0,
+ select_field_names_inited= 0;
+static ulong opt_max_allowed_packet, opt_net_buffer_length;
+static double opt_max_statement_time= 0.0;
+static MYSQL mysql_connection,*mysql=0;
+static DYNAMIC_STRING insert_pat, select_field_names, select_field_names_for_header;
+static char *opt_password=0,*current_user=0,
+ *current_host=0,*path=0,*fields_terminated=0,
+ *lines_terminated=0, *enclosed=0, *opt_enclosed=0, *escaped=0,
+ *where=0, *order_by=0,
+ *err_ptr= 0,
+ *log_error_file= NULL, *opt_asof_timestamp= NULL;
+static const char *opt_compatible_mode_str= 0;
+static char **defaults_argv= 0;
+static char compatible_mode_normal_str[255];
+/* Server supports character_set_results session variable? */
+static my_bool server_supports_switching_charsets= TRUE;
+static ulong opt_compatible_mode= 0;
+#define MYSQL_OPT_MASTER_DATA_EFFECTIVE_SQL 1
+#define MYSQL_OPT_MASTER_DATA_COMMENTED_SQL 2
+#define MYSQL_OPT_MAX_STATEMENT_TIME 0
+#define MYSQL_OPT_SLAVE_DATA_EFFECTIVE_SQL 1
+#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 chosen 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;
+
+static uint opt_protocol= 0;
+static char *opt_plugin_dir= 0, *opt_default_auth= 0;
+
+/*
+ Dynamic_string wrapper functions. In this file use these
+ wrappers, they will terminate the process if there is
+ an allocation failure.
+*/
+static void init_dynamic_string_checked(DYNAMIC_STRING *str, const char *init_str,
+ size_t init_alloc, size_t alloc_increment);
+static void dynstr_append_checked(DYNAMIC_STRING* dest, const char* src);
+static void dynstr_set_checked(DYNAMIC_STRING *str, const char *init_str);
+static void dynstr_append_mem_checked(DYNAMIC_STRING *str, const char *append,
+ uint length);
+static void dynstr_realloc_checked(DYNAMIC_STRING *str, ulong additional_size);
+
+static int do_start_slave_sql(MYSQL *mysql_con);
+/*
+ Constant for detection of default value of default_charset.
+ If default_charset is equal to mysql_universal_client_charset, then
+ it is the default value which assigned at the very beginning of main().
+*/
+static const char *mysql_universal_client_charset=
+ MYSQL_UNIVERSAL_CLIENT_CHARSET;
+static char *default_charset;
+static CHARSET_INFO *charset_info= &my_charset_latin1;
+const char *default_dbug_option="d:t:o,/tmp/mariadb-dump.trace";
+/* have we seen any VIEWs during table scanning? */
+my_bool seen_views= 0;
+const char *compatible_mode_names[]=
+{
+ "MYSQL323", "MYSQL40", "POSTGRESQL", "ORACLE", "MSSQL", "DB2",
+ "MAXDB", "NO_KEY_OPTIONS", "NO_TABLE_OPTIONS", "NO_FIELD_OPTIONS",
+ "ANSI",
+ NullS
+};
+#define MASK_ANSI_QUOTES \
+(\
+ (1U<<2) | /* POSTGRESQL */\
+ (1U<<3) | /* ORACLE */\
+ (1U<<4) | /* MSSQL */\
+ (1U<<5) | /* DB2 */\
+ (1U<<6) | /* MAXDB */\
+ (1U<<10) /* ANSI */\
+)
+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"
+
+static HASH ignore_table, ignore_data;
+
+static HASH ignore_database;
+
+static struct my_option my_long_options[] =
+{
+ {"all-databases", 'A',
+ "Dump all the databases. This will be same as --databases with all databases selected.",
+ &opt_alldbs, &opt_alldbs, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
+ 0, 0},
+ {"all-tablespaces", 'Y',
+ "Dump all the tablespaces.",
+ &opt_alltspcs, &opt_alltspcs, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
+ 0, 0},
+ {"no-tablespaces", 'y',
+ "Do not dump any tablespace information.",
+ &opt_notspcs, &opt_notspcs, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
+ 0, 0},
+ {"add-drop-database", OPT_DROP_DATABASE, "Add a DROP DATABASE before each create.",
+ &opt_drop_database, &opt_drop_database, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0,
+ 0},
+ {"add-drop-table", OPT_DROP, "Add a DROP TABLE before each create.",
+ &opt_drop, &opt_drop, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0,
+ 0},
+ {"add-drop-trigger", 0, "Add a DROP TRIGGER before each create.",
+ &opt_drop_trigger, &opt_drop_trigger, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0,
+ 0},
+ {"add-locks", OPT_LOCKS, "Add locks around INSERT statements.",
+ &opt_lock, &opt_lock, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0,
+ 0},
+ {"allow-keywords", OPT_KEYWORDS,
+ "Allow creation of column names that are keywords.", &opt_keywords,
+ &opt_keywords, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"apply-slave-statements", OPT_MYSQLDUMP_SLAVE_APPLY,
+ "Adds 'STOP SLAVE' prior to 'CHANGE MASTER' and 'START SLAVE' to bottom of dump.",
+ &opt_slave_apply, &opt_slave_apply, 0, GET_BOOL, NO_ARG,
+ 0, 0, 0, 0, 0, 0},
+ {"as-of", OPT_ASOF_TIMESTAMP,
+ "Dump system versioned table(s) as of specified timestamp. "
+ "Argument is interpreted according to the --tz-utc setting. "
+ "Table structures are always dumped as of current timestamp.",
+ &opt_asof_timestamp, &opt_asof_timestamp, 0, GET_STR, REQUIRED_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},
+ {"comments", 'i', "Write additional information.",
+ &opt_comments, &opt_comments, 0, GET_BOOL, NO_ARG,
+ 1, 0, 0, 0, 0, 0},
+ {"compatible", OPT_COMPATIBLE,
+ "Change the dump to be compatible with a given mode. By default tables "
+ "are dumped in a format optimized for MariaDB. Legal modes are: ansi, "
+ "mysql323, mysql40, postgresql, oracle, mssql, db2, maxdb, no_key_options, "
+ "no_table_options, no_field_options. One can use several modes separated "
+ "by commas. Note: Requires MariaDB server version 4.1.0 or higher. "
+ "This option is ignored with earlier server versions.",
+ (char**) &opt_compatible_mode_str, (char**) &opt_compatible_mode_str, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"compact", OPT_COMPACT,
+ "Give less verbose output (useful for debugging). Disables structure "
+ "comments and header/footer constructs. Enables options --skip-add-"
+ "drop-table --skip-add-locks --skip-comments --skip-disable-keys "
+ "--skip-set-charset.",
+ &opt_compact, &opt_compact, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"complete-insert", 'c', "Use complete insert statements.",
+ &opt_complete_insert, &opt_complete_insert, 0, GET_BOOL,
+ NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"compress", 'C', "Use compression in server/client protocol.",
+ &opt_compress, &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0,
+ 0, 0, 0},
+ {"copy_s3_tables", OPT_COPY_S3_TABLES,
+ "If 'no' S3 tables will be ignored, otherwise S3 tables will be copied as "
+ " Aria tables and then altered to S3",
+ &opt_copy_s3_tables, &opt_copy_s3_tables, 0, GET_BOOL, NO_ARG, 0, 0, 0,
+ 0, 0, 0},
+ {"create-options", 'a',
+ "Include all MariaDB specific create options.",
+ &create_options, &create_options, 0, GET_BOOL, NO_ARG, 1,
+ 0, 0, 0, 0, 0},
+ {"databases", 'B',
+ "Dump several databases. Note the difference in usage; in this case no tables are given. All name arguments are regarded as database names. 'USE db_name;' will be included in the output.",
+ &opt_databases, &opt_databases, 0, GET_BOOL, NO_ARG, 0, 0,
+ 0, 0, 0, 0},
+#ifdef DBUG_OFF
+ {"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},
+#endif
+ {"debug-check", OPT_DEBUG_CHECK, "Check memory and open file usage at exit.",
+ &debug_check_flag, &debug_check_flag, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"debug-info", OPT_DEBUG_INFO, "Print some debug info at exit.",
+ &debug_info_flag, &debug_info_flag,
+ 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"default-character-set", OPT_DEFAULT_CHARSET,
+ "Set the default character set.", &default_charset,
+ &default_charset, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"delayed-insert", OPT_DELAYED, "Insert rows with INSERT DELAYED.",
+ &opt_delayed, &opt_delayed, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
+ 0, 0},
+ {"delete-master-logs", OPT_DELETE_MASTER_LOGS,
+ "Delete logs on master after backup. This automatically enables --master-data.",
+ &opt_delete_master_logs, &opt_delete_master_logs, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"disable-keys", 'K',
+ "'/*!40000 ALTER TABLE tb_name DISABLE KEYS */; and '/*!40000 ALTER "
+ "TABLE tb_name ENABLE KEYS */; will be put in the output.", &opt_disable_keys,
+ &opt_disable_keys, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
+ {"dump-date", OPT_DUMP_DATE, "Put a dump date to the end of the output.",
+ &opt_dump_date, &opt_dump_date, 0,
+ GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
+ {"dump-history", 'H', "Dump system-versioned tables with history (only for "
+ "timestamp based versioning)", &opt_dump_history,
+ &opt_dump_history, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"dump-slave", OPT_MYSQLDUMP_SLAVE_DATA,
+ "This causes the binary log position and filename of the master to be "
+ "appended to the dumped data output. Setting the value to 1, will print"
+ "it as a CHANGE MASTER command in the dumped data output; if equal"
+ " to 2, that command will be prefixed with a comment symbol. "
+ "This option will turn --lock-all-tables on, unless "
+ "--single-transaction is specified too (in which case a "
+ "global read lock is only taken a short time at the beginning of the dump "
+ "- don't forget to read about --single-transaction below). In all cases "
+ "any action on logs will happen at the exact moment of the dump."
+ "Option automatically turns --lock-tables off.",
+ &opt_slave_data, &opt_slave_data, 0,
+ GET_UINT, OPT_ARG, 0, 0, MYSQL_OPT_SLAVE_DATA_COMMENTED_SQL, 0, 0, 0},
+ {"events", 'E', "Dump events.",
+ &opt_events, &opt_events, 0, GET_BOOL,
+ NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"extended-insert", 'e',
+ "Use multiple-row INSERT syntax that include several VALUES lists.",
+ &extended_insert, &extended_insert, 0, GET_BOOL, NO_ARG,
+ 1, 0, 0, 0, 0, 0},
+ {"fields-terminated-by", OPT_FTB,
+ "Fields in the output file are terminated by the given string.",
+ &fields_terminated, &fields_terminated, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"fields-enclosed-by", OPT_ENC,
+ "Fields in the output file are enclosed by the given character.",
+ &enclosed, &enclosed, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0 ,0, 0},
+ {"fields-optionally-enclosed-by", OPT_O_ENC,
+ "Fields in the output file are optionally enclosed by the given character.",
+ &opt_enclosed, &opt_enclosed, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0 ,0, 0},
+ {"fields-escaped-by", OPT_ESC,
+ "Fields in the output file are escaped by the given character.",
+ &escaped, &escaped, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"flush-logs", 'F', "Flush logs file in server before starting dump. "
+ "Note that if you dump many databases at once (using the option "
+ "--databases= or --all-databases), the logs will be flushed for "
+ "each database dumped. The exception is when using --lock-all-tables "
+ "or --master-data: "
+ "in this case the logs will be flushed only once, corresponding "
+ "to the moment all tables are locked. So if you want your dump and "
+ "the log flush to happen at the same exact moment you should use "
+ "--lock-all-tables or --master-data with --flush-logs.",
+ &flush_logs, &flush_logs, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
+ 0, 0},
+ {"flush-privileges", OPT_ESC, "Emit a FLUSH PRIVILEGES statement "
+ "after dumping the mysql database. This option should be used any "
+ "time the dump contains the mysql database and any other database "
+ "that depends on the data in the mysql database for proper restore. ",
+ &flush_privileges, &flush_privileges, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
+ 0, 0},
+ {"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", 0, "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},
+ {"header", 0, "Used together with --tab. When enabled, adds header with column names to the top of output txt files.",
+ &opt_header, &opt_header, 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, "
+ "VARBINARY, BLOB) in hexadecimal format.",
+ &opt_hex_blob, &opt_hex_blob, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"host", 'h', "Connect to host.", &current_host,
+ &current_host, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"ignore-database", OPT_IGNORE_DATABASE,
+ "Do not dump the specified database. To specify more than one database to ignore, "
+ "use the directive multiple times, once for each database. Only takes effect "
+ "when used together with --all-databases|-A",
+ 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"ignore-table-data", OPT_IGNORE_DATA,
+ "Do not dump the specified table data. To specify more than one table "
+ "to ignore, use the directive multiple times, once for each table. "
+ "Each table must be specified with both database and table names, e.g., "
+ "--ignore-table-data=database.table.",
+ 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"ignore-table", OPT_IGNORE_TABLE,
+ "Do not dump the specified table. To specify more than one table to ignore, "
+ "use the directive multiple times, once for each table. Each table must "
+ "be specified with both database and table names, e.g., "
+ "--ignore-table=database.table.",
+ 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"include-master-host-port", OPT_MYSQLDUMP_INCLUDE_MASTER_HOST_PORT,
+ "Adds 'MASTER_HOST=<host>, MASTER_PORT=<port>' to 'CHANGE MASTER TO..' "
+ "in dump produced with --dump-slave.", &opt_include_master_host_port,
+ &opt_include_master_host_port, 0, GET_BOOL, NO_ARG,
+ 0, 0, 0, 0, 0, 0},
+ {"insert-ignore", OPT_INSERT_IGNORE, "Insert rows with INSERT IGNORE.",
+ &opt_ignore, &opt_ignore, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
+ 0, 0},
+ {"lines-terminated-by", OPT_LTB,
+ "Lines in the output file are terminated by the given string.",
+ &lines_terminated, &lines_terminated, 0, GET_STR,
+ REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"lock-all-tables", 'x', "Locks all tables across all databases. This "
+ "is achieved by taking a global read lock for the duration of the whole "
+ "dump. Automatically turns --single-transaction and --lock-tables off.",
+ &opt_lock_all_tables, &opt_lock_all_tables, 0, GET_BOOL, NO_ARG,
+ 0, 0, 0, 0, 0, 0},
+ {"lock-tables", 'l', "Lock all tables for read.", &lock_tables,
+ &lock_tables, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
+ {"log-error", OPT_ERROR_LOG_FILE, "Append warnings and errors to given file.",
+ &log_error_file, &log_error_file, 0, GET_STR,
+ REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"log-queries", 0, "When restoring the dump, the server will, if logging turned on, log the queries to the general and slow query log.",
+ &opt_logging, &opt_logging, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
+ {"master-data", OPT_MASTER_DATA,
+ "This causes the binary log position and filename to be appended to the "
+ "output. If equal to 1, will print it as a CHANGE MASTER command; if equal"
+ " to 2, that command will be prefixed with a comment symbol. "
+ "This option will turn --lock-all-tables on, unless --single-transaction "
+ "is specified too (on servers before MariaDB 5.3 this will still take a "
+ "global read lock for a short time at the beginning of the dump; "
+ "don't forget to read about --single-transaction below). In all cases, "
+ "any action on logs will happen at the exact moment of the dump. "
+ "Option automatically turns --lock-tables off.",
+ &opt_master_data, &opt_master_data, 0,
+ GET_UINT, OPT_ARG, 0, 0, MYSQL_OPT_MASTER_DATA_COMMENTED_SQL, 0, 0, 0},
+ {"max_allowed_packet", OPT_MAX_ALLOWED_PACKET,
+ "The maximum packet length to send to or receive from server.",
+ &opt_max_allowed_packet, &opt_max_allowed_packet, 0,
+ GET_ULONG, REQUIRED_ARG, 24*1024*1024, 4096,
+ (longlong) 2L*1024L*1024L*1024L, MALLOC_OVERHEAD, 1024, 0},
+ {"max-statement-time", MYSQL_OPT_MAX_STATEMENT_TIME,
+ "Max statement execution time. If unset, overrides server default with 0.",
+ &opt_max_statement_time, &opt_max_statement_time, 0, GET_DOUBLE,
+ REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"net_buffer_length", OPT_NET_BUFFER_LENGTH,
+ "The buffer size for TCP/IP and socket communication.",
+ &opt_net_buffer_length, &opt_net_buffer_length, 0,
+ GET_ULONG, REQUIRED_ARG, 1024*1024L-1025, 4096, 16*1024L*1024L,
+ MALLOC_OVERHEAD-1024, 1024, 0},
+ {"no-autocommit", OPT_AUTOCOMMIT,
+ "Wrap tables with autocommit/commit statements.",
+ &opt_autocommit, &opt_autocommit, 0, GET_BOOL, NO_ARG,
+ 0, 0, 0, 0, 0, 0},
+ {"no-create-db", 'n',
+ "Suppress the CREATE DATABASE ... IF EXISTS statement that normally is "
+ "output for each dumped database if --all-databases or --databases is "
+ "given.",
+ &opt_create_db, &opt_create_db, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"no-create-info", 't', "Don't write table creation info.",
+ &opt_no_create_info, &opt_no_create_info, 0, GET_BOOL,
+ 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,
+ "Same as --add-drop-table, --add-locks, --create-options, --quick, --extended-insert, --lock-tables, --set-charset, and --disable-keys. Enabled by default, disable with --skip-opt.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"order-by-primary", OPT_ORDER_BY_PRIMARY,
+ "Sorts each table's rows by primary key, or first unique key, if such a key exists. Useful when dumping a MyISAM table to be loaded into an InnoDB table, but will make the dump itself take considerably longer.",
+ &opt_order_by_primary, &opt_order_by_primary, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"order-by-size", 0,
+ "Dump tables in the order of their size, smaller first. Useful when using --single-transaction on tables which get truncated often. "
+ "Dumping smaller tables first reduces chances of often truncated tables to get altered before being dumped.",
+ &opt_order_by_size, &opt_order_by_size, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"password", 'p',
+ "Password to use when connecting to server. If password is not given it's solicited on the tty.",
+ 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
+#ifdef _WIN32
+ {"pipe", 'W', "Use named pipes to connect to server.", 0, 0, 0, GET_NO_ARG,
+ NO_ARG, 0, 0, 0, 0, 0, 0},
+#endif
+ {"port", 'P', "Port number to use for connection.", &opt_mysql_port,
+ &opt_mysql_port, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0,
+ 0},
+ {"protocol", OPT_MYSQL_PROTOCOL,
+ "The protocol to use for connection (tcp, socket, pipe).",
+ 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"quick", 'q', "Don't buffer query, dump directly to stdout.",
+ &quick, &quick, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
+ {"quote-names",'Q', "Quote table and column names with backticks (`).",
+ &opt_quoted, &opt_quoted, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0,
+ 0, 0},
+ {"replace", OPT_MYSQL_REPLACE_INTO, "Use REPLACE INTO instead of INSERT INTO.",
+ &opt_replace_into, &opt_replace_into, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
+ 0, 0},
+ {"result-file", 'r',
+ "Direct output to a given file. This option should be used in systems "
+ "(e.g., DOS, Windows) that use carriage-return linefeed pairs (\\r\\n) "
+ "to separate text lines. This option ensures that only a single newline "
+ "is used.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"routines", 'R', "Dump stored routines (functions and procedures).",
+ &opt_routines, &opt_routines, 0, GET_BOOL,
+ NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"set-charset", OPT_SET_CHARSET,
+ "Add 'SET NAMES default_character_set' to the output.",
+ &opt_set_charset, &opt_set_charset, 0, GET_BOOL, NO_ARG, 1,
+ 0, 0, 0, 0, 0},
+ /*
+ Note that the combination --single-transaction --master-data
+ will give bullet-proof binlog position only if server >=4.1.3. That's the
+ old "FLUSH TABLES WITH READ LOCK does not block commit" fixed bug.
+ */
+ {"single-transaction", OPT_TRANSACTION,
+ "Creates a consistent snapshot by dumping all tables in a single "
+ "transaction. Works ONLY for tables stored in storage engines which "
+ "support multiversioning (currently only InnoDB does); the dump is NOT "
+ "guaranteed to be consistent for other storage engines. "
+ "While a --single-transaction dump is in process, to ensure a valid "
+ "dump file (correct table contents and binary log position), no other "
+ "connection should use the following statements: ALTER TABLE, DROP "
+ "TABLE, RENAME TABLE, TRUNCATE TABLE, as consistent snapshot is not "
+ "isolated from them. Option automatically turns off --lock-tables.",
+ &opt_single_transaction, &opt_single_transaction, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"skip-opt", OPT_SKIP_OPTIMIZATION,
+ "Disable --opt. Disables --add-drop-table, --add-locks, --create-options, --quick, --extended-insert, --lock-tables, --set-charset, and --disable-keys.",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"socket", 'S', "The socket file to use for connection.",
+ &opt_mysql_unix_port, &opt_mysql_unix_port, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+#include <sslopt-longopts.h>
+ {"system", 256, "Dump system tables as portable SQL",
+ &opt_system, &opt_system, &opt_system_types, GET_SET, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"tab",'T',
+ "Create tab-separated textfile for each table to given path. (Create .sql "
+ "and .txt files.) NOTE: This only works if mysqldump is run on the same "
+ "machine as the mysqld server.",
+ &path, &path, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"tables", OPT_TABLES, "Overrides option --databases (-B).",
+ 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"triggers", OPT_TRIGGERS, "Dump triggers for each dumped table.",
+ &opt_dump_triggers, &opt_dump_triggers, 0, GET_BOOL,
+ NO_ARG, 1, 0, 0, 0, 0, 0},
+ {"tz-utc", OPT_TZ_UTC,
+ "Set connection time zone to UTC before commencing the dump and add "
+ "SET TIME_ZONE=´+00:00´ to the top of the dump file.",
+ &opt_tz_utc, &opt_tz_utc, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
+#ifndef DONT_ALLOW_USER_CHANGE
+ {"user", 'u', "User for login if not current user.",
+ &current_user, &current_user, 0, GET_STR, REQUIRED_ARG,
+ 0, 0, 0, 0, 0, 0},
+#endif
+ {"verbose", 'v', "Print info about the various stages.",
+ &verbose, &verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"version",'V', "Output version information and exit.", 0, 0, 0,
+ GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"where", 'w', "Dump only selected records. Quotes are mandatory.",
+ &where, &where, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"xml", 'X', "Dump a database as well formed XML.", 0, 0, 0, GET_NO_ARG,
+ NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"plugin_dir", OPT_PLUGIN_DIR, "Directory for client-side plugins.",
+ &opt_plugin_dir, &opt_plugin_dir, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"default_auth", OPT_DEFAULT_AUTH,
+ "Default authentication client-side plugin to use.",
+ &opt_default_auth, &opt_default_auth, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+
+static const char *load_default_groups[]=
+{ "mysqldump", "mariadb-dump", "client", "client-server", "client-mariadb",
+ 0 };
+
+static void maybe_exit(int error);
+static void die(int error, const char* reason, ...);
+static void maybe_die(int error, const char* reason, ...);
+static void write_header(FILE *sql_file, const char *db_name);
+static void print_value(FILE *file, MYSQL_RES *result, MYSQL_ROW row,
+ const char *prefix,const char *name,
+ int string_value);
+static int dump_selected_tables(char *db, char **table_names, int tables);
+static int dump_all_tables_in_db(char *db);
+static int init_dumping_views(char *);
+static int init_dumping_tables(char *);
+static int init_dumping(char *, int init_func(char*));
+static int dump_databases(char **);
+static int dump_all_databases();
+static int dump_all_users_roles_and_grants();
+static int dump_all_plugins();
+static int dump_all_udfs();
+static int dump_all_servers();
+static int dump_all_stats();
+static int dump_all_timezones();
+static char *quote_name(const char *name, char *buff, my_bool force);
+char check_if_ignore_table(const char *table_name, char *table_type);
+static char *primary_key_fields(const char *table_name);
+static my_bool get_view_structure(char *table, char* db);
+static my_bool dump_all_views_in_db(char *database);
+static int dump_all_tablespaces();
+static int dump_tablespaces_for_tables(char *db, char **table_names, int tables);
+static int dump_tablespaces_for_databases(char** databases);
+static int dump_tablespaces(char* ts_where);
+static void print_comment(FILE *, my_bool, const char *, ...);
+
+/*
+ Print the supplied message if in verbose mode
+
+ SYNOPSIS
+ verbose_msg()
+ fmt format specifier
+ ... variable number of parameters
+*/
+
+static void verbose_msg(const char *fmt, ...)
+{
+ va_list args;
+ DBUG_ENTER("verbose_msg");
+
+ if (!verbose)
+ DBUG_VOID_RETURN;
+
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+
+ fflush(stderr);
+
+ DBUG_VOID_RETURN;
+}
+
+/*
+ exit with message if ferror(file)
+
+ SYNOPSIS
+ check_io()
+ file - checked file
+*/
+
+void check_io(FILE *file)
+{
+ if (ferror(file))
+ die(EX_EOF, "Got errno %d on write", errno);
+}
+
+static void print_version(void)
+{
+ printf("%s Ver %s Distrib %s, for %s (%s)\n",my_progname_short,DUMP_VERSION,
+ MYSQL_SERVER_VERSION,SYSTEM_TYPE,MACHINE_TYPE);
+} /* print_version */
+
+
+static void short_usage_sub(FILE *f)
+{
+ fprintf(f, "Usage: %s [OPTIONS] database [tables]\n", my_progname_short);
+ fprintf(f, "OR %s [OPTIONS] --databases DB1 [DB2 DB3...]\n",
+ my_progname_short);
+ fprintf(f, "OR %s [OPTIONS] --all-databases\n", my_progname_short);
+ fprintf(f, "OR %s [OPTIONS] --system=[SYSTEMOPTIONS]]\n", my_progname_short);
+}
+
+
+static void usage(void)
+{
+ print_version();
+ puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000"));
+ puts("Dumping structure and contents of MariaDB databases and tables.");
+ short_usage_sub(stdout);
+ print_defaults("my",load_default_groups);
+ puts("");
+ my_print_help(my_long_options);
+ my_print_variables(my_long_options);
+} /* usage */
+
+
+static void short_usage(FILE *f)
+{
+ short_usage_sub(f);
+ fprintf(f, "For more options, use %s --help\n", my_progname_short);
+}
+
+
+/** returns a string fixed to be safely printed inside a -- comment
+
+ that is, any new line in it gets prefixed with --
+*/
+static const char *fix_for_comment(const char *ident)
+{
+ static char buf[1024];
+ char c, *s= buf;
+
+ while ((c= *s++= *ident++))
+ {
+ if (s >= buf + sizeof(buf) - 10)
+ {
+ strmov(s, "...");
+ break;
+ }
+ if (c == '\n')
+ s= strmov(s, "-- ");
+ }
+
+ return buf;
+}
+
+
+static void write_header(FILE *sql_file, const char *db_name)
+{
+ if (opt_xml)
+ {
+ fputs("<?xml version=\"1.0\"?>\n", sql_file);
+ /*
+ Schema reference. Allows use of xsi:nil for NULL values and
+ xsi:type to define an element's data type.
+ */
+ fputs("<mysqldump ", sql_file);
+ fputs("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"",
+ sql_file);
+ fputs(">\n", sql_file);
+ check_io(sql_file);
+ }
+ else if (!opt_compact)
+ {
+ print_comment(sql_file, 0,
+ "-- MariaDB dump %s Distrib %s, for %s (%s)\n--\n",
+ DUMP_VERSION, MYSQL_SERVER_VERSION, SYSTEM_TYPE,
+ MACHINE_TYPE);
+ print_comment(sql_file, 0, "-- Host: %s ",
+ fix_for_comment(current_host ? current_host : "localhost"));
+ print_comment(sql_file, 0, "Database: %s\n",
+ fix_for_comment(db_name ? db_name : ""));
+ print_comment(sql_file, 0,
+ "-- ------------------------------------------------------\n"
+ );
+ print_comment(sql_file, 0, "-- Server version\t%s\n",
+ mysql_get_server_info(&mysql_connection));
+
+ if (!opt_logging)
+ fprintf(sql_file,
+"\n/*M!100101 SET LOCAL SQL_LOG_OFF=0, LOCAL LOG_SLOW_QUERY=0 */;");
+
+ if (opt_set_charset)
+ fprintf(sql_file,
+"\n/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;"
+"\n/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;"
+"\n/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;"
+"\n/*!40101 SET NAMES %s */;\n",default_charset);
+
+ if (opt_tz_utc)
+ {
+ fprintf(sql_file, "/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;\n");
+ fprintf(sql_file, "/*!40103 SET TIME_ZONE='+00:00' */;\n");
+ }
+
+ if (!path)
+ {
+ if (!opt_no_create_info)
+ {
+ /* We don't need unique checks as the table is created just before */
+ fprintf(md_result_file,"\
+/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;\n");
+ }
+ fprintf(md_result_file,"\
+/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;\n\
+");
+ }
+ fprintf(sql_file,
+ "/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='%s%s%s' */;\n"
+ "/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;\n",
+ path?"":"NO_AUTO_VALUE_ON_ZERO",compatible_mode_normal_str[0]==0?"":",",
+ compatible_mode_normal_str);
+ check_io(sql_file);
+ }
+} /* write_header */
+
+
+static void write_footer(FILE *sql_file)
+{
+ if (opt_xml)
+ {
+ fputs("</mysqldump>\n", sql_file);
+ check_io(sql_file);
+ }
+ else if (!opt_compact)
+ {
+ if (opt_tz_utc)
+ fprintf(sql_file,"/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;\n");
+
+ fprintf(sql_file,"\n/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;\n");
+ if (!path)
+ {
+ fprintf(md_result_file,"\
+/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;\n");
+ if (!opt_no_create_info)
+ {
+ fprintf(md_result_file,"\
+/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;\n");
+ }
+ }
+ if (opt_set_charset)
+ fprintf(sql_file,
+"/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;\n"
+"/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;\n"
+"/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;\n");
+ fprintf(sql_file,
+ "/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;\n");
+ fputs("\n", sql_file);
+
+ if (opt_dump_date)
+ {
+ char time_str[20];
+ get_date(time_str, GETDATE_DATE_TIME, 0);
+ print_comment(sql_file, 0, "-- Dump completed on %s\n", time_str);
+ }
+ else
+ print_comment(sql_file, 0, "-- Dump completed\n");
+
+ check_io(sql_file);
+ }
+} /* write_footer */
+
+
+uchar* get_table_key(const char *entry, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length= strlen(entry);
+ return (uchar*) entry;
+}
+
+
+static my_bool
+get_one_option(const struct my_option *opt,
+ const char *argument,
+ const char *filename)
+{
+
+ switch (opt->id) {
+ case 'p':
+ if (argument == disabled_my_option)
+ argument= (char*) ""; /* Don't require password */
+ if (argument)
+ {
+ /*
+ One should not really change the argument, but we make an
+ exception for passwords
+ */
+ char *start= (char*) argument;
+ my_free(opt_password);
+ opt_password= my_strdup(PSI_NOT_INSTRUMENTED, argument, MYF(MY_FAE));
+ while (*argument)
+ *(char*) argument++= 'x'; /* Destroy argument */
+ if (*start)
+ start[1]=0; /* Cut length of argument */
+ tty_password= 0;
+ }
+ else
+ tty_password=1;
+ break;
+ case 'r':
+ if (!(md_result_file= my_fopen(argument, O_WRONLY | FILE_BINARY,
+ MYF(MY_WME))))
+ exit(1);
+ break;
+ case 'W':
+#ifdef _WIN32
+ opt_protocol= MYSQL_PROTOCOL_PIPE;
+#endif
+ break;
+ case 'N':
+ opt_set_charset= 0;
+ break;
+ case 'T':
+ opt_disable_keys=0;
+
+ if (strlen(argument) >= FN_REFLEN)
+ {
+ /*
+ This check is made because the some the file functions below
+ have FN_REFLEN sized stack allocated buffers and will cause
+ a crash even if the input destination buffer is large enough
+ to hold the output.
+ */
+ die(EX_USAGE, "Input filename too long: %s", argument);
+ }
+
+ break;
+ case '#':
+ DBUG_PUSH(argument ? argument : default_dbug_option);
+ debug_check_flag= 1;
+ break;
+#include <sslopt-case.h>
+ case 'V': print_version(); exit(0);
+ case 'X':
+ opt_xml= 1;
+ extended_insert= opt_drop= opt_lock=
+ opt_disable_keys= opt_autocommit= opt_create_db= 0;
+ break;
+ case 'i':
+ opt_comments_used= 1;
+ break;
+ case 'I':
+ case '?':
+ usage();
+ exit(0);
+ case (int) OPT_MASTER_DATA:
+ if (!argument) /* work like in old versions */
+ opt_master_data= MYSQL_OPT_MASTER_DATA_EFFECTIVE_SQL;
+ break;
+ case (int) OPT_MYSQLDUMP_SLAVE_DATA:
+ if (!argument) /* work like in old versions */
+ opt_slave_data= MYSQL_OPT_SLAVE_DATA_EFFECTIVE_SQL;
+ break;
+ case (int) OPT_OPTIMIZE:
+ extended_insert= opt_drop= opt_lock= quick= create_options=
+ opt_disable_keys= lock_tables= opt_set_charset= 1;
+ break;
+ case (int) OPT_SKIP_OPTIMIZATION:
+ extended_insert= opt_drop= opt_lock= quick= create_options=
+ opt_disable_keys= lock_tables= opt_set_charset= 0;
+ break;
+ case (int) OPT_COMPACT:
+ if (opt_compact)
+ {
+ opt_comments= opt_drop= opt_disable_keys= opt_lock= 0;
+ opt_set_charset= 0;
+ }
+ break;
+ case (int) OPT_TABLES:
+ opt_databases=0;
+ break;
+ case (int) OPT_IGNORE_DATABASE:
+ if (my_hash_insert(&ignore_database,
+ (uchar*) my_strdup(PSI_NOT_INSTRUMENTED, argument, MYF(0))))
+ exit(EX_EOM);
+ break;
+ case (int) OPT_IGNORE_DATA:
+ {
+ if (!strchr(argument, '.'))
+ {
+ fprintf(stderr,
+ "Illegal use of option --ignore-table-data=<database>.<table>\n");
+ exit(1);
+ }
+ if (my_hash_insert(&ignore_data, (uchar*)my_strdup(PSI_NOT_INSTRUMENTED,
+ argument, MYF(0))))
+ exit(EX_EOM);
+ break;
+ }
+ case (int) OPT_IGNORE_TABLE:
+ {
+ if (!strchr(argument, '.'))
+ {
+ fprintf(stderr, "Illegal use of option --ignore-table=<database>.<table>\n");
+ exit(1);
+ }
+ if (my_hash_insert(&ignore_table,
+ (uchar*)my_strdup(PSI_NOT_INSTRUMENTED, argument, MYF(0))))
+ exit(EX_EOM);
+ break;
+ }
+ case (int) OPT_COMPATIBLE:
+ {
+ char buff[255];
+ char *end= compatible_mode_normal_str;
+ int i;
+ ulong mode;
+ uint err_len;
+
+ opt_quoted= 1;
+ opt_set_charset= 0;
+ opt_compatible_mode_str= argument;
+ opt_compatible_mode= find_set(&compatible_mode_typelib,
+ argument, strlen(argument),
+ &err_ptr, &err_len);
+ if (err_len)
+ {
+ strmake(buff, err_ptr, MY_MIN(sizeof(buff) - 1, err_len));
+ fprintf(stderr, "Invalid mode to --compatible: %s\n", buff);
+ exit(1);
+ }
+#if !defined(DBUG_OFF)
+ {
+ size_t size_for_sql_mode= 0;
+ const char **ptr;
+ for (ptr= compatible_mode_names; *ptr; ptr++)
+ size_for_sql_mode+= strlen(*ptr);
+ size_for_sql_mode+= sizeof(compatible_mode_names)-1;
+ DBUG_ASSERT(sizeof(compatible_mode_normal_str)>=size_for_sql_mode);
+ }
+#endif
+ mode= opt_compatible_mode;
+ for (i= 0, mode= opt_compatible_mode; mode; mode>>= 1, i++)
+ {
+ if (mode & 1)
+ {
+ end= strmov(end, compatible_mode_names[i]);
+ end= strmov(end, ",");
+ }
+ }
+ if (end!=compatible_mode_normal_str)
+ end[-1]= 0;
+ /*
+ Set charset to the default compiled value if it hasn't
+ been reset yet by --default-character-set=xxx.
+ */
+ if (default_charset == mysql_universal_client_charset)
+ default_charset= (char*) MYSQL_DEFAULT_CHARSET_NAME;
+ break;
+ }
+ case (int) OPT_MYSQL_PROTOCOL:
+ if ((opt_protocol= find_type_with_warning(argument, &sql_protocol_typelib,
+ opt->name)) <= 0)
+ {
+ sf_leaking_memory= 1; /* no memory leak reports here */
+ exit(1);
+ }
+ break;
+ case (int) OPT_DEFAULT_CHARSET:
+ if (default_charset == disabled_my_option)
+ default_charset= (char *)mysql_universal_client_charset;
+ break;
+ case 'P':
+ if (filename[0] == '\0')
+ {
+ /* Port given on command line, switch protocol to use TCP */
+ opt_protocol= MYSQL_PROTOCOL_TCP;
+ }
+ break;
+ case 'S':
+ if (filename[0] == '\0')
+ {
+ /*
+ Socket given on command line, switch protocol to use SOCKETSt
+ Except on Windows if 'protocol= pipe' has been provided in
+ the config file or command line.
+ */
+ if (opt_protocol != MYSQL_PROTOCOL_PIPE)
+ {
+ opt_protocol= MYSQL_PROTOCOL_SOCKET;
+ }
+ }
+ break;
+ }
+ return 0;
+}
+
+static int get_options(int *argc, char ***argv)
+{
+ int ho_error;
+ MYSQL_PARAMETERS *mysql_params= mysql_get_parameters();
+
+ opt_max_allowed_packet= *mysql_params->p_max_allowed_packet;
+ opt_net_buffer_length= *mysql_params->p_net_buffer_length;
+
+ /* We need to know if protocol-related options originate from CLI args */
+ my_defaults_mark_files = TRUE;
+
+ md_result_file= stdout;
+ load_defaults_or_exit("my", load_default_groups, argc, argv);
+ defaults_argv= *argv;
+
+ if (my_hash_init(PSI_NOT_INSTRUMENTED, &ignore_database, charset_info, 16, 0, 0,
+ (my_hash_get_key) get_table_key, my_free, 0))
+ return(EX_EOM);
+ if (my_hash_init(PSI_NOT_INSTRUMENTED, &ignore_table, charset_info, 16, 0, 0,
+ (my_hash_get_key) get_table_key, my_free, 0))
+ return(EX_EOM);
+ /* Don't copy internal log tables */
+ if (my_hash_insert(&ignore_table, (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.apply_status", MYF(MY_WME))) ||
+ my_hash_insert(&ignore_table, (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.schema", MYF(MY_WME))) ||
+ my_hash_insert(&ignore_table, (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.general_log", MYF(MY_WME))) ||
+ my_hash_insert(&ignore_table, (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.slow_log", MYF(MY_WME))) ||
+ my_hash_insert(&ignore_table, (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.transaction_registry", MYF(MY_WME))))
+ return(EX_EOM);
+
+ if (my_hash_init(PSI_NOT_INSTRUMENTED, &ignore_data, charset_info, 16, 0, 0,
+ (my_hash_get_key) get_table_key, my_free, 0))
+ return(EX_EOM);
+
+ if ((ho_error= handle_options(argc, argv, my_long_options, get_one_option)))
+ return(ho_error);
+
+ /*
+ Dumping under --system=stats with --replace or --insert-ignore is
+ safe and will not result into race condition. Otherwise dump only
+ structure and ignore data by default while dumping.
+ */
+ if (!(opt_system & OPT_SYSTEM_STATS) && !(opt_ignore || opt_replace_into))
+ {
+ if (my_hash_insert(&ignore_data,
+ (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.innodb_index_stats", MYF(MY_WME))) ||
+ my_hash_insert(&ignore_data,
+ (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.innodb_table_stats", MYF(MY_WME))))
+ return(EX_EOM);
+ }
+
+ if (opt_system & OPT_SYSTEM_ALL)
+ opt_system|= ~0;
+
+ if (opt_system & OPT_SYSTEM_USERS &&
+ (my_hash_insert(&ignore_table,
+ (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.db", MYF(MY_WME))) ||
+ my_hash_insert(&ignore_table,
+ (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.global_priv", MYF(MY_WME))) ||
+ my_hash_insert(&ignore_table,
+ (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.tables_priv", MYF(MY_WME))) ||
+ my_hash_insert(&ignore_table,
+ (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.columns_priv", MYF(MY_WME))) ||
+ my_hash_insert(&ignore_table,
+ (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.procs_priv", MYF(MY_WME))) ||
+ my_hash_insert(&ignore_table,
+ (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.user", MYF(MY_WME))) ||
+ my_hash_insert(&ignore_table,
+ (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.host", MYF(MY_WME))) ||
+ my_hash_insert(&ignore_table,
+ (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.proxies_priv", MYF(MY_WME))) ||
+ my_hash_insert(&ignore_table,
+ (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.roles_mapping", MYF(MY_WME))) ||
+ /* and MySQL-8.0 role tables (role_edges and default_roles) as well */
+ my_hash_insert(&ignore_table,
+ (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.role_edges", MYF(MY_WME))) ||
+ my_hash_insert(&ignore_table,
+ (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.default_roles", MYF(MY_WME)))))
+ return(EX_EOM);
+
+ if (opt_system & OPT_SYSTEM_PLUGINS &&
+ my_hash_insert(&ignore_table,
+ (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.plugin", MYF(MY_WME))))
+ return(EX_EOM);
+
+ if (opt_system & OPT_SYSTEM_UDFS &&
+ my_hash_insert(&ignore_table,
+ (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.func", MYF(MY_WME))))
+ return(EX_EOM);
+
+ if (opt_system & OPT_SYSTEM_SERVERS &&
+ my_hash_insert(&ignore_table,
+ (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.servers", MYF(MY_WME))))
+ return(EX_EOM);
+
+ if (opt_system & OPT_SYSTEM_STATS &&
+ (my_hash_insert(&ignore_table,
+ (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.column_stats", MYF(MY_WME))) ||
+ my_hash_insert(&ignore_table,
+ (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.index_stats", MYF(MY_WME))) ||
+ my_hash_insert(&ignore_table,
+ (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.table_stats", MYF(MY_WME))) ||
+ my_hash_insert(&ignore_table,
+ (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.innodb_table_stats", MYF(MY_WME))) ||
+ my_hash_insert(&ignore_table,
+ (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.innodb_index_stats", MYF(MY_WME)))))
+ return(EX_EOM);
+
+ if (opt_system & OPT_SYSTEM_TIMEZONES &&
+ (my_hash_insert(&ignore_table,
+ (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.time_zone", MYF(MY_WME))) ||
+ my_hash_insert(&ignore_table,
+ (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.time_zone_leap_second", MYF(MY_WME))) ||
+ my_hash_insert(&ignore_table,
+ (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.time_zone_name", MYF(MY_WME))) ||
+ my_hash_insert(&ignore_table,
+ (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.time_zone_transition", MYF(MY_WME))) ||
+ my_hash_insert(&ignore_table,
+ (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
+ "mysql.time_zone_transition_type", MYF(MY_WME)))))
+ return(EX_EOM);
+
+ *mysql_params->p_max_allowed_packet= opt_max_allowed_packet;
+ *mysql_params->p_net_buffer_length= opt_net_buffer_length;
+ if (debug_info_flag)
+ my_end_arg= MY_CHECK_ERROR | MY_GIVE_INFO;
+ if (debug_check_flag)
+ my_end_arg= MY_CHECK_ERROR;
+
+ if (opt_delayed)
+ opt_lock=0; /* Can't have lock with delayed */
+ if (!path && (enclosed || opt_enclosed || escaped || lines_terminated ||
+ fields_terminated))
+ {
+ fprintf(stderr,
+ "%s: You must use option --tab with --fields-...\n", my_progname_short);
+ return(EX_USAGE);
+ }
+ if (!path && opt_header)
+ {
+ fprintf(stderr,
+ "%s: You must use option --tab with --header\n", my_progname_short);
+ return(EX_USAGE);
+ }
+
+ /* We don't delete master logs if slave data option */
+ if (opt_slave_data)
+ {
+ opt_lock_all_tables= !opt_single_transaction;
+ opt_master_data= 0;
+ opt_delete_master_logs= 0;
+ }
+
+ /* Ensure consistency of the set of binlog & locking options */
+ if (opt_delete_master_logs && !opt_master_data)
+ opt_master_data= MYSQL_OPT_MASTER_DATA_COMMENTED_SQL;
+ if (opt_single_transaction && opt_lock_all_tables)
+ {
+ fprintf(stderr, "%s: You can't use --single-transaction and "
+ "--lock-all-tables at the same time.\n", my_progname_short);
+ return(EX_USAGE);
+ }
+ if (opt_master_data)
+ {
+ opt_lock_all_tables= !opt_single_transaction;
+ opt_slave_data= 0;
+ }
+ if (opt_single_transaction || opt_lock_all_tables)
+ lock_tables= 0;
+ if (enclosed && opt_enclosed)
+ {
+ fprintf(stderr, "%s: You can't use ..enclosed.. and ..optionally-enclosed.. at the same time.\n", my_progname_short);
+ return(EX_USAGE);
+ }
+ if ((opt_databases || opt_alldbs) && path)
+ {
+ fprintf(stderr,
+ "%s: --databases or --all-databases can't be used with --tab.\n",
+ my_progname_short);
+ return(EX_USAGE);
+ }
+ if (ignore_database.records && !opt_alldbs)
+ {
+ fprintf(stderr,
+ "%s: --ignore-database can only be used together with --all-databases.\n",
+ my_progname_short);
+ return(EX_USAGE);
+ }
+ if (opt_xml && path)
+ {
+ fprintf(stderr, "%s: --xml can't be used with --tab.\n", my_progname_short);
+ return(EX_USAGE);
+ }
+ if (opt_xml && opt_dump_history)
+ {
+ fprintf(stderr, "%s: --xml can't be used with --dump-history.\n",
+ my_progname_short);
+ return(EX_USAGE);
+ }
+ if (opt_replace_into && opt_dump_history)
+ {
+ fprintf(stderr, "%s: --dump-history can't be used with --replace.\n",
+ my_progname_short);
+ return(EX_USAGE);
+ }
+ if (opt_asof_timestamp && opt_dump_history)
+ {
+ fprintf(stderr, "%s: --dump-history can't be used with --as-of.\n",
+ my_progname_short);
+ return(EX_USAGE);
+ }
+ if (opt_asof_timestamp && strchr(opt_asof_timestamp, '\''))
+ {
+ fprintf(stderr, "%s: Incorrect DATETIME value: '%s'\n",
+ my_progname_short, opt_asof_timestamp);
+ return(EX_USAGE);
+ }
+
+ if (strcmp(default_charset, MYSQL_AUTODETECT_CHARSET_NAME) &&
+ !(charset_info= get_charset_by_csname(default_charset, MY_CS_PRIMARY,
+ MYF(MY_UTF8_IS_UTF8MB3 | MY_WME))))
+ exit(1);
+ if (opt_order_by_size && (*argc > 1 && !opt_databases))
+ {
+ fprintf(stderr, "%s: --order-by-size can't be used when dumping selected tables\n",
+ my_progname_short);
+ return EX_USAGE;
+ }
+ if ((*argc < 1 && (!opt_alldbs && !opt_system)) || (*argc > 0 && opt_alldbs))
+ {
+ short_usage(stderr);
+ return EX_USAGE;
+ }
+ if (tty_password)
+ opt_password=my_get_tty_password(NullS);
+ return(0);
+} /* get_options */
+
+
+/*
+** DB_error -- prints mysql error message and exits the program.
+*/
+static void DB_error(MYSQL *mysql_arg, const char *when)
+{
+ DBUG_ENTER("DB_error");
+ maybe_die(EX_MYSQLERR, "Got error: %d: \"%s\" %s",
+ mysql_errno(mysql_arg), mysql_error(mysql_arg), when);
+ DBUG_VOID_RETURN;
+}
+
+
+
+/*
+ Prints out an error message and kills the process.
+
+ SYNOPSIS
+ die()
+ error_num - process return value
+ fmt_reason - a format string for use by my_vsnprintf.
+ ... - variable arguments for above fmt_reason string
+
+ DESCRIPTION
+ This call prints out the formatted error message to stderr and then
+ terminates the process.
+*/
+static void die(int error_num, const char* fmt_reason, ...)
+{
+ char buffer[1000];
+ va_list args;
+ va_start(args,fmt_reason);
+ my_vsnprintf(buffer, sizeof(buffer), fmt_reason, args);
+ va_end(args);
+
+ fprintf(stderr, "%s: %s\n", my_progname_short, buffer);
+ fflush(stderr);
+
+ ignore_errors= 0; /* force the exit */
+ maybe_exit(error_num);
+}
+
+
+/*
+ Prints out an error message and maybe kills the process.
+
+ SYNOPSIS
+ maybe_die()
+ error_num - process return value
+ fmt_reason - a format string for use by my_vsnprintf.
+ ... - variable arguments for above fmt_reason string
+
+ DESCRIPTION
+ This call prints out the formatted error message to stderr and then
+ terminates the process, unless the --force command line option is used.
+
+ This call should be used for non-fatal errors (such as database
+ errors) that the code may still be able to continue to the next unit
+ of work.
+
+*/
+static void maybe_die(int error_num, const char* fmt_reason, ...)
+{
+ char buffer[1000];
+ va_list args;
+ va_start(args,fmt_reason);
+ my_vsnprintf(buffer, sizeof(buffer), fmt_reason, args);
+ va_end(args);
+
+ fprintf(stderr, "%s: %s\n", my_progname_short, buffer);
+ fflush(stderr);
+
+ maybe_exit(error_num);
+}
+
+
+
+/*
+ Sends a query to server, optionally reads result, prints error message if
+ some.
+
+ SYNOPSIS
+ mysql_query_with_error_report()
+ mysql_con connection to use
+ res if non zero, result will be put there with
+ mysql_store_result()
+ query query to send to server
+
+ RETURN VALUES
+ 0 query sending and (if res!=0) result reading went ok
+ 1 error
+*/
+
+static int mysql_query_with_error_report(MYSQL *mysql_con, MYSQL_RES **res,
+ const char *query)
+{
+ if (mysql_query(mysql_con, query) ||
+ (res && !((*res)= mysql_store_result(mysql_con))))
+ {
+ maybe_die(EX_MYSQLERR, "Couldn't execute '%s': %s (%d)",
+ query, mysql_error(mysql_con), mysql_errno(mysql_con));
+ return 1;
+ }
+ return 0;
+}
+
+
+static int fetch_db_collation(const char *db_name,
+ char *db_cl_name,
+ int db_cl_size)
+{
+ my_bool err_status= FALSE;
+ MYSQL_RES *db_cl_res;
+ MYSQL_ROW db_cl_row;
+ if (mysql_select_db(mysql, db_name))
+ {
+ DB_error(mysql, "when selecting the database");
+ return 1; /* If --force */
+ }
+
+ if (mysql_query_with_error_report(mysql, &db_cl_res,
+ "select @@collation_database"))
+ return 1;
+
+ do
+ {
+ if (mysql_num_rows(db_cl_res) != 1)
+ {
+ err_status= TRUE;
+ break;
+ }
+
+ if (!(db_cl_row= mysql_fetch_row(db_cl_res)))
+ {
+ err_status= TRUE;
+ break;
+ }
+
+ strncpy(db_cl_name, db_cl_row[0], db_cl_size-1);
+ db_cl_name[db_cl_size - 1]= 0;
+
+ } while (FALSE);
+
+ mysql_free_result(db_cl_res);
+
+ return err_status ? 1 : 0;
+}
+
+
+/*
+ Check if server supports non-blocking binlog position using the
+ binlog_snapshot_file and binlog_snapshot_position status variables. If it
+ does, also return the position obtained if output pointers are non-NULL.
+ Returns 1 if position available, 0 if not.
+*/
+static int
+check_consistent_binlog_pos(char *binlog_pos_file, char *binlog_pos_offset)
+{
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+ int found;
+
+ if (mysql_query_with_error_report(mysql, &res,
+ "SHOW STATUS LIKE 'binlog_snapshot_%'"))
+ return 0;
+
+ found= 0;
+ while ((row= mysql_fetch_row(res)))
+ {
+ if (0 == strcmp(row[0], "Binlog_snapshot_file"))
+ {
+ if (binlog_pos_file)
+ strmake(binlog_pos_file, row[1], FN_REFLEN-1);
+ found++;
+ }
+ else if (0 == strcmp(row[0], "Binlog_snapshot_position"))
+ {
+ if (binlog_pos_offset)
+ strmake(binlog_pos_offset, row[1], LONGLONG_LEN);
+ found++;
+ }
+ }
+ mysql_free_result(res);
+
+ 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, (ulong)len_pos_file);
+ mysql_real_escape_string(mysql, offset_buf, binlog_pos_offset, (ulong)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,
+ uint token_len)
+{
+ my_match_t match;
+
+ uint status= my_ci_instr(&my_charset_latin1,
+ str, str_len,
+ token, token_len,
+ &match, 1);
+
+ return status ? (char *) str + match.end : NULL;
+}
+
+
+static int switch_db_collation(FILE *sql_file,
+ const char *db_name,
+ const char *delimiter,
+ const char *current_db_cl_name,
+ const char *required_db_cl_name,
+ int *db_cl_altered)
+{
+ if (strcmp(current_db_cl_name, required_db_cl_name) != 0)
+ {
+ char quoted_db_buf[NAME_LEN * 2 + 3];
+ char *quoted_db_name= quote_name(db_name, quoted_db_buf, FALSE);
+
+ CHARSET_INFO *db_cl= get_charset_by_name(required_db_cl_name, MYF(MY_UTF8_IS_UTF8MB3));
+
+ if (!db_cl)
+ return 1;
+
+ fprintf(sql_file,
+ "ALTER DATABASE %s CHARACTER SET %s COLLATE %s %s\n",
+ (const char *) quoted_db_name,
+ (const char *) db_cl->cs_name.str,
+ (const char *) db_cl->coll_name.str,
+ (const char *) delimiter);
+
+ *db_cl_altered= 1;
+
+ return 0;
+ }
+
+ *db_cl_altered= 0;
+
+ return 0;
+}
+
+
+static int restore_db_collation(FILE *sql_file,
+ const char *db_name,
+ const char *delimiter,
+ const char *db_cl_name)
+{
+ char quoted_db_buf[NAME_LEN * 2 + 3];
+ char *quoted_db_name= quote_name(db_name, quoted_db_buf, FALSE);
+
+ CHARSET_INFO *db_cl= get_charset_by_name(db_cl_name, MYF(MY_UTF8_IS_UTF8MB3));
+
+ if (!db_cl)
+ return 1;
+
+ fprintf(sql_file,
+ "ALTER DATABASE %s CHARACTER SET %s COLLATE %s %s\n",
+ (const char *) quoted_db_name,
+ (const char *) db_cl->cs_name.str,
+ (const char *) db_cl->coll_name.str,
+ (const char *) delimiter);
+
+ return 0;
+}
+
+
+static void switch_cs_variables(FILE *sql_file,
+ const char *delimiter,
+ const char *character_set_client,
+ const char *character_set_results,
+ const char *collation_connection)
+{
+ fprintf(sql_file,
+ "/*!50003 SET @saved_cs_client = @@character_set_client */ %s\n"
+ "/*!50003 SET @saved_cs_results = @@character_set_results */ %s\n"
+ "/*!50003 SET @saved_col_connection = @@collation_connection */ %s\n"
+ "/*!50003 SET character_set_client = %s */ %s\n"
+ "/*!50003 SET character_set_results = %s */ %s\n"
+ "/*!50003 SET collation_connection = %s */ %s\n",
+ (const char *) delimiter,
+ (const char *) delimiter,
+ (const char *) delimiter,
+
+ (const char *) character_set_client,
+ (const char *) delimiter,
+
+ (const char *) character_set_results,
+ (const char *) delimiter,
+
+ (const char *) collation_connection,
+ (const char *) delimiter);
+}
+
+
+static void restore_cs_variables(FILE *sql_file,
+ const char *delimiter)
+{
+ fprintf(sql_file,
+ "/*!50003 SET character_set_client = @saved_cs_client */ %s\n"
+ "/*!50003 SET character_set_results = @saved_cs_results */ %s\n"
+ "/*!50003 SET collation_connection = @saved_col_connection */ %s\n",
+ (const char *) delimiter,
+ (const char *) delimiter,
+ (const char *) delimiter);
+}
+
+
+static void switch_sql_mode(FILE *sql_file,
+ const char *delimiter,
+ const char *sql_mode)
+{
+ fprintf(sql_file,
+ "/*!50003 SET @saved_sql_mode = @@sql_mode */ %s\n"
+ "/*!50003 SET sql_mode = '%s' */ %s\n",
+ (const char *) delimiter,
+
+ (const char *) sql_mode,
+ (const char *) delimiter);
+}
+
+
+static void restore_sql_mode(FILE *sql_file,
+ const char *delimiter)
+{
+ fprintf(sql_file,
+ "/*!50003 SET sql_mode = @saved_sql_mode */ %s\n",
+ (const char *) delimiter);
+}
+
+
+static void switch_time_zone(FILE *sql_file,
+ const char *delimiter,
+ const char *time_zone)
+{
+ fprintf(sql_file,
+ "/*!50003 SET @saved_time_zone = @@time_zone */ %s\n"
+ "/*!50003 SET time_zone = '%s' */ %s\n",
+ (const char *) delimiter,
+
+ (const char *) time_zone,
+ (const char *) delimiter);
+}
+
+
+static void restore_time_zone(FILE *sql_file,
+ const char *delimiter)
+{
+ fprintf(sql_file,
+ "/*!50003 SET time_zone = @saved_time_zone */ %s\n",
+ (const char *) delimiter);
+}
+
+
+/**
+ Switch charset for results to some specified charset. If the server does not
+ support character_set_results variable, nothing can be done here. As for
+ whether something should be done here, future new callers of this function
+ should be aware that the server lacking the facility of switching charsets is
+ treated as success.
+
+ @note If the server lacks support, then nothing is changed and no error
+ condition is returned.
+
+ @returns whether there was an error or not
+*/
+static int switch_character_set_results(MYSQL *mysql, const char *cs_name)
+{
+ char query_buffer[QUERY_LENGTH];
+ size_t query_length;
+
+ if (!strcmp(cs_name, MYSQL_AUTODETECT_CHARSET_NAME))
+ cs_name= (char *)my_default_csname();
+
+ /* Server lacks facility. This is not an error, by arbitrary decision . */
+ if (!server_supports_switching_charsets)
+ return FALSE;
+
+ query_length= my_snprintf(query_buffer,
+ sizeof (query_buffer),
+ "SET SESSION character_set_results = '%s'",
+ (const char *) cs_name);
+
+ return mysql_real_query(mysql, query_buffer, (ulong)query_length);
+}
+
+/**
+ Rewrite statement, enclosing DEFINER clause in version-specific comment.
+
+ This function parses any CREATE statement and encloses DEFINER-clause in
+ version-specific comment:
+ input query: CREATE DEFINER=a@b FUNCTION ...
+ rewritten query: CREATE * / / *!50020 DEFINER=a@b * / / *!50003 FUNCTION ...
+
+ @note This function will go away when WL#3995 is implemented.
+
+ @param[in] stmt_str CREATE statement string.
+ @param[in] stmt_length Length of the stmt_str.
+ @param[in] definer_version_str Minimal MySQL version number when
+ DEFINER clause is supported in the
+ given statement.
+ @param[in] definer_version_length Length of definer_version_str.
+ @param[in] stmt_version_str Minimal MySQL version number when the
+ given statement is supported.
+ @param[in] stmt_version_length Length of stmt_version_str.
+ @param[in] keyword_str Keyword to look for after CREATE.
+ @param[in] keyword_length Length of keyword_str.
+
+ @return pointer to the new allocated query string.
+*/
+
+static char *cover_definer_clause(const char *stmt_str,
+ size_t stmt_length,
+ const char *definer_version_str,
+ uint definer_version_length,
+ const char *stmt_version_str,
+ uint stmt_version_length,
+ const char *keyword_str,
+ uint keyword_length)
+{
+ char *definer_begin= my_case_str(stmt_str, stmt_length,
+ C_STRING_WITH_LEN(" DEFINER"));
+ char *definer_end= NULL;
+
+ char *query_str= NULL;
+ char *query_ptr;
+
+ if (!definer_begin)
+ return NULL;
+
+ definer_end= my_case_str(definer_begin, strlen(definer_begin),
+ keyword_str, keyword_length);
+
+ if (!definer_end)
+ return NULL;
+
+ /*
+ Allocate memory for new query string: original string
+ from SHOW statement and version-specific comments.
+ */
+ query_str= alloc_query_str(stmt_length + 23);
+
+ query_ptr= strnmov(query_str, stmt_str, definer_begin - stmt_str);
+ query_ptr= strnmov(query_ptr, C_STRING_WITH_LEN("*/ /*!"));
+ query_ptr= strnmov(query_ptr, definer_version_str, definer_version_length);
+ query_ptr= strnmov(query_ptr, definer_begin, definer_end - definer_begin);
+ query_ptr= strnmov(query_ptr, C_STRING_WITH_LEN("*/ /*!"));
+ query_ptr= strnmov(query_ptr, stmt_version_str, stmt_version_length);
+ query_ptr= strxmov(query_ptr, definer_end, NullS);
+
+ return query_str;
+}
+
+/*
+ Open a new .sql file to dump the table or view into
+
+ SYNOPSIS
+ open_sql_file_for_table
+ name name of the table or view
+ flags flags (as per "man 2 open")
+
+ RETURN VALUES
+ 0 Failed to open file
+ > 0 Handle of the open file
+*/
+static FILE* open_sql_file_for_table(const char* table, int flags)
+{
+ FILE* res;
+ char filename[FN_REFLEN], tmp_path[FN_REFLEN];
+ convert_dirname(tmp_path,path,NullS);
+ res= my_fopen(fn_format(filename, table, tmp_path, ".sql", 4),
+ flags, MYF(MY_WME));
+ return res;
+}
+
+
+static void free_resources()
+{
+ if (md_result_file)
+ {
+ if (md_result_file != stdout)
+ my_fclose(md_result_file, MYF(0));
+ else
+ fflush(md_result_file);
+ }
+ 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_database))
+ my_hash_free(&ignore_database);
+ if (my_hash_inited(&ignore_table))
+ my_hash_free(&ignore_table);
+ if (my_hash_inited(&ignore_data))
+ my_hash_free(&ignore_data);
+ dynstr_free(&extended_row);
+ dynstr_free(&dynamic_where);
+ dynstr_free(&insert_pat);
+ dynstr_free(&select_field_names);
+ dynstr_free(&select_field_names_for_header);
+ if (defaults_argv)
+ free_defaults(defaults_argv);
+ mysql_library_end();
+ my_end(my_end_arg);
+}
+
+
+static void maybe_exit(int error)
+{
+ if (!first_error)
+ first_error= error;
+ if (ignore_errors)
+ return;
+ ignore_errors= 1; /* don't want to recurse, if something fails below */
+ if (opt_slave_data)
+ do_start_slave_sql(mysql);
+ free_resources();
+ exit(error);
+}
+
+
+/*
+ db_connect -- connects to the host and selects DB.
+*/
+
+static int connect_to_db(char *host, char *user,char *passwd)
+{
+ char buff[20+FN_REFLEN];
+ my_bool reconnect;
+ DBUG_ENTER("connect_to_db");
+
+ verbose_msg("-- Connecting to %s...\n", host ? host : "localhost");
+ mysql_init(&mysql_connection);
+ if (opt_compress)
+ 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, MARIADB_OPT_TLS_VERSION, opt_tls_version);
+ }
+ mysql_options(&mysql_connection,MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
+ (char*)&opt_ssl_verify_server_cert);
+#endif
+ if (opt_protocol)
+ mysql_options(&mysql_connection,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol);
+ mysql_options(&mysql_connection, MYSQL_SET_CHARSET_NAME, default_charset);
+
+ if (opt_plugin_dir && *opt_plugin_dir)
+ mysql_options(&mysql_connection, MYSQL_PLUGIN_DIR, opt_plugin_dir);
+
+ 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))
+ {
+ DB_error(&mysql_connection, "when trying to connect");
+ DBUG_RETURN(1);
+ }
+ if ((mysql_get_server_version(&mysql_connection) < 40100) ||
+ (opt_compatible_mode & 3))
+ {
+ /* Don't dump SET NAMES with a pre-4.1 server (bug#7997). */
+ opt_set_charset= 0;
+
+ /* Don't switch charsets for 4.1 and earlier. (bug#34192). */
+ server_supports_switching_charsets= FALSE;
+ }
+ /*
+ As we're going to set SQL_MODE, it would be lost on reconnect, so we
+ cannot reconnect.
+ */
+ reconnect= 0;
+ mysql_options(&mysql_connection, MYSQL_OPT_RECONNECT, &reconnect);
+ my_snprintf(buff, sizeof(buff), "/*!40100 SET @@SQL_MODE='%s' */",
+ compatible_mode_normal_str);
+ if (mysql_query_with_error_report(mysql, 0, buff))
+ DBUG_RETURN(1);
+ /*
+ set time_zone to UTC to allow dumping date types between servers with
+ different time zone settings
+ */
+ if (opt_tz_utc)
+ {
+ my_snprintf(buff, sizeof(buff), "/*!40103 SET TIME_ZONE='+00:00' */");
+ if (mysql_query_with_error_report(mysql, 0, buff))
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(0);
+} /* connect_to_db */
+
+
+/*
+** dbDisconnect -- disconnects from the host.
+*/
+static void dbDisconnect(char *host)
+{
+ verbose_msg("-- Disconnecting from %s...\n", host ? host : "localhost");
+ mysql_close(mysql);
+ mysql= 0;
+} /* dbDisconnect */
+
+
+static void unescape(FILE *file,char *pos, size_t length)
+{
+ char *tmp;
+ DBUG_ENTER("unescape");
+ if (!(tmp=(char*) my_malloc(PSI_NOT_INSTRUMENTED, length*2+1, MYF(MY_WME))))
+ die(EX_MYSQLERR, "Couldn't allocate memory");
+
+ mysql_real_escape_string(&mysql_connection, tmp, pos, (ulong)length);
+ fputc('\'', file);
+ fputs(tmp, file);
+ fputc('\'', file);
+ check_io(file);
+ my_free(tmp);
+ DBUG_VOID_RETURN;
+} /* unescape */
+
+
+static my_bool test_if_special_chars(const char *str)
+{
+#if MYSQL_VERSION_ID >= 32300
+ for ( ; *str ; str++)
+ if (!my_isvar(charset_info,*str) && *str != '$')
+ return 1;
+#endif
+ return 0;
+} /* test_if_special_chars */
+
+
+/*
+ quote(name, buff, force, quote_c)
+
+ Quotes a string, if it requires quoting. To force quoting regardless
+ of the characters within the string, the force flag can be set to true.
+
+ Args
+
+ name Unquoted string containing that which will be quoted
+ buff The buffer that contains the quoted value, also returned
+ force Flag to make it ignore 'test_if_special_chars'
+
+ Returns
+ A pointer to the quoted string, or the original string if nothing has
+ changed.
+
+*/
+static char *quote_name(const char *name, char *buff, my_bool force)
+{
+ char *to= buff;
+ char qtype= (opt_compatible_mode & MASK_ANSI_QUOTES) ? '\"' : '`';
+
+ if (!force && !opt_quoted && !test_if_special_chars(name))
+ return (char*) name;
+ *to++= qtype;
+ while (*name)
+ {
+ if (*name == qtype)
+ *to++= qtype;
+ *to++= *name++;
+ }
+ to[0]= qtype;
+ to[1]= 0;
+ return buff;
+} /* quote_name */
+
+
+/*
+ Quote a table name so it can be used in "SHOW TABLES LIKE <tabname>"
+
+ SYNOPSIS
+ quote_for_like()
+ name name of the table
+ buff quoted name of the table
+
+ DESCRIPTION
+ Quote \, _, ' and % characters
+
+ Note: Because MySQL uses the C escape syntax in strings
+ (for example, '\n' to represent newline), you must double
+ any '\' that you use in your LIKE strings. For example, to
+ search for '\n', specify it as '\\n'. To search for '\', specify
+ it as '\\\\' (the backslashes are stripped once by the parser
+ and another time when the pattern match is done, leaving a
+ single backslash to be matched).
+
+ Example: "t\1" => "t\\\\1"
+
+*/
+static char *quote_for_like(const char *name, char *buff)
+{
+ char *to= buff;
+ *to++= '\'';
+ while (*name)
+ {
+ if (*name == '\\')
+ {
+ *to++='\\';
+ *to++='\\';
+ *to++='\\';
+ }
+ else if (*name == '\'' || *name == '_' || *name == '%')
+ *to++= '\\';
+ *to++= *name++;
+ }
+ to[0]= '\'';
+ to[1]= 0;
+ return buff;
+}
+
+static char *quote_for_equal(const char *name, char *buff)
+{
+ char *to= buff;
+ *to++= '\'';
+ while (*name)
+ {
+ if (*name == '\\')
+ {
+ *to++='\\';
+ }
+ if (*name == '\'')
+ *to++= '\\';
+ *to++= *name++;
+ }
+ to[0]= '\'';
+ to[1]= 0;
+ return buff;
+
+}
+
+
+/**
+ Quote and print a string.
+
+ @param xml_file - Output file.
+ @param str - String to print.
+ @param len - Its length.
+ @param is_attribute_name - A check for attribute name or value.
+
+ @description
+ Quote '<' '>' '&' '\"' chars and print a string to the xml_file.
+*/
+
+static void print_quoted_xml(FILE *xml_file, const char *str, size_t len,
+ my_bool is_attribute_name)
+{
+ const char *end;
+
+ for (end= str + len; str != end; str++)
+ {
+ switch (*str) {
+ case '<':
+ fputs("&lt;", xml_file);
+ break;
+ case '>':
+ fputs("&gt;", xml_file);
+ break;
+ case '&':
+ fputs("&amp;", xml_file);
+ break;
+ case '\"':
+ fputs("&quot;", xml_file);
+ break;
+ case ' ':
+ /* Attribute names cannot contain spaces. */
+ if (is_attribute_name)
+ {
+ fputs("_", xml_file);
+ break;
+ }
+ /* fall through */
+ default:
+ fputc(*str, xml_file);
+ break;
+ }
+ }
+ check_io(xml_file);
+}
+
+
+/*
+ Print xml tag. Optionally add attribute(s).
+
+ SYNOPSIS
+ print_xml_tag(xml_file, sbeg, send, tag_name, first_attribute_name,
+ ..., attribute_name_n, attribute_value_n, NullS)
+ xml_file - output file
+ sbeg - line beginning
+ line_end - line ending
+ tag_name - XML tag name.
+ first_attribute_name - tag and first attribute
+ first_attribute_value - (Implied) value of first attribute
+ attribute_name_n - attribute n
+ attribute_value_n - value of attribute n
+
+ DESCRIPTION
+ Print XML tag with any number of attribute="value" pairs to the xml_file.
+
+ Format is:
+ sbeg<tag_name first_attribute_name="first_attribute_value" ...
+ attribute_name_n="attribute_value_n">send
+ NOTE
+ Additional arguments must be present in attribute/value pairs.
+ The last argument should be the null character pointer.
+ All attribute_value arguments MUST be NULL terminated strings.
+ All attribute_value arguments will be quoted before output.
+*/
+
+static void print_xml_tag(FILE * xml_file, const char* sbeg,
+ const char* line_end,
+ const char* tag_name,
+ const char* first_attribute_name, ...)
+{
+ va_list arg_list;
+ const char *attribute_name, *attribute_value;
+
+ fputs(sbeg, xml_file);
+ fputc('<', xml_file);
+ fputs(tag_name, xml_file);
+
+ va_start(arg_list, first_attribute_name);
+ attribute_name= first_attribute_name;
+ while (attribute_name != NullS)
+ {
+ attribute_value= va_arg(arg_list, char *);
+ DBUG_ASSERT(attribute_value != NullS);
+
+ fputc(' ', xml_file);
+ fputs(attribute_name, xml_file);
+ fputc('\"', xml_file);
+
+ print_quoted_xml(xml_file, attribute_value, strlen(attribute_value), 0);
+ fputc('\"', xml_file);
+
+ attribute_name= va_arg(arg_list, char *);
+ }
+ va_end(arg_list);
+
+ fputc('>', xml_file);
+ fputs(line_end, xml_file);
+ check_io(xml_file);
+}
+
+
+/*
+ Print xml tag with for a field that is null
+
+ SYNOPSIS
+ print_xml_null_tag()
+ xml_file - output file
+ sbeg - line beginning
+ stag_atr - tag and attribute
+ sval - value of attribute
+ line_end - line ending
+
+ DESCRIPTION
+ Print tag with one attribute to the xml_file. Format is:
+ <stag_atr="sval" xsi:nil="true"/>
+ NOTE
+ sval MUST be a NULL terminated string.
+ sval string will be quoted before output.
+*/
+
+static void print_xml_null_tag(FILE * xml_file, const char* sbeg,
+ const char* stag_atr, const char* sval,
+ const char* line_end)
+{
+ fputs(sbeg, xml_file);
+ fputs("<", xml_file);
+ fputs(stag_atr, xml_file);
+ fputs("\"", xml_file);
+ print_quoted_xml(xml_file, sval, strlen(sval), 0);
+ fputs("\" xsi:nil=\"true\" />", xml_file);
+ fputs(line_end, xml_file);
+ check_io(xml_file);
+}
+
+
+/**
+ Print xml CDATA section.
+
+ @param xml_file - output file
+ @param str - string to print
+ @param len - length of the string
+
+ @note
+ This function also takes care of the presence of '[[>'
+ string in the str. If found, the CDATA section is broken
+ into two CDATA sections, <![CDATA[]]]]> and <![CDATA[>]].
+*/
+
+static void print_xml_cdata(FILE *xml_file, const char *str, ulong len)
+{
+ const char *end;
+
+ fputs("<![CDATA[\n", xml_file);
+ for (end= str + len; str != end; str++)
+ {
+ switch(*str) {
+ case ']':
+ if ((*(str + 1) == ']') && (*(str + 2) =='>'))
+ {
+ fputs("]]]]><![CDATA[>", xml_file);
+ str += 2;
+ continue;
+ }
+ /* fall through */
+ default:
+ fputc(*str, xml_file);
+ break;
+ }
+ }
+ fputs("\n]]>\n", xml_file);
+ check_io(xml_file);
+}
+
+
+/*
+ Print xml tag with many attributes.
+
+ SYNOPSIS
+ print_xml_row()
+ xml_file - output file
+ row_name - xml tag name
+ tableRes - query result
+ row - result row
+ str_create - create statement header string
+
+ DESCRIPTION
+ Print tag with many attribute to the xml_file. Format is:
+ \t\t<row_name Atr1="Val1" Atr2="Val2"... />
+ NOTE
+ All attributes and values will be quoted before output.
+*/
+
+static void print_xml_row(FILE *xml_file, const char *row_name,
+ MYSQL_RES *tableRes, MYSQL_ROW *row,
+ const char *str_create)
+{
+ uint i;
+ my_bool body_found __attribute__((unused)) = 0;
+ char *create_stmt_ptr= NULL;
+ ulong create_stmt_len= 0;
+ MYSQL_FIELD *field;
+ ulong *lengths= mysql_fetch_lengths(tableRes);
+
+ fprintf(xml_file, "\t\t<%s", row_name);
+ check_io(xml_file);
+ mysql_field_seek(tableRes, 0);
+ for (i= 0; (field= mysql_fetch_field(tableRes)); i++)
+ {
+ if ((*row)[i])
+ {
+ /* For 'create' statements, dump using CDATA. */
+ if ((str_create) && (strcmp(str_create, field->name) == 0))
+ {
+ create_stmt_ptr= (*row)[i];
+ create_stmt_len= lengths[i];
+#ifdef DBUG_ASSERT_EXISTS
+ body_found= 1;
+#endif
+ }
+ else
+ {
+ fputc(' ', xml_file);
+ print_quoted_xml(xml_file, field->name, field->name_length, 1);
+ fputs("=\"", xml_file);
+ if (opt_copy_s3_tables &&
+ !strcmp(field->name, "Engine") &&
+ !strcmp((*row)[i], "S3"))
+ print_quoted_xml(xml_file, "Aria", sizeof("Aria") - 1, 0);
+ else
+ print_quoted_xml(xml_file, (*row)[i], lengths[i], 0);
+ fputc('"', xml_file);
+ check_io(xml_file);
+ }
+ }
+ }
+
+ if (create_stmt_len)
+ {
+ DBUG_ASSERT(body_found);
+ fputs(">\n", xml_file);
+ print_xml_cdata(xml_file, create_stmt_ptr, create_stmt_len);
+ fprintf(xml_file, "\t\t</%s>\n", row_name);
+ }
+ else
+ fputs(" />\n", xml_file);
+
+ check_io(xml_file);
+}
+
+
+/**
+ Print xml comments.
+
+ @param xml_file - output file
+ @param len - length of comment message
+ @param comment_string - comment message
+
+ @description
+ Print the comment message in the format:
+ "<!-- \n comment string \n -->\n"
+
+ @note
+ Any occurrence of continuous hyphens will be
+ squeezed to a single hyphen.
+*/
+
+static void print_xml_comment(FILE *xml_file, size_t len,
+ const char *comment_string)
+{
+ const char* end;
+
+ fputs("<!-- ", xml_file);
+
+ for (end= comment_string + len; comment_string != end; comment_string++)
+ {
+ /*
+ The string "--" (double-hyphen) MUST NOT occur within xml comments.
+ */
+ switch (*comment_string) {
+ case '-':
+ if (*(comment_string + 1) == '-') /* Only one hyphen allowed. */
+ break;
+ /* fall through */
+ default:
+ fputc(*comment_string, xml_file);
+ break;
+ }
+ }
+ fputs(" -->\n", xml_file);
+ check_io(xml_file);
+}
+
+
+
+/* A common printing function for xml and non-xml modes. */
+
+static void print_comment(FILE *sql_file, my_bool is_error, const char *format,
+ ...)
+{
+ static char comment_buff[COMMENT_LENGTH];
+ va_list args;
+
+ /* If its an error message, print it ignoring opt_comments. */
+ if (!is_error && !opt_comments)
+ return;
+
+ va_start(args, format);
+ my_vsnprintf(comment_buff, COMMENT_LENGTH, format, args);
+ va_end(args);
+
+ if (!opt_xml)
+ {
+ fputs(comment_buff, sql_file);
+ check_io(sql_file);
+ return;
+ }
+
+ print_xml_comment(sql_file, strlen(comment_buff), comment_buff);
+}
+
+/*
+ create_delimiter
+ Generate a new (null-terminated) string that does not exist in query
+ and is therefore suitable for use as a query delimiter. Store this
+ delimiter in delimiter_buff .
+
+ This is quite simple in that it doesn't even try to parse statements as an
+ interpreter would. It merely returns a string that is not in the query, which
+ is much more than adequate for constructing a delimiter.
+
+ RETURN
+ ptr to the delimiter on Success
+ NULL on Failure
+*/
+static char *create_delimiter(char *query, char *delimiter_buff,
+ int delimiter_max_size)
+{
+ int proposed_length;
+ char *presence;
+
+ delimiter_buff[0]= ';'; /* start with one semicolon, and */
+
+ for (proposed_length= 2; proposed_length < delimiter_max_size;
+ delimiter_max_size++) {
+
+ delimiter_buff[proposed_length-1]= ';'; /* add semicolons, until */
+ delimiter_buff[proposed_length]= '\0';
+
+ presence = strstr(query, delimiter_buff);
+ if (presence == NULL) { /* the proposed delimiter is not in the query. */
+ return delimiter_buff;
+ }
+
+ }
+ return NULL; /* but if we run out of space, return nothing at all. */
+}
+
+
+/*
+ dump_events_for_db
+ -- retrieves list of events for a given db, and prints out
+ the CREATE EVENT statement into the output (the dump).
+
+ RETURN
+ 0 Success
+ 1 Error
+*/
+static uint dump_events_for_db(char *db)
+{
+ char query_buff[QUERY_LENGTH];
+ char db_name_buff[NAME_LEN*2+3], name_buff[NAME_LEN*2+3];
+ char *event_name;
+ char delimiter[QUERY_LENGTH];
+ FILE *sql_file= md_result_file;
+ MYSQL_RES *event_res, *event_list_res;
+ MYSQL_ROW row, event_list_row;
+
+ char db_cl_name[MY_CS_COLLATION_NAME_SIZE];
+ int db_cl_altered= FALSE;
+
+ DBUG_ENTER("dump_events_for_db");
+ DBUG_PRINT("enter", ("db: '%s'", db));
+
+ mysql_real_escape_string(mysql, db_name_buff, db, (ulong)strlen(db));
+
+ /* nice comments */
+ print_comment(sql_file, 0,
+ "\n--\n-- Dumping events for database '%s'\n--\n",
+ fix_for_comment(db));
+
+ /*
+ not using "mysql_query_with_error_report" because we may have not
+ enough privileges to lock mysql.events.
+ */
+ if (lock_tables)
+ mysql_query(mysql, "LOCK TABLES mysql.event READ");
+
+ if (mysql_query_with_error_report(mysql, &event_list_res, "show events"))
+ DBUG_RETURN(0);
+
+ safe_strcpy(delimiter, sizeof(delimiter), ";");
+ if (mysql_num_rows(event_list_res) > 0)
+ {
+ if (opt_xml)
+ fputs("\t<events>\n", sql_file);
+ else
+ {
+ fprintf(sql_file, "/*!50106 SET @save_time_zone= @@TIME_ZONE */ ;\n");
+
+ /* Get database collation. */
+
+ if (fetch_db_collation(db_name_buff, db_cl_name, sizeof (db_cl_name)))
+ {
+ mysql_free_result(event_list_res);
+ DBUG_RETURN(1);
+ }
+ }
+
+ if (switch_character_set_results(mysql, "binary"))
+ DBUG_RETURN(1);
+
+ while ((event_list_row= mysql_fetch_row(event_list_res)) != NULL)
+ {
+ event_name= quote_name(event_list_row[1], name_buff, 0);
+ DBUG_PRINT("info", ("retrieving CREATE EVENT for %s", name_buff));
+ my_snprintf(query_buff, sizeof(query_buff), "SHOW CREATE EVENT %s",
+ event_name);
+
+ if (mysql_query_with_error_report(mysql, &event_res, query_buff))
+ DBUG_RETURN(1);
+
+ while ((row= mysql_fetch_row(event_res)) != NULL)
+ {
+ if (opt_xml)
+ {
+ print_xml_row(sql_file, "event", event_res, &row,
+ "Create Event");
+ continue;
+ }
+
+ /*
+ if the user has EXECUTE privilege he can see event names, but not the
+ event body!
+ */
+ if (strlen(row[3]) != 0)
+ {
+ char *query_str;
+
+ if (opt_drop)
+ fprintf(sql_file, "/*!50106 DROP EVENT IF EXISTS %s */%s\n",
+ event_name, delimiter);
+
+ if (create_delimiter(row[3], delimiter, sizeof(delimiter)) == NULL)
+ {
+ fprintf(stderr, "%s: Warning: Can't create delimiter for event '%s'\n",
+ my_progname_short, event_name);
+ DBUG_RETURN(1);
+ }
+
+ fprintf(sql_file, "DELIMITER %s\n", delimiter);
+
+ if (mysql_num_fields(event_res) >= 7)
+ {
+ if (switch_db_collation(sql_file, db_name_buff, delimiter,
+ db_cl_name, row[6], &db_cl_altered))
+ {
+ DBUG_RETURN(1);
+ }
+
+ switch_cs_variables(sql_file, delimiter,
+ row[4], /* character_set_client */
+ row[4], /* character_set_results */
+ row[5]); /* collation_connection */
+ }
+ else
+ {
+ /*
+ mysqldump is being run against the server, that does not
+ provide character set information in SHOW CREATE
+ statements.
+
+ NOTE: the dump may be incorrect, since character set
+ information is required in order to restore event properly.
+ */
+
+ fprintf(sql_file,
+ "--\n"
+ "-- WARNING: old server version. "
+ "The following dump may be incomplete.\n"
+ "--\n");
+ }
+
+ switch_sql_mode(sql_file, delimiter, row[1]);
+
+ switch_time_zone(sql_file, delimiter, row[2]);
+
+ query_str= cover_definer_clause(row[3], strlen(row[3]),
+ C_STRING_WITH_LEN("50117"),
+ C_STRING_WITH_LEN("50106"),
+ C_STRING_WITH_LEN(" EVENT"));
+
+ fprintf(sql_file,
+ "/*!50106 %s */ %s\n",
+ (const char *) (query_str != NULL ? query_str : row[3]),
+ (const char *) delimiter);
+
+ my_free(query_str);
+ restore_time_zone(sql_file, delimiter);
+ restore_sql_mode(sql_file, delimiter);
+
+ if (mysql_num_fields(event_res) >= 7)
+ {
+ restore_cs_variables(sql_file, delimiter);
+
+ if (db_cl_altered)
+ {
+ if (restore_db_collation(sql_file, db_name_buff, delimiter,
+ db_cl_name))
+ DBUG_RETURN(1);
+ }
+ }
+ }
+ } /* end of event printing */
+ mysql_free_result(event_res);
+
+ } /* end of list of events */
+ if (opt_xml)
+ {
+ fputs("\t</events>\n", sql_file);
+ check_io(sql_file);
+ }
+ else
+ {
+ fprintf(sql_file, "DELIMITER ;\n");
+ fprintf(sql_file, "/*!50106 SET TIME_ZONE= @save_time_zone */ ;\n");
+ }
+
+ if (switch_character_set_results(mysql, default_charset))
+ DBUG_RETURN(1);
+ }
+ mysql_free_result(event_list_res);
+
+ if (lock_tables)
+ (void) mysql_query_with_error_report(mysql, 0, "UNLOCK TABLES");
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Print hex value for blob data.
+
+ SYNOPSIS
+ print_blob_as_hex()
+ output_file - output file
+ str - string to print
+ len - its length
+
+ DESCRIPTION
+ Print hex value for blob data.
+*/
+
+static void print_blob_as_hex(FILE *output_file, const char *str, ulong len)
+{
+ /* sakaik got the idea to to provide blob's in hex notation. */
+ const char *ptr= str, *end= ptr + len;
+ for (; ptr < end ; ptr++)
+ fprintf(output_file, "%02X", *((uchar *)ptr));
+ check_io(output_file);
+}
+
+/*
+ dump_routines_for_db
+ -- retrieves list of routines for a given db, and prints out
+ the CREATE PROCEDURE definition into the output (the dump).
+
+ This function has logic to print the appropriate syntax depending on whether
+ this is a procedure or functions
+
+ RETURN
+ 0 Success
+ 1 Error
+*/
+
+static uint dump_routines_for_db(char *db)
+{
+ char query_buff[QUERY_LENGTH];
+ const char *routine_type[]= {"FUNCTION",
+ "PROCEDURE",
+ "PACKAGE",
+ "PACKAGE BODY"};
+ const char *create_caption_xml[]= {"Create Function",
+ "Create Procedure",
+ "Create Package",
+ "Create Package Body"};
+ char db_name_buff[NAME_LEN*2+3], name_buff[NAME_LEN*2+3];
+ char *routine_name;
+ uint i;
+ FILE *sql_file= md_result_file;
+ MYSQL_ROW row, routine_list_row;
+
+ char db_cl_name[MY_CS_COLLATION_NAME_SIZE];
+ int db_cl_altered= FALSE;
+ // before 10.3 packages are not supported
+ uint upper_bound= mysql_get_server_version(mysql) >= 100300 ?
+ array_elements(routine_type) : 2;
+ DBUG_ENTER("dump_routines_for_db");
+ DBUG_PRINT("enter", ("db: '%s'", db));
+
+ mysql_real_escape_string(mysql, db_name_buff, db, (ulong)strlen(db));
+
+ /* nice comments */
+ print_comment(sql_file, 0,
+ "\n--\n-- Dumping routines for database '%s'\n--\n",
+ fix_for_comment(db));
+
+ /*
+ not using "mysql_query_with_error_report" because we may have not
+ enough privileges to lock mysql.proc.
+ */
+ if (lock_tables)
+ mysql_query(mysql, "LOCK TABLES mysql.proc READ");
+
+ /* Get database collation. */
+
+ if (fetch_db_collation(db, db_cl_name, sizeof (db_cl_name)))
+ DBUG_RETURN(1);
+
+ if (switch_character_set_results(mysql, "binary"))
+ DBUG_RETURN(1);
+
+ if (opt_xml)
+ fputs("\t<routines>\n", sql_file);
+
+ /* 0, retrieve and dump functions, 1, procedures, etc. */
+ for (i= 0; i < upper_bound; i++)
+ {
+ my_snprintf(query_buff, sizeof(query_buff),
+ "SHOW %s STATUS WHERE Db = '%s'",
+ routine_type[i], db_name_buff);
+
+ if (mysql_query_with_error_report(mysql, &routine_list_res, query_buff))
+ DBUG_RETURN(1);
+
+ if (mysql_num_rows(routine_list_res))
+ {
+
+ while ((routine_list_row= mysql_fetch_row(routine_list_res)))
+ {
+ routine_name= quote_name(routine_list_row[1], name_buff, 0);
+ DBUG_PRINT("info", ("retrieving CREATE %s for %s", routine_type[i],
+ name_buff));
+ my_snprintf(query_buff, sizeof(query_buff), "SHOW CREATE %s %s",
+ routine_type[i], routine_name);
+
+ if (mysql_query_with_error_report(mysql, &routine_res, query_buff))
+ continue;
+
+ while ((row= mysql_fetch_row(routine_res)))
+ {
+ /*
+ if the user has EXECUTE privilege he see routine names, but NOT the
+ routine body of other routines that are not the creator of!
+ */
+ DBUG_PRINT("info",("length of body for %s row[2] '%s' is %zu",
+ routine_name, row[2] ? row[2] : "(null)",
+ row[2] ? strlen(row[2]) : 0));
+ if (row[2] == NULL)
+ {
+ print_comment(sql_file, 1, "\n-- insufficient privileges to %s\n",
+ query_buff);
+ 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 insufficient privileges to %s!",
+ current_user, query_buff);
+ }
+ else if (strlen(row[2]))
+ {
+ if (opt_xml)
+ {
+ print_xml_row(sql_file, "routine", routine_res, &row,
+ create_caption_xml[i]);
+ continue;
+ }
+
+ switch_sql_mode(sql_file, ";", row[1]);
+
+ if (opt_drop)
+ fprintf(sql_file, "/*!50003 DROP %s IF EXISTS %s */;\n",
+ routine_type[i], routine_name);
+
+ if (mysql_num_fields(routine_res) >= 6)
+ {
+ 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);
+ }
+
+ switch_cs_variables(sql_file, ";",
+ row[3], /* character_set_client */
+ row[3], /* character_set_results */
+ row[4]); /* collation_connection */
+ }
+ else
+ {
+ /*
+ mysqldump is being run against the server, that does not
+ provide character set information in SHOW CREATE
+ statements.
+
+ NOTE: the dump may be incorrect, since character set
+ information is required in order to restore stored
+ procedure/function properly.
+ */
+
+ fprintf(sql_file,
+ "--\n"
+ "-- WARNING: old server version. "
+ "The following dump may be incomplete.\n"
+ "--\n");
+ }
+
+ fprintf(sql_file,
+ "DELIMITER ;;\n"
+ "%s ;;\n"
+ "DELIMITER ;\n",
+ (const char *) row[2]);
+
+ restore_sql_mode(sql_file, ";");
+
+ if (mysql_num_fields(routine_res) >= 6)
+ {
+ restore_cs_variables(sql_file, ";");
+
+ 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)
+ {
+ fputs("\t</routines>\n", sql_file);
+ check_io(sql_file);
+ }
+
+ if (switch_character_set_results(mysql, default_charset))
+ DBUG_RETURN(1);
+
+ if (lock_tables)
+ (void) mysql_query_with_error_report(mysql, 0, "UNLOCK TABLES");
+ DBUG_RETURN(0);
+}
+
+/* general_log or slow_log tables under mysql database */
+static inline my_bool general_log_or_slow_log_tables(const char *db,
+ const char *table)
+{
+ return (!my_strcasecmp(charset_info, db, "mysql")) &&
+ (!my_strcasecmp(charset_info, table, "general_log") ||
+ !my_strcasecmp(charset_info, table, "slow_log") ||
+ !my_strcasecmp(charset_info, table, "transaction_registry"));
+}
+/*
+ get_sequence_structure-- retrieves sequence structure, prints out corresponding
+ CREATE statement
+ ARGS
+ seq - sequence name
+ db - db name
+*/
+
+static void get_sequence_structure(const char *seq, const char *db)
+{
+
+ char table_buff[NAME_LEN*2+3];
+ char *result_seq;
+ FILE *sql_file= md_result_file;
+ MYSQL_RES *result;
+ MYSQL_ROW row;
+
+ DBUG_ENTER("get_sequence_structure");
+ DBUG_PRINT("enter", ("db: %s sequence: %s", db, seq));
+
+ verbose_msg("-- Retrieving sequence structure for %s...\n", seq);
+
+ result_seq= quote_name(seq, table_buff, 1);
+ // Sequences as tables share same flags
+ if (!opt_no_create_info)
+ {
+ char buff[20+FN_REFLEN];
+ my_snprintf(buff, sizeof(buff), "SHOW CREATE SEQUENCE %s", result_seq);
+ if (mysql_query_with_error_report(mysql, &result, buff))
+ {
+ DBUG_VOID_RETURN;
+ }
+
+ print_comment(sql_file, 0,
+ "\n--\n-- Sequence structure for %s\n--\n\n",
+ fix_for_comment(result_seq));
+ if (opt_drop)
+ {
+ fprintf(sql_file, "DROP SEQUENCE IF EXISTS %s;\n", result_seq);
+ check_io(sql_file);
+ }
+
+ row= mysql_fetch_row(result);
+ fprintf(sql_file, "%s;\n", row[1]);
+ mysql_free_result(result);
+
+ // Restore next not cached value from sequence
+ my_snprintf(buff, sizeof(buff), "SELECT next_not_cached_value FROM %s", result_seq);
+ if (mysql_query_with_error_report(mysql, &result, buff))
+ {
+ DBUG_VOID_RETURN;
+ }
+ row= mysql_fetch_row(result);
+ if (row[0])
+ {
+ fprintf(sql_file, "SELECT SETVAL(%s, %s, 0);\n", result_seq, row[0]);
+ }
+ // Sequences will not use inserts, so no need for REPLACE and LOCKS
+ mysql_free_result(result);
+ }
+ DBUG_VOID_RETURN;
+}
+/*
+ get_table_structure -- retrieves database structure, prints out corresponding
+ CREATE statement and fills out insert_pat if the table is the type we will
+ be dumping.
+
+ ARGS
+ table - table name
+ db - db name
+ table_type - table type, e.g. "MyISAM" or "InnoDB", but also "VIEW"
+ ignore_flag - what we must particularly ignore - see IGNORE_ defines above
+
+ RETURN
+ number of fields in table, 0 if error
+*/
+
+static uint get_table_structure(const char *table, const char *db, char *table_type,
+ char *ignore_flag, my_bool *versioned)
+{
+ my_bool init=0, delayed, write_data, complete_insert;
+ my_ulonglong num_fields;
+ char *result_table, *opt_quoted_table;
+ const char *insert_option;
+ char name_buff[NAME_LEN+3],table_buff[NAME_LEN*2+3];
+ char table_buff2[NAME_LEN*2+3], query_buff[QUERY_LENGTH];
+ char temp_buff[NAME_LEN*2 + 3], temp_buff2[NAME_LEN*2 + 3];
+ FILE *sql_file= md_result_file;
+ size_t len;
+ my_bool is_log_table;
+ MYSQL_RES *result;
+ MYSQL_ROW row;
+ const char *s3_engine_ptr;
+ DYNAMIC_STRING create_table_str;
+ static const char s3_engine_token[]= " ENGINE=S3 ";
+ static const char aria_engine_token[]= " ENGINE=Aria ";
+ DBUG_ENTER("get_table_structure");
+ DBUG_PRINT("enter", ("db: %s table: %s", db, table));
+
+ *ignore_flag= check_if_ignore_table(table, table_type);
+
+ if (!opt_copy_s3_tables && *ignore_flag == IGNORE_S3_TABLE)
+ DBUG_RETURN(0);
+
+ delayed= opt_delayed;
+ if (delayed && (*ignore_flag & IGNORE_INSERT_DELAYED))
+ {
+ delayed= 0;
+ verbose_msg("-- Warning: Unable to use delayed inserts for table '%s' "
+ "because it's of type %s\n", table, table_type);
+ }
+
+ complete_insert= 0;
+ if ((write_data= !(*ignore_flag & IGNORE_DATA)))
+ {
+ complete_insert= opt_complete_insert;
+ if (!insert_pat_inited)
+ {
+ insert_pat_inited= 1;
+ init_dynamic_string_checked(&insert_pat, "", 1024, 1024);
+ }
+ else
+ dynstr_set_checked(&insert_pat, "");
+ }
+ if (!select_field_names_inited)
+ {
+ select_field_names_inited= 1;
+ init_dynamic_string_checked(&select_field_names, "", 1024, 1024);
+ if (opt_header)
+ init_dynamic_string_checked(&select_field_names_for_header, "", 1024, 1024);
+ }
+ else
+ {
+ dynstr_set_checked(&select_field_names, "");
+ if (opt_header)
+ dynstr_set_checked(&select_field_names_for_header, "");
+ }
+ insert_option= ((delayed && opt_ignore) ? " DELAYED IGNORE " :
+ delayed ? " DELAYED " : opt_ignore ? " IGNORE " : "");
+
+ verbose_msg("-- Retrieving table structure for table %s...\n", table);
+
+ if (versioned)
+ {
+ if (!opt_asof_timestamp && !opt_dump_history)
+ versioned= NULL;
+ else
+ {
+ my_snprintf(query_buff, sizeof(query_buff), "select 1 from"
+ " information_schema.tables where table_schema=database()"
+ " and table_name=%s and table_type='SYSTEM VERSIONED'",
+ quote_for_equal(table, table_buff));
+ if (!mysql_query_with_error_report(mysql, &result, query_buff))
+ {
+ *versioned= result->row_count > 0;
+ mysql_free_result(result);
+ }
+ else
+ *versioned= 0;
+ }
+ }
+
+ len= my_snprintf(query_buff, sizeof(query_buff),
+ "SET SQL_QUOTE_SHOW_CREATE=%d", opt_quoted || opt_keywords);
+ if (!create_options)
+ strmov(query_buff+len,
+ "/*!40102 ,SQL_MODE=concat(@@sql_mode, _utf8 ',NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS') */");
+
+ result_table= quote_name(table, table_buff, 1);
+ opt_quoted_table= quote_name(table, table_buff2, 0);
+
+ if (opt_order_by_primary)
+ order_by= primary_key_fields(result_table);
+
+ if (!opt_xml && !mysql_query_with_error_report(mysql, 0, query_buff))
+ {
+ int vers_hidden= opt_dump_history && versioned && *versioned;
+ /* using SHOW CREATE statement */
+ if (!opt_no_create_info)
+ {
+ /* Make an sql-file, if path was given iow. option -T was given */
+ char buff[20+FN_REFLEN];
+ MYSQL_FIELD *field;
+
+ my_snprintf(buff, sizeof(buff), "show create table %s", result_table);
+
+ 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);
+ }
+
+ if (strcmp (table_type, "VIEW") == 0) /* view */
+ print_comment(sql_file, 0,
+ "\n--\n-- Temporary table structure for view %s\n--\n\n",
+ fix_for_comment(result_table));
+ else
+ print_comment(sql_file, 0,
+ "\n--\n-- Table structure for table %s\n--\n\n",
+ fix_for_comment(result_table));
+
+ if (opt_drop)
+ {
+ /*
+ Even if the "table" is a view, we do a DROP TABLE here. The
+ view-specific code below fills in the DROP VIEW.
+ We will skip the DROP TABLE for general_log and slow_log, since
+ those stmts will fail, in case we apply dump by enabling logging.
+ */
+ if (!general_log_or_slow_log_tables(db, table))
+ fprintf(sql_file, "DROP TABLE IF EXISTS %s;\n",
+ opt_quoted_table);
+ check_io(sql_file);
+ }
+
+ field= mysql_fetch_field_direct(result, 0);
+ if (strcmp(field->name, "View") == 0)
+ {
+ char *scv_buff= NULL;
+
+ verbose_msg("-- It's a view, create dummy view for view\n");
+
+ /* save "show create" statement for later */
+ if ((row= mysql_fetch_row(result)) && (scv_buff=row[1]))
+ scv_buff= my_strdup(PSI_NOT_INSTRUMENTED, scv_buff, MYF(0));
+
+ mysql_free_result(result);
+
+ /*
+ Create a view with the same name as the view and with columns of
+ the same name in order to satisfy views that depend on this view.
+ The view will be removed when the actual view is created.
+
+ The properties of each column, are not preserved in this temporary
+ table, because they are not necessary.
+
+ This will not be necessary once we can determine dependencies
+ between views and can simply dump them in the appropriate order.
+ */
+ my_snprintf(query_buff, sizeof(query_buff),
+ "SHOW FIELDS FROM %s", result_table);
+ if (switch_character_set_results(mysql, "binary") ||
+ mysql_query_with_error_report(mysql, &result, query_buff) ||
+ switch_character_set_results(mysql, default_charset))
+ {
+ /*
+ View references invalid or privileged table/col/fun (err 1356),
+ so we cannot create a stand-in table. Be defensive and dump
+ a comment with the view's 'show create' statement. (Bug #17371)
+ */
+
+ if (mysql_errno(mysql) == ER_VIEW_INVALID)
+ fprintf(sql_file, "\n-- failed on view %s: %s\n\n", result_table, scv_buff ? scv_buff : "");
+
+ my_free(scv_buff);
+
+ if (path)
+ my_fclose(sql_file, MYF(MY_WME));
+ DBUG_RETURN(0);
+ }
+ else
+ my_free(scv_buff);
+
+ if (mysql_num_rows(result) != 0)
+ {
+
+ if (opt_drop)
+ {
+ /*
+ We have already dropped any table of the same name above, so
+ here we just drop the view.
+ */
+
+ fprintf(sql_file, "/*!50001 DROP VIEW IF EXISTS %s*/;\n",
+ opt_quoted_table);
+ check_io(sql_file);
+ }
+
+ fprintf(sql_file,
+ "SET @saved_cs_client = @@character_set_client;\n"
+ "SET character_set_client = utf8;\n"
+ "/*!50001 CREATE VIEW %s AS SELECT\n",
+ result_table);
+
+ /*
+ Get first row, following loop will prepend comma - keeps from
+ having to know if the row being printed is last to determine if
+ there should be a _trailing_ comma.
+ */
+
+ row= mysql_fetch_row(result);
+
+ /*
+ The actual column value doesn't matter anyway, since the view will
+ be dropped at run time.
+ */
+ fprintf(sql_file, " 1 AS %s",
+ quote_name(row[0], name_buff, 0));
+
+ while((row= mysql_fetch_row(result)))
+ {
+ /* col name, col type */
+ fprintf(sql_file, ",\n 1 AS %s",
+ quote_name(row[0], name_buff, 0));
+ }
+
+ fprintf(sql_file,
+ " */;\n"
+ "SET character_set_client = @saved_cs_client;\n");
+
+ check_io(sql_file);
+ }
+
+ mysql_free_result(result);
+
+ if (path)
+ my_fclose(sql_file, MYF(MY_WME));
+
+ seen_views= 1;
+ DBUG_RETURN(0);
+ }
+
+ row= mysql_fetch_row(result);
+
+ is_log_table= general_log_or_slow_log_tables(db, table);
+ if (is_log_table)
+ row[1]+= 13; /* strlen("CREATE TABLE ")= 13 */
+ create_table_str.str= row[1];
+ if (opt_copy_s3_tables && (*ignore_flag & IGNORE_S3_TABLE) &&
+ (s3_engine_ptr= strstr(row[1], s3_engine_token)))
+ {
+ init_dynamic_string_checked(&create_table_str, "", 1024, 1024);
+ dynstr_append_mem_checked(&create_table_str, row[1],
+ (uint)(s3_engine_ptr - row[1]));
+ dynstr_append_checked(&create_table_str, aria_engine_token);
+ dynstr_append_checked(&create_table_str,
+ s3_engine_ptr + sizeof(s3_engine_token) - 1);
+ }
+ if (opt_compatible_mode & 3)
+ {
+ fprintf(sql_file,
+ is_log_table ? "CREATE TABLE IF NOT EXISTS %s;\n" : "%s;\n",
+ create_table_str.str);
+ }
+ else
+ {
+ fprintf(sql_file,
+ "/*!40101 SET @saved_cs_client = @@character_set_client */;\n"
+ "/*!40101 SET character_set_client = utf8 */;\n"
+ "%s%s;\n"
+ "/*!40101 SET character_set_client = @saved_cs_client */;\n",
+ is_log_table ? "CREATE TABLE IF NOT EXISTS " : "",
+ create_table_str.str);
+ }
+
+ check_io(sql_file);
+ if (create_table_str.str != row[1])
+ dynstr_free(&create_table_str);
+ mysql_free_result(result);
+ }
+ my_snprintf(query_buff, sizeof(query_buff),
+ "select column_name, extra, generation_expression, data_type "
+ "from information_schema.columns where table_schema=database() "
+ "and table_name=%s order by ordinal_position",
+ quote_for_equal(table, temp_buff));
+ if (mysql_query_with_error_report(mysql, &result, query_buff))
+ {
+ if (path)
+ my_fclose(sql_file, MYF(MY_WME));
+ DBUG_RETURN(0);
+ }
+
+ while ((row= mysql_fetch_row(result)))
+ {
+ if (strstr(row[1],"INVISIBLE"))
+ complete_insert= 1;
+ if (vers_hidden && row[2] && strcmp(row[2], "ROW START") == 0)
+ {
+ vers_hidden= 0;
+ if (row[3] && strcmp(row[3], "bigint") == 0)
+ {
+ maybe_die(EX_ILLEGAL_TABLE, "Cannot use --dump-history for table %s with transaction-precise history",
+ result_table);
+ *versioned= 0;
+ }
+ }
+ if (init)
+ {
+ dynstr_append_checked(&select_field_names, ", ");
+ if (opt_header)
+ dynstr_append_checked(&select_field_names_for_header, ", ");
+ }
+ init=1;
+ dynstr_append_checked(&select_field_names,
+ quote_name(row[0], name_buff, 0));
+ if (opt_header)
+ dynstr_append_checked(&select_field_names_for_header,
+ quote_for_equal(row[0], name_buff));
+ }
+
+ if (vers_hidden)
+ {
+ complete_insert= 1;
+ dynstr_append_checked(&select_field_names, ", row_start, row_end");
+ }
+
+ /*
+ If write_data is true, then we build up insert statements for
+ the table's data. Note: in subsequent lines of code, this test
+ will have to be performed each time we are appending to
+ insert_pat.
+ */
+ if (write_data)
+ {
+ if (opt_replace_into)
+ dynstr_append_checked(&insert_pat, "REPLACE ");
+ else
+ dynstr_append_checked(&insert_pat, "INSERT ");
+ dynstr_append_checked(&insert_pat, insert_option);
+ dynstr_append_checked(&insert_pat, "INTO ");
+ dynstr_append_checked(&insert_pat, opt_quoted_table);
+ if (complete_insert)
+ {
+ dynstr_append_checked(&insert_pat, " (");
+ }
+ else
+ {
+ if (extended_insert)
+ dynstr_append_checked(&insert_pat, " VALUES\n");
+ else
+ dynstr_append_checked(&insert_pat, " VALUES (");
+ }
+ }
+
+ if (complete_insert)
+ dynstr_append_checked(&insert_pat, select_field_names.str);
+ num_fields= mysql_num_rows(result) + (vers_hidden ? 2 : 0);
+ mysql_free_result(result);
+ }
+ else
+ {
+ const char *show_fields_stmt= "SELECT `COLUMN_NAME` AS `Field`, "
+ "`COLUMN_TYPE` AS `Type`, "
+ "`IS_NULLABLE` AS `Null`, "
+ "`COLUMN_KEY` AS `Key`, "
+ "`COLUMN_DEFAULT` AS `Default`, "
+ "`EXTRA` AS `Extra`, "
+ "`COLUMN_COMMENT` AS `Comment` "
+ "FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE "
+ "TABLE_SCHEMA = %s AND TABLE_NAME = %s "
+ "ORDER BY ORDINAL_POSITION";
+
+ verbose_msg("%s: Warning: Can't set SQL_QUOTE_SHOW_CREATE option (%s)\n",
+ my_progname_short, mysql_error(mysql));
+
+ my_snprintf(query_buff, sizeof(query_buff), show_fields_stmt,
+ quote_for_equal(db, temp_buff),
+ quote_for_equal(table, temp_buff2));
+
+ if (mysql_query_with_error_report(mysql, &result, query_buff))
+ DBUG_RETURN(0);
+
+ /* Make an sql-file, if path was given iow. option -T was given */
+ if (!opt_no_create_info)
+ {
+ if (path)
+ {
+ if (!(sql_file= open_sql_file_for_table(table, O_WRONLY)))
+ {
+ mysql_free_result(result);
+ DBUG_RETURN(0);
+ }
+ write_header(sql_file, db);
+ }
+
+ print_comment(sql_file, 0,
+ "\n--\n-- Table structure for table %s\n--\n\n",
+ fix_for_comment(result_table));
+ if (opt_drop)
+ fprintf(sql_file, "DROP TABLE IF EXISTS %s;\n", result_table);
+ if (!opt_xml)
+ fprintf(sql_file, "CREATE TABLE %s (\n", result_table);
+ else
+ print_xml_tag(sql_file, "\t", "\n", "table_structure", "name=", table,
+ NullS);
+ check_io(sql_file);
+ }
+
+ if (write_data)
+ {
+ if (opt_replace_into)
+ dynstr_append_checked(&insert_pat, "REPLACE ");
+ else
+ dynstr_append_checked(&insert_pat, "INSERT ");
+ dynstr_append_checked(&insert_pat, insert_option);
+ dynstr_append_checked(&insert_pat, "INTO ");
+ dynstr_append_checked(&insert_pat, result_table);
+ if (complete_insert)
+ dynstr_append_checked(&insert_pat, " (");
+ else
+ {
+ dynstr_append_checked(&insert_pat, " VALUES ");
+ if (!extended_insert)
+ dynstr_append_checked(&insert_pat, "(");
+ }
+ }
+
+ while ((row= mysql_fetch_row(result)))
+ {
+ ulong *lengths= mysql_fetch_lengths(result);
+ if (init)
+ {
+ if (!opt_xml && !opt_no_create_info)
+ {
+ fputs(",\n",sql_file);
+ check_io(sql_file);
+ }
+ dynstr_append_checked(&select_field_names, ", ");
+ if (opt_header)
+ dynstr_append_checked(&select_field_names_for_header, ", ");
+ }
+ dynstr_append_checked(&select_field_names,
+ quote_name(row[SHOW_FIELDNAME], name_buff, 0));
+ if (opt_header)
+ dynstr_append_checked(&select_field_names_for_header,
+ quote_for_equal(row[SHOW_FIELDNAME], name_buff));
+ init=1;
+ if (!opt_no_create_info)
+ {
+ if (opt_xml)
+ {
+ print_xml_row(sql_file, "field", result, &row, NullS);
+ continue;
+ }
+
+ if (opt_keywords)
+ fprintf(sql_file, " %s.%s %s", result_table,
+ quote_name(row[SHOW_FIELDNAME],name_buff, 0), row[SHOW_TYPE]);
+ else
+ fprintf(sql_file, " %s %s",
+ quote_name(row[SHOW_FIELDNAME], name_buff, 0), row[SHOW_TYPE]);
+ if (row[SHOW_DEFAULT])
+ {
+ fputs(" DEFAULT ", sql_file);
+ unescape(sql_file, row[SHOW_DEFAULT], lengths[SHOW_DEFAULT]);
+ }
+ if (!row[SHOW_NULL][0])
+ fputs(" NOT NULL", sql_file);
+ if (row[SHOW_EXTRA][0])
+ fprintf(sql_file, " %s",row[SHOW_EXTRA]);
+ check_io(sql_file);
+ }
+ }
+ if (complete_insert)
+ dynstr_append_checked(&insert_pat, select_field_names.str);
+ num_fields= mysql_num_rows(result);
+ mysql_free_result(result);
+ if (!opt_no_create_info)
+ {
+ /* Make an sql-file, if path was given iow. option -T was given */
+ char buff[20+FN_REFLEN];
+ uint keynr,primary_key;
+ my_snprintf(buff, sizeof(buff), "show keys from %s", result_table);
+ if (mysql_query_with_error_report(mysql, &result, buff))
+ {
+ if (mysql_errno(mysql) == ER_WRONG_OBJECT)
+ {
+ /* it is VIEW */
+ fputs("\t\t<options Comment=\"view\" />\n", sql_file);
+ goto continue_xml;
+ }
+ fprintf(stderr, "%s: Can't get keys for table %s (%s)\n",
+ my_progname_short, result_table, mysql_error(mysql));
+ if (path)
+ my_fclose(sql_file, MYF(MY_WME));
+ DBUG_RETURN(0);
+ }
+
+ /* Find first which key is primary key */
+ keynr=0;
+ primary_key=INT_MAX;
+ while ((row= mysql_fetch_row(result)))
+ {
+ if (atoi(row[3]) == 1)
+ {
+ keynr++;
+#ifdef FORCE_PRIMARY_KEY
+ if (atoi(row[1]) == 0 && primary_key == INT_MAX)
+ primary_key=keynr;
+#endif
+ if (!strcmp(row[2],"PRIMARY"))
+ {
+ primary_key=keynr;
+ break;
+ }
+ }
+ }
+ mysql_data_seek(result,0);
+ keynr=0;
+ while ((row= mysql_fetch_row(result)))
+ {
+ if (opt_xml)
+ {
+ print_xml_row(sql_file, "key", result, &row, NullS);
+ continue;
+ }
+
+ if (atoi(row[3]) == 1)
+ {
+ if (keynr++)
+ putc(')', sql_file);
+ if (atoi(row[1])) /* Test if duplicate key */
+ /* Duplicate allowed */
+ fprintf(sql_file, ",\n KEY %s (",quote_name(row[2],name_buff,0));
+ else if (keynr == primary_key)
+ fputs(",\n PRIMARY KEY (",sql_file); /* First UNIQUE is primary */
+ else
+ fprintf(sql_file, ",\n UNIQUE %s (",quote_name(row[2],name_buff,
+ 0));
+ }
+ else
+ putc(',', sql_file);
+ fputs(quote_name(row[4], name_buff, 0), sql_file);
+ if (row[7])
+ fprintf(sql_file, " (%s)",row[7]); /* Sub key */
+ check_io(sql_file);
+ }
+ mysql_free_result(result);
+ if (!opt_xml)
+ {
+ if (keynr)
+ putc(')', sql_file);
+ fputs("\n)",sql_file);
+ check_io(sql_file);
+ }
+
+ /* Get MySQL specific create options */
+ if (create_options)
+ {
+ char show_name_buff[NAME_LEN*2+2+24];
+
+ /* Check memory for quote_for_like() */
+ my_snprintf(buff, sizeof(buff), "show table status like %s",
+ quote_for_like(table, show_name_buff));
+
+ if (mysql_query_with_error_report(mysql, &result, buff))
+ {
+ if (mysql_errno(mysql) != ER_PARSE_ERROR)
+ { /* If old MySQL version */
+ verbose_msg("-- Warning: Couldn't get status information for " \
+ "table %s (%s)\n", result_table,mysql_error(mysql));
+ }
+ }
+ else if (!(row= mysql_fetch_row(result)))
+ {
+ fprintf(stderr,
+ "Error: Couldn't read status information for table %s (%s)\n",
+ result_table,mysql_error(mysql));
+ }
+ else
+ {
+ if (opt_xml)
+ print_xml_row(sql_file, "options", result, &row, NullS);
+ else
+ {
+ fputs("/*!",sql_file);
+ print_value(sql_file,result,row,"engine=","Engine",0);
+ print_value(sql_file,result,row,"","Create_options",0);
+ print_value(sql_file,result,row,"comment=","Comment",1);
+ fputs(" */",sql_file);
+ check_io(sql_file);
+ }
+ }
+ mysql_free_result(result); /* Is always safe to free */
+ }
+continue_xml:
+ if (!opt_xml)
+ fputs(";\n", sql_file);
+ else
+ fputs("\t</table_structure>\n", sql_file);
+ check_io(sql_file);
+ }
+ }
+ if (complete_insert)
+ {
+ dynstr_append_checked(&insert_pat, ") VALUES ");
+ if (!extended_insert)
+ dynstr_append_checked(&insert_pat, "(");
+ }
+ if (sql_file != md_result_file)
+ {
+ fputs("\n", sql_file);
+ write_footer(sql_file);
+ my_fclose(sql_file, MYF(MY_WME));
+ }
+ DBUG_RETURN((uint) num_fields);
+} /* get_table_structure */
+
+static void dump_trigger_old(FILE *sql_file, MYSQL_RES *show_triggers_rs,
+ MYSQL_ROW *show_trigger_row,
+ const char *table_name)
+{
+ char quoted_table_name_buf[NAME_LEN * 2 + 3];
+ char *quoted_table_name= quote_name(table_name, quoted_table_name_buf, 1);
+
+ char name_buff[NAME_LEN * 4 + 3];
+ const char *xml_msg= "\nWarning! mysqldump being run against old server "
+ "that does not\nsupport 'SHOW CREATE TRIGGER' "
+ "statement. Skipping..\n";
+
+ DBUG_ENTER("dump_trigger_old");
+
+ if (opt_xml)
+ {
+ print_xml_comment(sql_file, strlen(xml_msg), xml_msg);
+ check_io(sql_file);
+ DBUG_VOID_RETURN;
+ }
+
+ fprintf(sql_file,
+ "--\n"
+ "-- WARNING: old server version. "
+ "The following dump may be incomplete.\n"
+ "--\n");
+
+ if (opt_compact)
+ fprintf(sql_file, "/*!50003 SET @OLD_SQL_MODE=@@SQL_MODE*/;\n");
+
+ if (opt_drop_trigger)
+ fprintf(sql_file, "/*!50032 DROP TRIGGER IF EXISTS %s */;\n",
+ (*show_trigger_row)[0]);
+
+ fprintf(sql_file,
+ "DELIMITER ;;\n"
+ "/*!50003 SET SESSION SQL_MODE=\"%s\" */;;\n"
+ "/*!50003 CREATE */ ",
+ (*show_trigger_row)[6]);
+
+ if (mysql_num_fields(show_triggers_rs) > 7)
+ {
+ /*
+ mysqldump can be run against the server, that does not support
+ definer in triggers (there is no DEFINER column in SHOW TRIGGERS
+ output). So, we should check if we have this column before
+ accessing it.
+ */
+
+ size_t user_name_len;
+ char user_name_str[USERNAME_LENGTH + 1];
+ char quoted_user_name_str[USERNAME_LENGTH * 2 + 3];
+ size_t host_name_len;
+ char host_name_str[HOSTNAME_LENGTH + 1];
+ char quoted_host_name_str[HOSTNAME_LENGTH * 2 + 3];
+
+ parse_user((*show_trigger_row)[7],
+ strlen((*show_trigger_row)[7]),
+ user_name_str, &user_name_len,
+ host_name_str, &host_name_len);
+
+ fprintf(sql_file,
+ "/*!50017 DEFINER=%s@%s */ ",
+ quote_name(user_name_str, quoted_user_name_str, FALSE),
+ quote_name(host_name_str, quoted_host_name_str, FALSE));
+ }
+
+ fprintf(sql_file,
+ "/*!50003 TRIGGER %s %s %s ON %s FOR EACH ROW%s%s */;;\n"
+ "DELIMITER ;\n",
+ quote_name((*show_trigger_row)[0], name_buff, 0), /* Trigger */
+ (*show_trigger_row)[4], /* Timing */
+ (*show_trigger_row)[1], /* Event */
+ quoted_table_name,
+ (strchr(" \t\n\r", *((*show_trigger_row)[3]))) ? "" : " ",
+ (*show_trigger_row)[3] /* Statement */);
+
+ if (opt_compact)
+ fprintf(sql_file, "/*!50003 SET SESSION SQL_MODE=@OLD_SQL_MODE */;\n");
+
+ DBUG_VOID_RETURN;
+}
+
+static int dump_trigger(FILE *sql_file, MYSQL_RES *show_create_trigger_rs,
+ const char *db_name,
+ const char *db_cl_name)
+{
+ MYSQL_ROW row;
+ char *query_str;
+ int db_cl_altered= FALSE;
+
+ DBUG_ENTER("dump_trigger");
+
+ while ((row= mysql_fetch_row(show_create_trigger_rs)))
+ {
+ if (opt_xml)
+ {
+ print_xml_row(sql_file, "trigger", show_create_trigger_rs, &row,
+ "SQL Original Statement");
+ check_io(sql_file);
+ continue;
+ }
+
+ if (switch_db_collation(sql_file, db_name, ";",
+ db_cl_name, row[5], &db_cl_altered))
+ DBUG_RETURN(TRUE);
+
+ switch_cs_variables(sql_file, ";",
+ row[3], /* character_set_client */
+ row[3], /* character_set_results */
+ row[4]); /* collation_connection */
+
+ switch_sql_mode(sql_file, ";", row[1]);
+
+ if (opt_drop_trigger)
+ fprintf(sql_file, "/*!50032 DROP TRIGGER IF EXISTS %s */;\n",
+ row[0]);
+
+ 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, ";");
+
+ if (db_cl_altered)
+ {
+ if (restore_db_collation(sql_file, db_name, ";", db_cl_name))
+ DBUG_RETURN(TRUE);
+ }
+ }
+
+ DBUG_RETURN(FALSE);
+}
+
+/**
+ Dump the triggers for a given table.
+
+ This should be called after the tables have been dumped in case a trigger
+ depends on the existence of a table.
+
+ @param[in] table_name
+ @param[in] db_name
+
+ @return Error status.
+ @retval TRUE error has occurred.
+ @retval FALSE operation succeed.
+*/
+
+static int dump_triggers_for_table(char *table_name, char *db_name)
+{
+ char name_buff[NAME_LEN*4+3];
+ char query_buff[QUERY_LENGTH];
+ uint old_opt_compatible_mode= opt_compatible_mode;
+ MYSQL_RES *show_triggers_rs= NULL;
+ MYSQL_ROW row;
+ FILE *sql_file= md_result_file;
+
+ char db_cl_name[MY_CS_COLLATION_NAME_SIZE];
+ int ret= TRUE;
+ /* Servers below 5.1.21 do not support SHOW CREATE TRIGGER */
+ const int use_show_create_trigger= mysql_get_server_version(mysql) >= 50121;
+
+ DBUG_ENTER("dump_triggers_for_table");
+ DBUG_PRINT("enter", ("db: %s, table_name: %s", db_name, table_name));
+
+ if (path &&
+ !(sql_file= open_sql_file_for_table(table_name, O_WRONLY | O_APPEND)))
+ DBUG_RETURN(1);
+
+ /* Do not use ANSI_QUOTES on triggers in dump */
+ opt_compatible_mode&= ~MASK_ANSI_QUOTES;
+
+ /* Get database collation. */
+
+ if (switch_character_set_results(mysql, "binary"))
+ goto done;
+
+ if (fetch_db_collation(db_name, db_cl_name, sizeof (db_cl_name)))
+ goto done;
+
+ /* Get list of triggers. */
+
+ if (use_show_create_trigger)
+ my_snprintf(query_buff, sizeof(query_buff),
+ "SELECT TRIGGER_NAME FROM INFORMATION_SCHEMA.TRIGGERS "
+ "WHERE EVENT_OBJECT_SCHEMA = DATABASE() AND "
+ "EVENT_OBJECT_TABLE = %s",
+ quote_for_equal(table_name, name_buff));
+ else
+ my_snprintf(query_buff, sizeof(query_buff), "SHOW TRIGGERS LIKE %s",
+ quote_for_like(table_name, name_buff));
+
+ if (mysql_query_with_error_report(mysql, &show_triggers_rs, query_buff))
+ goto done;
+
+ /* Dump triggers. */
+
+ if (! mysql_num_rows(show_triggers_rs))
+ goto skip;
+
+ if (opt_xml)
+ print_xml_tag(sql_file, "\t", "\n", "triggers", "name=",
+ table_name, NullS);
+
+ while ((row= mysql_fetch_row(show_triggers_rs)))
+ {
+ if (use_show_create_trigger)
+ {
+ MYSQL_RES *show_create_trigger_rs;
+
+ my_snprintf(query_buff, sizeof (query_buff), "SHOW CREATE TRIGGER %s",
+ quote_name(row[0], name_buff, TRUE));
+
+ if (mysql_query_with_error_report(mysql, &show_create_trigger_rs,
+ query_buff))
+ goto done;
+ else
+ {
+ 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;
+ }
+ }
+ else
+ dump_trigger_old(sql_file, show_triggers_rs, &row, table_name);
+ }
+
+ if (opt_xml)
+ {
+ fputs("\t</triggers>\n", sql_file);
+ check_io(sql_file);
+ }
+
+skip:
+ if (switch_character_set_results(mysql, default_charset))
+ goto done;
+
+ /*
+ make sure to set back opt_compatible mode to
+ original value
+ */
+ opt_compatible_mode=old_opt_compatible_mode;
+
+ ret= FALSE;
+
+done:
+ if (path)
+ my_fclose(sql_file, MYF(0));
+ mysql_free_result(show_triggers_rs);
+ DBUG_RETURN(ret);
+}
+
+static void add_load_option(DYNAMIC_STRING *str, const char *option,
+ const char *option_value)
+{
+ if (!option_value)
+ {
+ /* Null value means we don't add this option. */
+ return;
+ }
+
+ dynstr_append_checked(str, option);
+
+ if (strncmp(option_value, "0x", sizeof("0x")-1) == 0)
+ {
+ /* It's a hex constant, don't escape */
+ dynstr_append_checked(str, option_value);
+ }
+ else
+ {
+ /* char constant; escape */
+ field_escape(str, option_value);
+ }
+}
+
+
+/*
+ Allow the user to specify field terminator strings like:
+ "'", "\", "\\" (escaped backslash), "\t" (tab), "\n" (newline)
+ This is done by doubling ' and add a end -\ if needed to avoid
+ syntax errors from the SQL parser.
+*/
+
+static void field_escape(DYNAMIC_STRING* in, const char *from)
+{
+ uint end_backslashes= 0;
+
+ dynstr_append_checked(in, "'");
+
+ while (*from)
+ {
+ dynstr_append_mem_checked(in, from, 1);
+
+ if (*from == '\\')
+ end_backslashes^=1; /* find odd number of backslashes */
+ else
+ {
+ if (*from == '\'' && !end_backslashes)
+ {
+ /* We want a duplicate of "'" for MySQL */
+ dynstr_append_checked(in, "\'");
+ }
+ end_backslashes=0;
+ }
+ from++;
+ }
+ /* Add missing backslashes if user has specified odd number of backs.*/
+ if (end_backslashes)
+ dynstr_append_checked(in, "\\");
+
+ dynstr_append_checked(in, "'");
+}
+
+
+
+static char *alloc_query_str(size_t size)
+{
+ char *query;
+
+ if (!(query= (char*) my_malloc(PSI_NOT_INSTRUMENTED, size, MYF(MY_WME))))
+ die(EX_MYSQLERR, "Couldn't allocate a query string.");
+
+ return query;
+}
+
+
+static void vers_append_system_time(DYNAMIC_STRING* query_string)
+{
+ if (opt_dump_history)
+ dynstr_append_checked(query_string, " FOR SYSTEM_TIME ALL");
+ else
+ {
+ DBUG_ASSERT(opt_asof_timestamp);
+ dynstr_append_checked(query_string, " FOR SYSTEM_TIME AS OF TIMESTAMP '");
+ dynstr_append_checked(query_string, opt_asof_timestamp);
+ dynstr_append_checked(query_string, "'");
+ }
+}
+
+
+/*
+
+ SYNOPSIS
+ dump_table()
+
+ dump_table saves database contents as a series of INSERT statements.
+
+ ARGS
+ table - table name
+ db - db name
+
+ RETURNS
+ void
+*/
+
+
+static void dump_table(const char *table, const char *db, const uchar *hash_key, size_t len)
+{
+ char ignore_flag;
+ char buf[200], table_buff[NAME_LEN+3];
+ DYNAMIC_STRING query_string;
+ char table_type[NAME_LEN];
+ char *result_table, table_buff2[NAME_LEN*2+3], *opt_quoted_table;
+ int error= 0;
+ ulong rownr, row_break;
+ uint num_fields;
+ size_t total_length, init_length;
+ my_bool versioned= 0;
+
+ MYSQL_RES *res= NULL;
+ MYSQL_FIELD *field;
+ MYSQL_ROW row;
+ DBUG_ENTER("dump_table");
+
+ /*
+ Make sure you get the create table info before the following check for
+ --no-data flag below. Otherwise, the create table info won't be printed.
+ */
+ num_fields= get_table_structure(table, db, table_type, &ignore_flag, &versioned);
+
+ /*
+ The "table" could be a view. If so, we don't do anything here.
+ */
+ if (strcmp(table_type, "VIEW") == 0)
+ DBUG_VOID_RETURN;
+
+ if (!opt_copy_s3_tables && (ignore_flag & IGNORE_S3_TABLE))
+ {
+ verbose_msg("-- Skipping dump data for table '%s', "
+ " this is S3 table and --copy-s3-tables=0\n",
+ table);
+ DBUG_VOID_RETURN;
+ }
+
+ /* Check --no-data flag */
+ if (opt_no_data || (hash_key && ignore_table_data(hash_key, len)))
+ {
+ verbose_msg("-- Skipping dump data for table '%s', --no-data was used\n",
+ table);
+ DBUG_VOID_RETURN;
+ }
+
+ DBUG_PRINT("info",
+ ("ignore_flag: %x num_fields: %d", (int) ignore_flag,
+ num_fields));
+ /*
+ If the table type is a merge table or any type that has to be
+ _completely_ ignored and no data dumped
+ */
+ if (ignore_flag & IGNORE_DATA)
+ {
+ verbose_msg("-- Warning: Skipping data for table '%s' because " \
+ "it's of type %s\n", table, table_type);
+ DBUG_VOID_RETURN;
+ }
+ /* Check that there are any fields in the table */
+ if (num_fields == 0)
+ {
+ verbose_msg("-- Skipping dump data for table '%s', it has no fields\n",
+ table);
+ DBUG_VOID_RETURN;
+ }
+
+ /*
+ Check --skip-events flag: it is not enough to skip creation of events
+ discarding SHOW CREATE EVENT statements generation. The myslq.event
+ table data should be skipped too.
+ */
+ if (!opt_events && !my_strcasecmp(&my_charset_latin1, db, "mysql") &&
+ !my_strcasecmp(&my_charset_latin1, table, "event"))
+ {
+ verbose_msg("-- Skipping data table mysql.event, --skip-events was used\n");
+ DBUG_VOID_RETURN;
+ }
+
+ result_table= quote_name(table,table_buff, 1);
+ opt_quoted_table= quote_name(table, table_buff2, 0);
+
+ verbose_msg("-- Sending SELECT query...\n");
+
+ init_dynamic_string_checked(&query_string, "", 1024, 1024);
+
+ if (path)
+ {
+ char filename[FN_REFLEN], tmp_path[FN_REFLEN];
+ /*
+ Convert the path to native os format
+ and resolve to the full filepath.
+ */
+ convert_dirname(tmp_path,path,NullS);
+ my_load_path(tmp_path, tmp_path, NULL);
+ fn_format(filename, table, tmp_path, ".txt", MYF(MY_UNPACK_FILENAME));
+
+ /* Must delete the file that 'INTO OUTFILE' will write to */
+ my_delete(filename, MYF(0));
+
+ /* convert to a unix path name to stick into the query */
+ to_unix_path(filename);
+
+ /* now build the query string */
+
+ dynstr_append_checked(&query_string, "SELECT /*!40001 SQL_NO_CACHE */ ");
+ dynstr_append_checked(&query_string, select_field_names.str);
+ dynstr_append_checked(&query_string, " INTO OUTFILE '");
+ dynstr_append_checked(&query_string, filename);
+ dynstr_append_checked(&query_string, "'");
+
+ dynstr_append_checked(&query_string, " /*!50138 CHARACTER SET ");
+ dynstr_append_checked(&query_string, default_charset == mysql_universal_client_charset ?
+ my_charset_bin.coll_name.str : /* backward compatibility */
+ default_charset);
+ dynstr_append_checked(&query_string, " */");
+
+ if (fields_terminated || enclosed || opt_enclosed || escaped)
+ dynstr_append_checked(&query_string, " FIELDS");
+
+ add_load_option(&query_string, " TERMINATED BY ", fields_terminated);
+ add_load_option(&query_string, " ENCLOSED BY ", enclosed);
+ add_load_option(&query_string, " OPTIONALLY ENCLOSED BY ", opt_enclosed);
+ add_load_option(&query_string, " ESCAPED BY ", escaped);
+ add_load_option(&query_string, " LINES TERMINATED BY ", lines_terminated);
+
+ if (opt_header)
+ {
+ dynstr_append_checked(&query_string, " FROM ( SELECT ");
+ if (order_by)
+ dynstr_append_checked(&query_string, " 0 AS `_$is_data_row$_`,");
+ dynstr_append_checked(&query_string, select_field_names_for_header.str);
+ dynstr_append_checked(&query_string, " UNION ALL SELECT ");
+ if (order_by)
+ dynstr_append_checked(&query_string, "1 AS `_$is_data_row$_`,");
+ dynstr_append_checked(&query_string, select_field_names.str);
+ }
+ dynstr_append_checked(&query_string, " FROM ");
+ dynstr_append_checked(&query_string, result_table);
+
+ if (versioned)
+ vers_append_system_time(&query_string);
+
+ if (where)
+ {
+ dynstr_append_checked(&query_string, " WHERE ");
+ dynstr_append_checked(&query_string, where);
+ }
+ if (opt_header)
+ dynstr_append_checked(&query_string, ") s");
+
+ if (order_by)
+ {
+ if (opt_header)
+ dynstr_append_checked(&query_string, " ORDER BY `_$is_data_row$_`,");
+ else
+ 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, (ulong)query_string.length))
+ {
+ dynstr_free(&query_string);
+ DB_error(mysql, "when executing 'SELECT INTO OUTFILE'");
+ DBUG_VOID_RETURN;
+ }
+ }
+ else
+ {
+ print_comment(md_result_file, 0,
+ "\n--\n-- Dumping data for table %s\n--\n",
+ fix_for_comment(result_table));
+
+ dynstr_append_checked(&query_string, "SELECT /*!40001 SQL_NO_CACHE */ ");
+ dynstr_append_checked(&query_string, select_field_names.str);
+ dynstr_append_checked(&query_string, " FROM ");
+ dynstr_append_checked(&query_string, result_table);
+ if (versioned)
+ vers_append_system_time(&query_string);
+
+ if (where)
+ {
+ print_comment(md_result_file, 0, "-- WHERE: %s\n", fix_for_comment(where));
+
+ dynstr_append_checked(&query_string, " WHERE ");
+ dynstr_append_checked(&query_string, where);
+ }
+ if (order_by)
+ {
+ print_comment(md_result_file, 0, "-- ORDER BY: %s\n", fix_for_comment(order_by));
+
+ 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)
+ {
+ fputs("\n", md_result_file);
+ check_io(md_result_file);
+ }
+ 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;
+ }
+ if (quick)
+ res=mysql_use_result(mysql);
+ else
+ res=mysql_store_result(mysql);
+ if (!res)
+ {
+ dynstr_free(&query_string);
+ DB_error(mysql, "when retrieving data from server");
+ goto err;
+ }
+
+ verbose_msg("-- Retrieving rows...\n");
+ if (mysql_num_fields(res) != num_fields)
+ {
+ fprintf(stderr,"%s: Error in field count for table: %s ! Aborting.\n",
+ my_progname_short, result_table);
+ error= EX_CONSCHECK;
+ if (!quick)
+ mysql_free_result(res);
+ goto err;
+ }
+
+ if (versioned && !opt_xml && opt_dump_history)
+ {
+ fprintf(md_result_file,"/*!101100 SET @old_system_versioning_insert_history=@@session.system_versioning_insert_history, @@session.system_versioning_insert_history=1 */;\n");
+ check_io(md_result_file);
+ }
+ if (opt_lock)
+ {
+ fprintf(md_result_file,"LOCK TABLES %s WRITE;\n", opt_quoted_table);
+ check_io(md_result_file);
+ }
+ /* Moved disable keys to after lock per bug 15977 */
+ if (opt_disable_keys)
+ {
+ fprintf(md_result_file, "/*!40000 ALTER TABLE %s DISABLE KEYS */;\n",
+ opt_quoted_table);
+ check_io(md_result_file);
+ }
+
+ total_length= opt_net_buffer_length; /* Force row break */
+ row_break=0;
+ rownr=0;
+ init_length=(uint) insert_pat.length+4;
+ if (opt_xml)
+ print_xml_tag(md_result_file, "\t", "\n", "table_data", "name=", table,
+ NullS);
+ if (opt_autocommit)
+ {
+ fprintf(md_result_file, "set autocommit=0;\n");
+ check_io(md_result_file);
+ }
+
+ while ((row= mysql_fetch_row(res)))
+ {
+ uint i;
+ ulong *lengths= mysql_fetch_lengths(res);
+ rownr++;
+ if (!extended_insert && !opt_xml)
+ {
+ fputs(insert_pat.str,md_result_file);
+ check_io(md_result_file);
+ }
+ mysql_field_seek(res,0);
+
+ if (opt_xml)
+ {
+ fputs("\t<row>\n", md_result_file);
+ check_io(md_result_file);
+ }
+
+ for (i= 0; i < mysql_num_fields(res); i++)
+ {
+ int is_blob;
+ ulong length= lengths[i];
+
+ if (!(field= mysql_fetch_field(res)))
+ die(EX_CONSCHECK,
+ "Not enough fields from table %s! Aborting.\n",
+ result_table);
+
+ /*
+ 63 is my_charset_bin. If charsetnr is not 63,
+ we have not a BLOB but a TEXT column.
+ we'll dump in hex only BLOB columns.
+ */
+ is_blob= (opt_hex_blob && field->charsetnr == 63 &&
+ (field->type == MYSQL_TYPE_BIT ||
+ field->type == MYSQL_TYPE_STRING ||
+ field->type == MYSQL_TYPE_VAR_STRING ||
+ field->type == MYSQL_TYPE_VARCHAR ||
+ field->type == MYSQL_TYPE_BLOB ||
+ field->type == MYSQL_TYPE_LONG_BLOB ||
+ field->type == MYSQL_TYPE_MEDIUM_BLOB ||
+ field->type == MYSQL_TYPE_TINY_BLOB ||
+ field->type == MYSQL_TYPE_GEOMETRY)) ? 1 : 0;
+ if (extended_insert && !opt_xml)
+ {
+ if (i == 0)
+ dynstr_set_checked(&extended_row,"(");
+ else
+ dynstr_append_checked(&extended_row,",");
+
+ if (row[i])
+ {
+ if (length)
+ {
+ if (!(field->flags & NUM_FLAG))
+ {
+ /*
+ "length * 2 + 2" is OK for both HEX and non-HEX modes:
+ - In HEX mode we need exactly 2 bytes per character
+ plus 2 bytes for '0x' prefix.
+ - In non-HEX mode we need up to 2 bytes per character,
+ plus 2 bytes for leading and trailing '\'' characters.
+ Also we need to reserve 1 byte for terminating '\0'.
+ */
+ dynstr_realloc_checked(&extended_row,length * 2 + 2 + 1);
+ if (opt_hex_blob && is_blob)
+ {
+ dynstr_append_checked(&extended_row, "0x");
+ extended_row.length+= mysql_hex_string(extended_row.str +
+ extended_row.length,
+ row[i], length);
+ DBUG_ASSERT(extended_row.length+1 <= extended_row.max_length);
+ /* mysql_hex_string() already terminated string by '\0' */
+ DBUG_ASSERT(extended_row.str[extended_row.length] == '\0');
+ }
+ else
+ {
+ dynstr_append_checked(&extended_row,"'");
+ extended_row.length +=
+ mysql_real_escape_string(&mysql_connection,
+ &extended_row.str[extended_row.length],
+ row[i],length);
+ extended_row.str[extended_row.length]='\0';
+ dynstr_append_checked(&extended_row,"'");
+ }
+ }
+ else
+ {
+ /* change any strings ("inf", "-inf", "nan") into NULL */
+ char *ptr= row[i];
+ if (my_isalpha(charset_info, *ptr) || (*ptr == '-' &&
+ my_isalpha(charset_info, ptr[1])))
+ dynstr_append_checked(&extended_row, "NULL");
+ else
+ {
+ if (field->type == MYSQL_TYPE_DECIMAL)
+ {
+ /* add " signs around */
+ dynstr_append_checked(&extended_row, "'");
+ dynstr_append_checked(&extended_row, ptr);
+ dynstr_append_checked(&extended_row, "'");
+ }
+ else
+ dynstr_append_checked(&extended_row, ptr);
+ }
+ }
+ }
+ else
+ dynstr_append_checked(&extended_row,"''");
+ }
+ else
+ dynstr_append_checked(&extended_row,"NULL");
+ }
+ else
+ {
+ if (i && !opt_xml)
+ {
+ fputc(',', md_result_file);
+ check_io(md_result_file);
+ }
+ if (row[i])
+ {
+ if (!(field->flags & NUM_FLAG))
+ {
+ if (opt_xml)
+ {
+ if (opt_hex_blob && is_blob && length)
+ {
+ /* Define xsi:type="xs:hexBinary" for hex encoded data */
+ print_xml_tag(md_result_file, "\t\t", "", "field", "name=",
+ field->name, "xsi:type=", "xs:hexBinary", NullS);
+ print_blob_as_hex(md_result_file, row[i], length);
+ }
+ else
+ {
+ print_xml_tag(md_result_file, "\t\t", "", "field", "name=",
+ field->name, NullS);
+ print_quoted_xml(md_result_file, row[i], length, 0);
+ }
+ fputs("</field>\n", md_result_file);
+ }
+ else if (opt_hex_blob && is_blob && length)
+ {
+ fputs("0x", md_result_file);
+ print_blob_as_hex(md_result_file, row[i], length);
+ }
+ else
+ unescape(md_result_file, row[i], length);
+ }
+ else
+ {
+ /* change any strings ("inf", "-inf", "nan") into NULL */
+ char *ptr= row[i];
+ if (opt_xml)
+ {
+ print_xml_tag(md_result_file, "\t\t", "", "field", "name=",
+ field->name, NullS);
+ fputs(!my_isalpha(charset_info, *ptr) ? ptr: "NULL",
+ md_result_file);
+ fputs("</field>\n", md_result_file);
+ }
+ else if (my_isalpha(charset_info, *ptr) ||
+ (*ptr == '-' && my_isalpha(charset_info, ptr[1])))
+ fputs("NULL", md_result_file);
+ else if (field->type == MYSQL_TYPE_DECIMAL)
+ {
+ /* add " signs around */
+ fputc('\'', md_result_file);
+ fputs(ptr, md_result_file);
+ fputc('\'', md_result_file);
+ }
+ else
+ fputs(ptr, md_result_file);
+ }
+ }
+ else
+ {
+ /* The field value is NULL */
+ if (!opt_xml)
+ fputs("NULL", md_result_file);
+ else
+ print_xml_null_tag(md_result_file, "\t\t", "field name=",
+ field->name, "\n");
+ }
+ check_io(md_result_file);
+ }
+ }
+
+ if (opt_xml)
+ {
+ fputs("\t</row>\n", md_result_file);
+ check_io(md_result_file);
+ }
+
+ if (extended_insert)
+ {
+ size_t row_length;
+ dynstr_append_checked(&extended_row,")");
+ row_length= 2 + extended_row.length;
+ if (total_length + row_length < opt_net_buffer_length)
+ {
+ total_length+= row_length;
+ fputs(",\n",md_result_file); /* Always row break */
+ fputs(extended_row.str,md_result_file);
+ }
+ else
+ {
+ if (row_break)
+ fputs(";\n", md_result_file);
+ row_break=1; /* This is first row */
+
+ fputs(insert_pat.str,md_result_file);
+ fputs(extended_row.str,md_result_file);
+ total_length= row_length+init_length;
+ }
+ check_io(md_result_file);
+ }
+ else if (!opt_xml)
+ {
+ fputs(");\n", md_result_file);
+ check_io(md_result_file);
+ }
+ }
+
+ /* XML - close table tag and suppress regular output */
+ if (opt_xml)
+ fputs("\t</table_data>\n", md_result_file);
+ else if (extended_insert && row_break)
+ fputs(";\n", md_result_file); /* If not empty table */
+ if (!opt_xml && opt_copy_s3_tables && (ignore_flag & IGNORE_S3_TABLE))
+ {
+ DYNAMIC_STRING alter_string;
+ init_dynamic_string_checked(&alter_string, "ALTER TABLE ", 1024, 1024);
+ dynstr_append_checked(&alter_string, opt_quoted_table);
+ dynstr_append_checked(&alter_string, " ENGINE=S3;\n");
+ fputs(alter_string.str, md_result_file);
+ dynstr_free(&alter_string);
+ }
+ fflush(md_result_file);
+ check_io(md_result_file);
+ if (mysql_errno(mysql))
+ {
+ my_snprintf(buf, sizeof(buf),
+ "%s: Error %d: %s when dumping table %s at row: %ld\n",
+ my_progname_short,
+ mysql_errno(mysql),
+ mysql_error(mysql),
+ result_table,
+ rownr);
+ fputs(buf,stderr);
+ error= EX_CONSCHECK;
+ goto err;
+ }
+
+ /* Moved enable keys to before unlock per bug 15977 */
+ if (opt_disable_keys)
+ {
+ fprintf(md_result_file,"/*!40000 ALTER TABLE %s ENABLE KEYS */;\n",
+ opt_quoted_table);
+ check_io(md_result_file);
+ }
+ if (opt_lock)
+ {
+ fputs("UNLOCK TABLES;\n", md_result_file);
+ check_io(md_result_file);
+ }
+ if (opt_autocommit)
+ {
+ fprintf(md_result_file, "commit;\n");
+ check_io(md_result_file);
+ }
+ if (versioned && !opt_xml && opt_dump_history)
+ {
+ fprintf(md_result_file,"/*!101100 SET system_versioning_insert_history=@old_system_versioning_insert_history */;\n");
+ check_io(md_result_file);
+ }
+ mysql_free_result(res);
+ }
+ dynstr_free(&query_string);
+ DBUG_VOID_RETURN;
+
+err:
+ dynstr_free(&query_string);
+ maybe_exit(error);
+ mysql_free_result(res);
+ DBUG_VOID_RETURN;
+} /* dump_table */
+
+
+static char *getTableName(int reset, int want_sequences)
+{
+ MYSQL_ROW row;
+ const char *query;
+
+ if (!get_table_name_result)
+ {
+ if (opt_order_by_size || mysql_get_server_version(mysql) >= FIRST_SEQUENCE_VERSION)
+ {
+ if (opt_order_by_size) {
+ query= "SELECT table_name, table_type FROM INFORMATION_SCHEMA.TABLES "
+ "WHERE table_schema = DATABASE() ORDER BY data_length, table_name";
+ } else {
+ query = "SHOW FULL TABLES";
+ }
+ if (mysql_query_with_error_report(mysql, 0, query))
+ return (NULL);
+
+ if (!(get_table_name_result= mysql_store_result(mysql)))
+ return (NULL);
+ }
+ else
+ {
+ if (!(get_table_name_result= mysql_list_tables(mysql,NullS)))
+ return(NULL);
+ }
+ }
+ if ((row= mysql_fetch_row(get_table_name_result)))
+ {
+ if (want_sequences != DUMP_TABLE_ALL)
+ while (row && MY_TEST(strcmp(row[1], "SEQUENCE")) == want_sequences)
+ row= mysql_fetch_row(get_table_name_result);
+
+ if (row)
+ return((char*) row[0]);
+ }
+ if (reset)
+ mysql_data_seek(get_table_name_result,0); /* We want to read again */
+ else
+ {
+ mysql_free_result(get_table_name_result);
+ get_table_name_result= NULL;
+ }
+ return(NULL);
+} /* getTableName */
+
+
+/*
+ dump user/role grants
+ ARGS
+ user_role: is either a user, or a role
+*/
+
+static int dump_grants(const char *user_role)
+{
+ DYNAMIC_STRING sqlbuf;
+ MYSQL_ROW row;
+ MYSQL_RES *tableres;
+
+ init_dynamic_string_checked(&sqlbuf, "SHOW GRANTS FOR ", 256, 1024);
+ dynstr_append_checked(&sqlbuf, user_role);
+
+ if (mysql_query_with_error_report(mysql, &tableres, sqlbuf.str))
+ {
+ dynstr_free(&sqlbuf);
+ return 1;
+ }
+ while ((row= mysql_fetch_row(tableres)))
+ {
+ if (strncmp(row[0], "SET DEFAULT ROLE", sizeof("SET DEFAULT ROLE") - 1) == 0)
+ continue;
+ fprintf(md_result_file, "%s;\n", row[0]);
+ }
+ mysql_free_result(tableres);
+ dynstr_free(&sqlbuf);
+ return 0;
+}
+
+
+/*
+ dump create user
+*/
+
+static int dump_create_user(const char *user)
+{
+ DYNAMIC_STRING sqlbuf;
+ MYSQL_ROW row;
+ MYSQL_RES *tableres;
+
+ init_dynamic_string_checked(&sqlbuf, "SHOW CREATE USER ", 256, 1024);
+ dynstr_append_checked(&sqlbuf, user);
+
+ if (mysql_query_with_error_report(mysql, &tableres, sqlbuf.str))
+ {
+ dynstr_free(&sqlbuf);
+ return 1;
+ }
+ while ((row= mysql_fetch_row(tableres)))
+ {
+ fprintf(md_result_file, "CREATE %sUSER %s%s;\n", opt_replace_into ? "/*M!100103 OR REPLACE */ ": "",
+ opt_ignore ? "IF NOT EXISTS " : "",
+ row[0] + sizeof("CREATE USER"));
+ }
+ mysql_free_result(tableres);
+ dynstr_free(&sqlbuf);
+ return 0;
+}
+
+
+/*
+ dump all users, roles and their grants
+*/
+
+static int dump_all_users_roles_and_grants()
+{
+ MYSQL_ROW row;
+ MYSQL_RES *tableres;
+ int result= 0;
+ /* Roles added in MariaDB-10.0.5 or MySQL-8.0 */
+ my_bool maria_roles_exist= (mysql_get_server_version(mysql) >= 100005);
+ my_bool mysql_roles_exist= (mysql_get_server_version(mysql) >= 80001) && !maria_roles_exist;
+
+ if (mysql_query_with_error_report(mysql, &tableres,
+ "SELECT CONCAT(QUOTE(u.user), '@', QUOTE(u.Host)) AS u "
+ "FROM mysql.user u "
+ " /*!80001 LEFT JOIN mysql.role_edges e "
+ " ON u.user=e.from_user "
+ " AND u.host=e.from_host "
+ " WHERE e.from_user IS NULL */"
+ " /*M!100005 WHERE is_role='N' */"))
+ return 1;
+ while ((row= mysql_fetch_row(tableres)))
+ {
+ if (opt_replace_into)
+ /* Protection against removing the current import user */
+ /* MySQL-8.0 export capability */
+ fprintf(md_result_file,
+ "DELIMITER |\n"
+ "/*M!100101 IF current_user()=\"%s\" THEN\n"
+ " SIGNAL SQLSTATE '45000' SET MYSQL_ERRNO=30001,"
+ " MESSAGE_TEXT=\"Don't remove current user %s'\";\n"
+ "END IF */|\n"
+ "DELIMITER ;\n"
+ "/*!50701 DROP USER IF EXISTS %s */;\n", row[0], row[0], row[0]);
+ if (dump_create_user(row[0]))
+ result= 1;
+ /* if roles exist, defer dumping grants until after roles created */
+ if (maria_roles_exist || mysql_roles_exist)
+ continue;
+ if (dump_grants(row[0]))
+ result= 1;
+ }
+ mysql_free_result(tableres);
+
+ if (!(maria_roles_exist || mysql_roles_exist))
+ goto exit;
+
+ /*
+ Preserve current role active role, in case this dump is imported
+ in the same connection that assumes the active role at the beginning
+ is the same as at the end of the connection. This is so:
+
+ #!/bin/sh
+ (
+ echo "set role special_role; ";
+ cat mysqldump.sql;
+ echo "$dosomethingspecial"
+ ) | mysql -h $host
+
+ doesn't end up with a surprise that the $dosomethingspecial cannot
+ be done because `special_role` isn't active.
+
+ We create a new role for importing that becomes the default admin for new
+ roles. This is because without being a admin on new roles we don't
+ have the necessary privileges to grant users to a created role or to
+ create new admins for the created role.
+
+ At the end of the import the mariadb_dump_import_role is be dropped,
+ which implicitly drops all its admin aspects of the dropped role.
+ This is significantly easier than revoking the ADMIN of each role
+ from the current user.
+ */
+ fputs("SELECT COALESCE(CURRENT_ROLE(),'NONE') into @current_role;\n"
+ "CREATE ROLE IF NOT EXISTS mariadb_dump_import_role;\n"
+ "GRANT mariadb_dump_import_role TO CURRENT_USER();\n"
+ "SET ROLE mariadb_dump_import_role;\n"
+ , md_result_file);
+ /* No show create role yet, MDEV-22311 */
+ /* Roles, with user admins first, then roles they administer, and recurse on that */
+ if (maria_roles_exist && mysql_query_with_error_report(mysql, &tableres,
+ "WITH RECURSIVE create_role_order AS"
+ " (SELECT 1 as n, roles_mapping.*"
+ " FROM mysql.roles_mapping"
+ " JOIN mysql.user USING (user,host)"
+ " WHERE is_role='N'"
+ " AND Admin_option='Y'"
+ " UNION SELECT c.n+1, r.*"
+ " FROM create_role_order c"
+ " JOIN mysql.roles_mapping r ON c.role=r.user"
+ " AND r.host=''"
+ " AND r.Admin_option='Y') "
+ "SELECT QUOTE(ROLE) AS r,"
+ " CONCAT(QUOTE(user),"
+ " IF(HOST='', '', CONCAT('@', QUOTE(HOST)))) AS c,"
+ " Admin_option "
+ "FROM create_role_order ORDER BY n, r, user"))
+ return 1;
+ /*
+ TODO Mysql - misses roles that have no admin or role members.
+ MySQL roles don't require an admin.
+ */
+ if (mysql_roles_exist && mysql_query_with_error_report(mysql, &tableres,
+ "WITH RECURSIVE create_role_order AS"
+ " (SELECT 1 AS n,"
+ " re.*"
+ " FROM mysql.role_edges re"
+ " JOIN mysql.user u ON re.TO_HOST=u.HOST"
+ " AND re.TO_USER = u.USER"
+ " LEFT JOIN mysql.role_edges re2 ON re.TO_USER=re2.FROM_USER"
+ " AND re2.TO_HOST=re2.FROM_HOST"
+ " WHERE re2.FROM_USER IS NULL"
+ " UNION SELECT c.n+1,"
+ " re.*"
+ " FROM create_role_order c"
+ " JOIN mysql.role_edges re ON c.FROM_USER=re.TO_USER"
+ " AND c.FROM_HOST=re.TO_HOST) "
+ "SELECT CONCAT(QUOTE(FROM_USER), '/*!80001 @', QUOTE(FROM_HOST), '*/') AS r,"
+ " CONCAT(QUOTE(TO_USER), IF(n=1, CONCAT('@', QUOTE(TO_HOST)),"
+ " CONCAT('/*!80001 @', QUOTE(TO_HOST), ' */'))) AS u,"
+ " WITH_ADMIN_OPTION "
+ "FROM create_role_order "
+ "ORDER BY n,"
+ " FROM_USER,"
+ " FROM_HOST,"
+ " TO_USER,"
+ " TO_HOST,"
+ " WITH_ADMIN_OPTION"))
+ return 1;
+ while ((row= mysql_fetch_row(tableres)))
+ {
+ /* MySQL-8.0 export capability */
+ if (opt_replace_into)
+ fprintf(md_result_file,
+ "/*!80001 DROP ROLE IF EXISTS %s */;\n", row[0]);
+ fprintf(md_result_file,
+ "/*!80001 CREATE ROLE %s%s */;\n", opt_ignore ? "IF NOT EXISTS " : "", row[0]);
+ /* By default created with current role */
+ fprintf(md_result_file,
+ "%sROLE %s%s WITH ADMIN mariadb_dump_import_role */;\n",
+ opt_replace_into ? "/*M!100103 CREATE OR REPLACE ": "/*M!100005 CREATE ",
+ opt_ignore ? "IF NOT EXISTS " : "", row[0]);
+ fprintf(md_result_file, "/*M!100005 GRANT %s TO %s%s*/;\n",
+ row[0], row[1], (row[2][0] == 'Y') ? " WITH ADMIN OPTION " : "");
+ }
+ mysql_free_result(tableres);
+
+ /* users and their default role */
+ if (maria_roles_exist && mysql_query_with_error_report(mysql, &tableres,
+ "select IF(default_role='', 'NONE', QUOTE(default_role)) as r,"
+ "concat(QUOTE(User), '@', QUOTE(Host)) as u FROM mysql.user "
+ "/*M!100005 WHERE is_role='N' */"))
+ return 1;
+ if (mysql_roles_exist && mysql_query_with_error_report(mysql, &tableres,
+ "SELECT IF(DEFAULT_ROLE_HOST IS NULL, 'NONE', CONCAT(QUOTE(DEFAULT_ROLE_USER),"
+ " '@', QUOTE(DEFAULT_ROLE_HOST))) as r,"
+ " CONCAT(QUOTE(mu.USER),'@',QUOTE(mu.HOST)) as u "
+ "FROM mysql.user mu LEFT JOIN mysql.default_roles using (USER, HOST)"))
+ {
+ mysql_free_result(tableres);
+ return 1;
+ }
+
+ while ((row= mysql_fetch_row(tableres)))
+ {
+ if (dump_grants(row[1]))
+ result= 1;
+ fprintf(md_result_file, "/*M!100005 SET DEFAULT ROLE %s FOR %s */;\n", row[0], row[1]);
+ fprintf(md_result_file, "/*!80001 ALTER USER %s DEFAULT ROLE %s */;\n", row[1], row[0]);
+ }
+ mysql_free_result(tableres);
+
+ if (maria_roles_exist && mysql_query_with_error_report(mysql, &tableres,
+ "SELECT DISTINCT QUOTE(m.role) AS r "
+ " FROM mysql.roles_mapping m"
+ " JOIN mysql.user u ON u.user = m.role"
+ " WHERE is_role='Y'"
+ " AND Admin_option='Y'"
+ " ORDER BY m.role"))
+ return 1;
+ if (mysql_roles_exist && mysql_query_with_error_report(mysql, &tableres,
+ "SELECT DISTINCT CONCAT(QUOTE(FROM_USER),'@', QUOTE(FROM_HOST)) AS r "
+ "FROM mysql.role_edges"))
+ return 1;
+ while ((row= mysql_fetch_row(tableres)))
+ {
+ if (dump_grants(row[0]))
+ result= 1;
+ }
+ mysql_free_result(tableres);
+ /* switch back */
+ fputs("SET ROLE NONE;\n"
+ "DROP ROLE mariadb_dump_import_role;\n"
+ "/*M!100203 EXECUTE IMMEDIATE CONCAT('SET ROLE ', @current_role) */;\n",
+ md_result_file);
+exit:
+
+ return result;
+}
+
+
+/*
+ dump all plugins
+*/
+
+static int dump_all_plugins()
+{
+ MYSQL_ROW row;
+ MYSQL_RES *tableres;
+
+ if (mysql_query_with_error_report(mysql, &tableres, "SHOW PLUGINS"))
+ return 1;
+ /* Name, Status, Type, Library, License */
+ while ((row= mysql_fetch_row(tableres)))
+ {
+ if (strcmp("ACTIVE", row[1]) != 0)
+ continue;
+ /* Should we be skipping builtins? */
+ if (row[3] == NULL)
+ continue;
+ if (opt_replace_into)
+ {
+ fprintf(md_result_file, "/*M!100401 UNINSTALL PLUGIN IF EXIST %s */;\n",
+ row[0]);
+ }
+ fprintf(md_result_file,
+ "INSTALL PLUGIN %s %s SONAME '%s';\n", row[0],
+ opt_ignore ? "/*M!100401 IF NOT EXISTS */" : "", row[3]);
+ }
+ mysql_free_result(tableres);
+
+ return 0;
+}
+
+
+/*
+ dump all udfs
+*/
+
+static int dump_all_udfs()
+{
+ /* we don't support all these types yet, but get prepared if we do */
+ static const char *udf_types[] = {"STRING", "REAL", "INT", "ROW", "DECIMAL", "TIME" };
+ MYSQL_ROW row;
+ MYSQL_RES *tableres;
+ int retresult, result= 0;
+
+ if (mysql_query_with_error_report(mysql, &tableres, "SELECT * FROM mysql.func"))
+ return 1;
+ /* Name, ret, dl, type*/
+ while ((row= mysql_fetch_row(tableres)))
+ {
+ retresult= atoi(row[1]);
+ if (retresult < 0 || array_elements(udf_types) <= (size_t) retresult)
+ {
+ fprintf(stderr, "%s: Error: invalid return type on udf function '%s'\n",
+ my_progname_short, row[0]);
+ result= 1;
+ continue;
+ }
+ if (opt_replace_into)
+ {
+ fprintf(md_result_file, "/*!50701 DROP FUNCTION IF EXISTS %s */;\n",
+ row[0]);
+ }
+ fprintf(md_result_file,
+ "CREATE %s%sFUNCTION %s%s RETURNS %s SONAME '%s';\n",
+ opt_replace_into ? "/*M!100103 OR REPLACE */ ": "",
+ (strcmp("AGGREGATE", row[2])==0 ? "AGGREGATE " : ""),
+ opt_ignore ? "IF NOT EXISTS " : "", row[0], udf_types[retresult], row[2]);
+ }
+ mysql_free_result(tableres);
+
+ return result;
+}
+
+
+/*
+ dump all servers
+*/
+
+static int dump_all_servers()
+{
+ /* No create server yet - MDEV-15696 */
+ MYSQL_ROW row;
+ MYSQL_RES *tableres;
+ MYSQL_FIELD *f;
+ unsigned int num_fields, i;
+ my_bool comma_prepend= 0;
+ const char *qstring;
+
+ if (mysql_query_with_error_report(mysql, &tableres, "SELECT * FROM mysql.servers"))
+ return 1;
+ num_fields= mysql_num_fields(tableres);
+ while ((row= mysql_fetch_row(tableres)))
+ {
+ fprintf(md_result_file,"CREATE %sSERVER %s%s FOREIGN DATA WRAPPER %s OPTIONS (",
+ opt_replace_into ? "/*M!100103 OR REPLACE */ ": "",
+ opt_ignore ? "/*M!100103 IF NOT EXISTS */ " : "", row[0], row[7]);
+ for (i= 1; i < num_fields; i++)
+ {
+ if (i == 7 || row[i][0] == '\0') /* Wrapper or empty string */
+ continue;
+ f= &tableres->fields[i];
+ qstring= (f->type == MYSQL_TYPE_STRING || f->type == MYSQL_TYPE_VAR_STRING) ? "'" : "";
+ fprintf(md_result_file, "%s%s %s%s%s",
+ (comma_prepend ? ", " : ""), f->name, qstring, row[i], qstring);
+ comma_prepend= 1;
+ }
+ fputs(");\n", md_result_file);
+ }
+ mysql_free_result(tableres);
+
+ return 0;
+}
+
+
+/*
+ dump all system statistical tables
+*/
+
+static int dump_all_stats()
+{
+ my_bool prev_no_create_info, prev_opt_replace_into;
+
+ if (mysql_select_db(mysql, "mysql"))
+ {
+ DB_error(mysql, "when selecting the database");
+ return 1; /* If --force */
+ }
+ fprintf(md_result_file,"\nUSE mysql;\n");
+ prev_opt_replace_into= opt_replace_into;
+ opt_replace_into|= !opt_ignore;
+ prev_no_create_info= opt_no_create_info;
+ opt_no_create_info= 1; /* don't overwrite recreate tables */
+ /* EITS added in 10.0.1 */
+ if (mysql_get_server_version(mysql) >= 100001)
+ {
+ dump_table("column_stats", "mysql", NULL, 0);
+ dump_table("index_stats", "mysql", NULL, 0);
+ dump_table("table_stats", "mysql", NULL, 0);
+ }
+ /* Innodb may be disabled */
+ if (!mysql_query(mysql, "show fields from innodb_index_stats"))
+ {
+ MYSQL_RES *tableres= mysql_store_result(mysql);
+ mysql_free_result(tableres);
+ dump_table("innodb_index_stats", "mysql", NULL, 0);
+ dump_table("innodb_table_stats", "mysql", NULL, 0);
+ }
+ opt_no_create_info= prev_no_create_info;
+ opt_replace_into= prev_opt_replace_into;
+ return 0;
+}
+
+
+/*
+ dump all system timezones
+*/
+
+static int dump_all_timezones()
+{
+ my_bool opt_prev_no_create_info, opt_prev_replace_into;
+ if (mysql_select_db(mysql, "mysql"))
+ {
+ DB_error(mysql, "when selecting the database");
+ return 1; /* If --force */
+ }
+ opt_prev_replace_into= opt_replace_into;
+ opt_replace_into|= !opt_ignore;
+ opt_prev_no_create_info= opt_no_create_info;
+ opt_no_create_info= 1;
+ fprintf(md_result_file,"\nUSE mysql;\n");
+ dump_table("time_zone", "mysql", NULL, 0);
+ dump_table("time_zone_name", "mysql", NULL, 0);
+ dump_table("time_zone_leap_second", "mysql", NULL, 0);
+ dump_table("time_zone_transition", "mysql", NULL, 0);
+ dump_table("time_zone_transition_type", "mysql", NULL, 0);
+ opt_no_create_info= opt_prev_no_create_info;
+ opt_replace_into= opt_prev_replace_into;
+ return 0;
+}
+
+
+/*
+ dump all logfile groups and tablespaces
+*/
+
+static int dump_all_tablespaces()
+{
+ return dump_tablespaces(NULL);
+}
+
+static int dump_tablespaces_for_tables(char *db, char **table_names, int tables)
+{
+ 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(&dynamic_where, " AND TABLESPACE_NAME IN ("
+ "SELECT DISTINCT TABLESPACE_NAME FROM"
+ " INFORMATION_SCHEMA.PARTITIONS"
+ " WHERE"
+ " TABLE_SCHEMA='", 256, 1024);
+ 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(&dynamic_where, "'");
+ dynstr_append_checked(&dynamic_where, name_buff);
+ dynstr_append_checked(&dynamic_where, "',");
+ }
+ dynstr_trunc(&dynamic_where, 1);
+ dynstr_append_checked(&dynamic_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)
+{
+ int r;
+ int i;
+
+ init_dynamic_string_checked(&dynamic_where, " AND TABLESPACE_NAME IN ("
+ "SELECT DISTINCT TABLESPACE_NAME FROM"
+ " INFORMATION_SCHEMA.PARTITIONS"
+ " WHERE"
+ " TABLE_SCHEMA IN (", 256, 1024);
+
+ for (i=0 ; databases[i]!=NULL ; i++)
+ {
+ 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(&dynamic_where, "'");
+ dynstr_append_checked(&dynamic_where, db_name_buff);
+ dynstr_append_checked(&dynamic_where, "',");
+ }
+ dynstr_trunc(&dynamic_where, 1);
+ dynstr_append_checked(&dynamic_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;
+}
+
+
+static int dump_tablespaces(char* ts_where)
+{
+ MYSQL_ROW row;
+ MYSQL_RES *tableres;
+ char buf[FN_REFLEN];
+ DYNAMIC_STRING sqlbuf;
+ int first= 0;
+ /*
+ The following are used for parsing the EXTRA field
+ */
+ char extra_format[]= "UNDO_BUFFER_SIZE=";
+ char *ubs;
+ char *endsemi;
+ DBUG_ENTER("dump_tablespaces");
+
+ /*
+ Try to turn off semi-join optimization (if that fails, this is a
+ pre-optimizer_switch server, and the old query plan is ok for us.
+ */
+ mysql_query(mysql, "set optimizer_switch='semijoin=off'");
+
+ init_dynamic_string_checked(&sqlbuf,
+ "SELECT LOGFILE_GROUP_NAME,"
+ " FILE_NAME,"
+ " TOTAL_EXTENTS,"
+ " INITIAL_SIZE,"
+ " ENGINE,"
+ " EXTRA"
+ " FROM INFORMATION_SCHEMA.FILES"
+ " WHERE FILE_TYPE = 'UNDO LOG'"
+ " AND FILE_NAME IS NOT NULL"
+ " AND LOGFILE_GROUP_NAME IS NOT NULL",
+ 256, 1024);
+ if(ts_where)
+ {
+ dynstr_append_checked(&sqlbuf,
+ " AND LOGFILE_GROUP_NAME IN ("
+ "SELECT DISTINCT LOGFILE_GROUP_NAME"
+ " FROM INFORMATION_SCHEMA.FILES"
+ " WHERE FILE_TYPE = 'DATAFILE'"
+ );
+ dynstr_append_checked(&sqlbuf, ts_where);
+ dynstr_append_checked(&sqlbuf, ")");
+ }
+ dynstr_append_checked(&sqlbuf,
+ " GROUP BY LOGFILE_GROUP_NAME, FILE_NAME"
+ ", ENGINE, TOTAL_EXTENTS, INITIAL_SIZE"
+ " ORDER BY LOGFILE_GROUP_NAME");
+
+ if (mysql_query(mysql, sqlbuf.str) ||
+ !(tableres = mysql_store_result(mysql)))
+ {
+ dynstr_free(&sqlbuf);
+ if (mysql_errno(mysql) == ER_BAD_TABLE_ERROR ||
+ mysql_errno(mysql) == ER_BAD_DB_ERROR ||
+ mysql_errno(mysql) == ER_UNKNOWN_TABLE)
+ {
+ fprintf(md_result_file,
+ "\n--\n-- Not dumping tablespaces as no INFORMATION_SCHEMA.FILES"
+ " table on this server\n--\n");
+ check_io(md_result_file);
+ DBUG_RETURN(0);
+ }
+
+ fprintf(stderr, "%s: Error: '%s' when trying to dump tablespaces\n",
+ my_progname_short, mysql_error(mysql));
+ DBUG_RETURN(1);
+ }
+
+ buf[0]= 0;
+ while ((row= mysql_fetch_row(tableres)))
+ {
+ if (strcmp(buf, row[0]) != 0)
+ first= 1;
+ if (first)
+ {
+ print_comment(md_result_file, 0, "\n--\n-- Logfile group: %s\n--\n",
+ fix_for_comment(row[0]));
+
+ fprintf(md_result_file, "\nCREATE");
+ }
+ else
+ {
+ fprintf(md_result_file, "\nALTER");
+ }
+ fprintf(md_result_file,
+ " LOGFILE GROUP %s\n"
+ " ADD UNDOFILE '%s'\n",
+ row[0],
+ row[1]);
+ if (first)
+ {
+ ubs= strstr(row[5],extra_format);
+ if(!ubs)
+ break;
+ ubs+= strlen(extra_format);
+ endsemi= strstr(ubs,";");
+ if(endsemi)
+ endsemi[0]= '\0';
+ fprintf(md_result_file,
+ " UNDO_BUFFER_SIZE %s\n",
+ ubs);
+ }
+ fprintf(md_result_file,
+ " INITIAL_SIZE %s\n"
+ " ENGINE=%s;\n",
+ row[3],
+ row[4]);
+ check_io(md_result_file);
+ if (first)
+ {
+ first= 0;
+ strxmov(buf, row[0], NullS);
+ }
+ }
+ dynstr_free(&sqlbuf);
+ mysql_free_result(tableres);
+ init_dynamic_string_checked(&sqlbuf,
+ "SELECT DISTINCT TABLESPACE_NAME,"
+ " FILE_NAME,"
+ " LOGFILE_GROUP_NAME,"
+ " EXTENT_SIZE,"
+ " INITIAL_SIZE,"
+ " ENGINE"
+ " FROM INFORMATION_SCHEMA.FILES"
+ " WHERE FILE_TYPE = 'DATAFILE'",
+ 256, 1024);
+
+ if(ts_where)
+ dynstr_append_checked(&sqlbuf, ts_where);
+
+ dynstr_append_checked(&sqlbuf, " ORDER BY TABLESPACE_NAME, LOGFILE_GROUP_NAME");
+
+ if (mysql_query_with_error_report(mysql, &tableres, sqlbuf.str))
+ {
+ dynstr_free(&sqlbuf);
+ DBUG_RETURN(1);
+ }
+
+ buf[0]= 0;
+ while ((row= mysql_fetch_row(tableres)))
+ {
+ if (strcmp(buf, row[0]) != 0)
+ first= 1;
+ if (first)
+ {
+ print_comment(md_result_file, 0, "\n--\n-- Tablespace: %s\n--\n",
+ fix_for_comment(row[0]));
+ fprintf(md_result_file, "\nCREATE");
+ }
+ else
+ {
+ fprintf(md_result_file, "\nALTER");
+ }
+ fprintf(md_result_file,
+ " TABLESPACE %s\n"
+ " ADD DATAFILE '%s'\n",
+ row[0],
+ row[1]);
+ if (first)
+ {
+ fprintf(md_result_file,
+ " USE LOGFILE GROUP %s\n"
+ " EXTENT_SIZE %s\n",
+ row[2],
+ row[3]);
+ }
+ fprintf(md_result_file,
+ " INITIAL_SIZE %s\n"
+ " ENGINE=%s;\n",
+ row[4],
+ row[5]);
+ check_io(md_result_file);
+ if (first)
+ {
+ first= 0;
+ strxmov(buf, row[0], NullS);
+ }
+ }
+
+ mysql_free_result(tableres);
+ dynstr_free(&sqlbuf);
+ mysql_query(mysql, "set optimizer_switch=default");
+
+ DBUG_RETURN(0);
+}
+
+
+/* Return 1 if we should copy the database */
+static my_bool include_database(const char *hash_key)
+{
+ return !my_hash_search(&ignore_database, (uchar*) hash_key, strlen(hash_key));
+}
+
+
+static int dump_all_databases()
+{
+ MYSQL_ROW row;
+ MYSQL_RES *tableres;
+ int result=0;
+
+ if (mysql_query_with_error_report(mysql, &tableres, "SHOW DATABASES"))
+ return 1;
+ while ((row= mysql_fetch_row(tableres)))
+ {
+ if (mysql_get_server_version(mysql) >= FIRST_INFORMATION_SCHEMA_VERSION &&
+ !my_strcasecmp(&my_charset_latin1, row[0], INFORMATION_SCHEMA_DB_NAME))
+ continue;
+
+ if (mysql_get_server_version(mysql) >= FIRST_PERFORMANCE_SCHEMA_VERSION &&
+ !my_strcasecmp(&my_charset_latin1, row[0], PERFORMANCE_SCHEMA_DB_NAME))
+ continue;
+
+ if (mysql_get_server_version(mysql) >= FIRST_SYS_SCHEMA_VERSION &&
+ !my_strcasecmp(&my_charset_latin1, row[0], SYS_SCHEMA_DB_NAME))
+ continue;
+
+ if (include_database(row[0]))
+ if (dump_all_tables_in_db(row[0]))
+ result=1;
+ }
+ mysql_free_result(tableres);
+ if (seen_views)
+ {
+ if (mysql_query(mysql, "SHOW DATABASES") ||
+ !(tableres= mysql_store_result(mysql)))
+ {
+ fprintf(stderr, "%s: Error: Couldn't execute 'SHOW DATABASES': %s\n",
+ my_progname_short, mysql_error(mysql));
+ return 1;
+ }
+ while ((row= mysql_fetch_row(tableres)))
+ {
+ if (mysql_get_server_version(mysql) >= FIRST_INFORMATION_SCHEMA_VERSION &&
+ !my_strcasecmp(&my_charset_latin1, row[0], INFORMATION_SCHEMA_DB_NAME))
+ continue;
+
+ if (mysql_get_server_version(mysql) >= FIRST_PERFORMANCE_SCHEMA_VERSION &&
+ !my_strcasecmp(&my_charset_latin1, row[0], PERFORMANCE_SCHEMA_DB_NAME))
+ continue;
+
+ if (mysql_get_server_version(mysql) >= FIRST_SYS_SCHEMA_VERSION &&
+ !my_strcasecmp(&my_charset_latin1, row[0], SYS_SCHEMA_DB_NAME))
+ continue;
+
+ if (include_database(row[0]))
+ if (dump_all_views_in_db(row[0]))
+ result=1;
+ }
+ mysql_free_result(tableres);
+ }
+ return result;
+}
+/* dump_all_databases */
+
+
+static int dump_databases(char **db_names)
+{
+ int result=0;
+ char **db;
+ DBUG_ENTER("dump_databases");
+
+ for (db= db_names ; *db ; db++)
+ {
+ if (dump_all_tables_in_db(*db))
+ result=1;
+ }
+ if (!result && seen_views)
+ {
+ for (db= db_names ; *db ; db++)
+ {
+ if (dump_all_views_in_db(*db))
+ result=1;
+ }
+ }
+ DBUG_RETURN(result);
+} /* dump_databases */
+
+
+/*
+View Specific database initialization.
+
+SYNOPSIS
+ init_dumping_views
+ qdatabase quoted name of the database
+
+RETURN VALUES
+ 0 Success.
+ 1 Failure.
+*/
+int init_dumping_views(char *qdatabase __attribute__((unused)))
+{
+ return 0;
+} /* init_dumping_views */
+
+
+/*
+mysql specific database initialization.
+
+SYNOPSIS
+ init_dumping_mysql_tables
+
+ protections around dumping general/slow query log
+ qdatabase quoted name of the "mysql" database
+
+RETURN VALUES
+ 0 Success.
+ 1 Failure.
+*/
+static int init_dumping_mysql_tables(char *qdatabase)
+{
+ DBUG_ENTER("init_dumping_mysql_tables");
+
+ if (opt_drop_database)
+ fprintf(md_result_file,
+ "\n/*!50106 SET @save_log_output=@@LOG_OUTPUT*/;\n"
+ "/*M!100203 EXECUTE IMMEDIATE IF(@@LOG_OUTPUT='TABLE' AND (@@LOG_SLOW_QUERY=1 OR @@GENERAL_LOG=1),"
+ "\"SET GLOBAL LOG_OUTPUT='NONE'\", \"DO 0\") */;\n");
+
+ DBUG_RETURN(init_dumping_tables(qdatabase));
+}
+
+
+static void dump_first_mysql_tables(char *database)
+{
+ char table_type[NAME_LEN];
+ char ignore_flag;
+ DBUG_ENTER("dump_first_mysql_tables");
+
+ if (!get_table_structure((char *) "general_log",
+ database, table_type, &ignore_flag, NULL) )
+ verbose_msg("-- Warning: get_table_structure() failed with some internal "
+ "error for 'general_log' table\n");
+ if (!get_table_structure((char *) "slow_log",
+ database, table_type, &ignore_flag, NULL) )
+ verbose_msg("-- Warning: get_table_structure() failed with some internal "
+ "error for 'slow_log' table\n");
+ /* general and slow query logs exist now */
+ if (opt_drop_database)
+ fprintf(md_result_file,
+ "\n/*!50106 SET GLOBAL LOG_OUTPUT=@save_log_output*/;\n\n");
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+Table Specific database initialization.
+
+SYNOPSIS
+ init_dumping_tables
+ qdatabase quoted name of the database
+
+RETURN VALUES
+ 0 Success.
+ 1 Failure.
+*/
+
+int init_dumping_tables(char *qdatabase)
+{
+ DBUG_ENTER("init_dumping_tables");
+
+ if (!opt_create_db)
+ {
+ char qbuf[256];
+ MYSQL_ROW row;
+ MYSQL_RES *dbinfo;
+
+ my_snprintf(qbuf, sizeof(qbuf),
+ "SHOW CREATE DATABASE IF NOT EXISTS %s",
+ qdatabase);
+
+ if (mysql_query(mysql, qbuf) || !(dbinfo = mysql_store_result(mysql)))
+ {
+ /* Old server version, dump generic CREATE DATABASE */
+ if (opt_drop_database)
+ fprintf(md_result_file,
+ "\n/*!40000 DROP DATABASE IF EXISTS %s*/;\n",
+ qdatabase);
+ fprintf(md_result_file,
+ "\nCREATE DATABASE /*!32312 IF NOT EXISTS*/ %s;\n",
+ qdatabase);
+ }
+ else
+ {
+ if (opt_drop_database)
+ fprintf(md_result_file,
+ "\n/*!40000 DROP DATABASE IF EXISTS %s*/;\n",
+ qdatabase);
+ row = mysql_fetch_row(dbinfo);
+ if (row[1])
+ {
+ fprintf(md_result_file,"\n%s;\n",row[1]);
+ }
+ mysql_free_result(dbinfo);
+ }
+ }
+ DBUG_RETURN(0);
+} /* init_dumping_tables */
+
+
+static int init_dumping(char *database, int init_func(char*))
+{
+ if (mysql_select_db(mysql, database))
+ {
+ DB_error(mysql, "when selecting the database");
+ return 1; /* If --force */
+ }
+ if (!path && !opt_xml)
+ {
+ if (opt_databases || opt_alldbs)
+ {
+ /*
+ length of table name * 2 (if name contains quotes), 2 quotes and 0
+ */
+ char quoted_database_buf[NAME_LEN*2+3];
+ char *qdatabase= quote_name(database,quoted_database_buf,opt_quoted);
+
+ print_comment(md_result_file, 0,
+ "\n--\n-- Current Database: %s\n--\n",
+ fix_for_comment(qdatabase));
+
+ /* Call the view or table specific function */
+ init_func(qdatabase);
+
+ fprintf(md_result_file,"\nUSE %s;\n", qdatabase);
+ check_io(md_result_file);
+ }
+ }
+ return 0;
+} /* init_dumping */
+
+
+/* Return 1 if we should copy the table */
+
+static my_bool include_table(const uchar *hash_key, size_t len)
+{
+ return ! my_hash_search(&ignore_table, hash_key, len);
+}
+static my_bool ignore_table_data(const uchar *hash_key, size_t len)
+{
+ return my_hash_search(&ignore_data, hash_key, len) != NULL;
+}
+
+
+static int dump_all_tables_in_db(char *database)
+{
+ char *table;
+ uint numrows;
+ char table_buff[NAME_LEN*2+3];
+ char hash_key[2*NAME_LEN+2]; /* "db.tablename" */
+ char *afterdot;
+ my_bool transaction_registry_table_exists= 0;
+ int using_mysql_db= !my_strcasecmp(charset_info, database, "mysql");
+ DBUG_ENTER("dump_all_tables_in_db");
+
+ afterdot= strmov(hash_key, database);
+ *afterdot++= '.';
+
+ if (init_dumping(database, using_mysql_db ? init_dumping_mysql_tables
+ : init_dumping_tables))
+ DBUG_RETURN(1);
+ if (opt_xml)
+ print_xml_tag(md_result_file, "", "\n", "database", "name=", database, NullS);
+
+ if (using_mysql_db)
+ dump_first_mysql_tables(database);
+
+ if (lock_tables)
+ {
+ DYNAMIC_STRING query;
+ init_dynamic_string_checked(&query, "LOCK TABLES ", 256, 1024);
+ for (numrows= 0 ; (table= getTableName(1, DUMP_TABLE_ALL)) ; )
+ {
+ char *end= strmov(afterdot, table);
+ if (include_table((uchar*) hash_key,end - hash_key))
+ {
+ numrows++;
+ dynstr_append_checked(&query, quote_name(table, table_buff, 1));
+ dynstr_append_checked(&query, " READ /*!32311 LOCAL */,");
+ }
+ }
+ if (numrows && mysql_real_query(mysql, query.str, (ulong)query.length-1))
+ {
+ dynstr_free(&query);
+ DB_error(mysql, "when using LOCK TABLES");
+ /* We shall continue here, if --force was given */
+ }
+ dynstr_free(&query); /* Safe to call twice */
+ }
+ if (flush_logs)
+ {
+ if (mysql_refresh(mysql, REFRESH_LOG))
+ DB_error(mysql, "when doing refresh");
+ /* We shall continue here, if --force was given */
+ 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);
+ }
+ }
+
+ if (mysql_get_server_version(mysql) >= FIRST_SEQUENCE_VERSION &&
+ !opt_no_create_info)
+ {
+ // First process sequences
+ while ((table= getTableName(1, DUMP_TABLE_SEQUENCE)))
+ {
+ char *end= strmov(afterdot, table);
+ if (include_table((uchar*) hash_key, end - hash_key))
+ get_sequence_structure(table, database);
+ }
+ }
+ while ((table= getTableName(0, DUMP_TABLE_TABLE)))
+ {
+ char *end= strmov(afterdot, table);
+ if (include_table((uchar*) hash_key, end - hash_key))
+ {
+ dump_table(table, database, (uchar*) hash_key, end - hash_key);
+ my_free(order_by);
+ order_by= 0;
+ if (opt_dump_triggers && mysql_get_server_version(mysql) >= 50009)
+ {
+ if (dump_triggers_for_table(table, database))
+ {
+ if (path)
+ my_fclose(md_result_file, MYF(MY_WME));
+ 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
+ {
+ /*
+ If transaction_registry exists in the 'mysql' database,
+ we should dump the table structure. But we cannot
+ call get_table_structure() here as 'LOCK TABLES' query got executed
+ above on the session and that 'LOCK TABLES' query does not contain
+ 'transaction_registry'. Hence mark the existence of the table here and
+ after 'UNLOCK TABLES' query is executed on the session, get the table
+ structure from server and dump it in the file.
+ */
+ if (using_mysql_db && !my_strcasecmp(charset_info, table, "transaction_registry"))
+ transaction_registry_table_exists= 1;
+ }
+ }
+
+ 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));
+ dump_events_for_db(database);
+ }
+ if (opt_routines && mysql_get_server_version(mysql) >= 50009)
+ {
+ DBUG_PRINT("info", ("Dumping routines for database %s", database));
+ dump_routines_for_db(database);
+ }
+ if (lock_tables)
+ (void) mysql_query_with_error_report(mysql, 0, "UNLOCK TABLES");
+ if (using_mysql_db)
+ {
+ if (transaction_registry_table_exists)
+ {
+ char table_type[NAME_LEN];
+ char ignore_flag;
+ if (!get_table_structure((char *) "transaction_registry",
+ database, table_type, &ignore_flag, NULL) )
+ verbose_msg("-- Warning: get_table_structure() failed with some internal "
+ "error for 'transaction_registry' table\n");
+ }
+ }
+ if (opt_xml)
+ {
+ fputs("</database>\n", md_result_file);
+ check_io(md_result_file);
+ }
+ if (flush_privileges && using_mysql_db)
+ {
+ fprintf(md_result_file,"\n--\n-- Flush Grant Tables \n--\n");
+ fprintf(md_result_file,"\n/*! FLUSH PRIVILEGES */;\n");
+ }
+ DBUG_RETURN(0);
+} /* dump_all_tables_in_db */
+
+
+/*
+ dump structure of views of database
+
+ SYNOPSIS
+ dump_all_views_in_db()
+ database database name
+
+ RETURN
+ 0 OK
+ 1 ERROR
+*/
+
+static my_bool dump_all_views_in_db(char *database)
+{
+ char *table;
+ uint numrows;
+ char table_buff[NAME_LEN*2+3];
+ char hash_key[2*NAME_LEN+2]; /* "db.tablename" */
+ char *afterdot;
+
+ afterdot= strmov(hash_key, database);
+ *afterdot++= '.';
+
+ if (init_dumping(database, init_dumping_views))
+ return 1;
+ if (opt_xml)
+ print_xml_tag(md_result_file, "", "\n", "database", "name=", database, NullS);
+ if (lock_tables)
+ {
+ DYNAMIC_STRING query;
+ init_dynamic_string_checked(&query, "LOCK TABLES ", 256, 1024);
+ for (numrows= 0 ; (table= getTableName(1, DUMP_TABLE_TABLE)); )
+ {
+ char *end= strmov(afterdot, table);
+ if (include_table((uchar*) hash_key,end - hash_key))
+ {
+ numrows++;
+ dynstr_append_checked(&query, quote_name(table, table_buff, 1));
+ dynstr_append_checked(&query, " READ /*!32311 LOCAL */,");
+ }
+ }
+ if (numrows && mysql_real_query(mysql, query.str, (ulong)query.length-1))
+ DB_error(mysql, "when using LOCK TABLES");
+ /* We shall continue here, if --force was given */
+ dynstr_free(&query);
+ }
+ if (flush_logs)
+ {
+ if (mysql_refresh(mysql, REFRESH_LOG))
+ DB_error(mysql, "when doing refresh");
+ /* We shall continue here, if --force was given */
+ else
+ verbose_msg("-- dump_all_views_in_db : logs flushed successfully!\n");
+ }
+ while ((table= getTableName(0, DUMP_TABLE_TABLE)))
+ {
+ char *end= strmov(afterdot, table);
+ if (include_table((uchar*) hash_key, end - hash_key))
+ get_view_structure(table, database);
+ }
+ if (opt_xml)
+ {
+ fputs("</database>\n", md_result_file);
+ check_io(md_result_file);
+ }
+ if (lock_tables)
+ (void) mysql_query_with_error_report(mysql, 0, "UNLOCK TABLES");
+ return 0;
+} /* dump_all_tables_in_db */
+
+
+/*
+ See get_actual_table_name. Used to retrieve the correct table name
+ from the database schema.
+*/
+static char *get_actual_table_name_helper(const char *old_table_name,
+ my_bool case_sensitive,
+ MEM_ROOT *root)
+{
+ char *name= 0;
+ MYSQL_RES *table_res;
+ MYSQL_ROW row;
+ char query[50 + 2*NAME_LEN];
+ char show_name_buff[FN_REFLEN];
+ DBUG_ENTER("get_actual_table_name_helper");
+
+ /* Check memory for quote_for_like() */
+ DBUG_ASSERT(2*sizeof(old_table_name) < sizeof(show_name_buff));
+
+ if (case_sensitive)
+ {
+ DBUG_PRINT("info", ("case sensitive search"));
+ my_snprintf(query, sizeof(query),
+ "SELECT table_name FROM INFORMATION_SCHEMA.TABLES "
+ "WHERE table_schema = DATABASE() AND table_name = %s",
+ quote_for_equal(old_table_name, show_name_buff));
+ }
+ else
+ {
+ DBUG_PRINT("info", ("case insensitive search"));
+ my_snprintf(query, sizeof(query), "SHOW TABLES LIKE %s",
+ quote_for_like(old_table_name, show_name_buff));
+ }
+
+ if (mysql_query_with_error_report(mysql, 0, query))
+ return NullS;
+
+ if ((table_res= mysql_store_result(mysql)))
+ {
+ my_ulonglong num_rows= mysql_num_rows(table_res);
+ if (num_rows > 0)
+ {
+ ulong *lengths;
+ /*
+ Return first row
+ TODO: Return all matching rows
+ */
+ row= mysql_fetch_row(table_res);
+ lengths= mysql_fetch_lengths(table_res);
+ name= strmake_root(root, row[0], lengths[0]);
+ }
+ mysql_free_result(table_res);
+ }
+ DBUG_PRINT("exit", ("new_table_name: %s", name));
+ DBUG_RETURN(name);
+}
+
+/*
+ get_actual_table_name -- executes a SELECT .. FROM I_S.tables to check
+ if the table name given on the command line matches the one in the database.
+ If the table is not found, it falls back to a slower SHOW TABLES LIKE '%s' to
+ get the actual table name from the server.
+
+ We do this because the table name given on the command line may be a
+ different case (e.g. T1 vs t1), but checking this takes a long time
+ when there are many tables present.
+
+ RETURN
+ pointer to the table name
+ 0 if error
+*/
+
+static char *get_actual_table_name(const char *old_table_name,
+ int lower_case_table_names,
+ MEM_ROOT *root)
+{
+ char *name= 0;
+ DBUG_ENTER("get_actual_table_name");
+
+ name= get_actual_table_name_helper(old_table_name, TRUE, root);
+ if (!name && !lower_case_table_names)
+ name= get_actual_table_name_helper(old_table_name, FALSE, root);
+ DBUG_RETURN(name);
+}
+
+/*
+ Retrieve the value for the server system variable lower_case_table_names.
+
+ RETURN
+ 0 case sensitive.
+ > 0 case insensitive
+*/
+static int get_sys_var_lower_case_table_names()
+{
+ int lower_case_table_names = 0;
+ MYSQL_RES *table_res;
+ MYSQL_ROW row;
+ const char *show_var_query = "SHOW VARIABLES LIKE 'lower_case_table_names'";
+ if (mysql_query_with_error_report(mysql, &table_res, show_var_query))
+ return 0; /* In case of error, assume default value of 0 */
+
+ if ((row= mysql_fetch_row(table_res)))
+ {
+ lower_case_table_names= atoi(row[1]);
+ mysql_free_result(table_res);
+ }
+ if (!row)
+ mysql_free_result(table_res);
+ return lower_case_table_names;
+}
+
+
+
+static int dump_selected_tables(char *db, char **table_names, int tables)
+{
+ char table_buff[NAME_LEN*2+3], table_type[NAME_LEN];
+ DYNAMIC_STRING lock_tables_query;
+ char **dump_tables, **pos, **end;
+ int lower_case_table_names;
+ DBUG_ENTER("dump_selected_tables");
+
+ if (init_dumping(db, init_dumping_tables))
+ DBUG_RETURN(1);
+
+ init_alloc_root(PSI_NOT_INSTRUMENTED, &glob_root, 8192, 0, MYF(0));
+ if (!(dump_tables= pos= (char**) alloc_root(&glob_root,
+ tables * sizeof(char *))))
+ die(EX_EOM, "alloc_root failure.");
+
+ /* Figure out how to compare table names. */
+ lower_case_table_names = get_sys_var_lower_case_table_names();
+
+ 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, lower_case_table_names,
+ &glob_root)))
+ {
+ /* Add found table name to lock_tables_query */
+ if (lock_tables)
+ {
+ dynstr_append_checked(&lock_tables_query, quote_name(*pos, table_buff, 1));
+ dynstr_append_checked(&lock_tables_query, " READ /*!32311 LOCAL */,");
+ }
+ pos++;
+ }
+ else
+ {
+ if (!ignore_errors)
+ {
+ dynstr_free(&lock_tables_query);
+ 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 */
+ }
+ }
+ end= pos;
+
+ /* Can't LOCK TABLES in I_S / P_S, so don't try. */
+ if (lock_tables &&
+ !(mysql_get_server_version(mysql) >= FIRST_INFORMATION_SCHEMA_VERSION &&
+ !my_strcasecmp(&my_charset_latin1, db, INFORMATION_SCHEMA_DB_NAME)) &&
+ !(mysql_get_server_version(mysql) >= FIRST_PERFORMANCE_SCHEMA_VERSION &&
+ !my_strcasecmp(&my_charset_latin1, db, PERFORMANCE_SCHEMA_DB_NAME)))
+ {
+ if (mysql_real_query(mysql, lock_tables_query.str,
+ (ulong)lock_tables_query.length-1))
+ {
+ if (!ignore_errors)
+ {
+ dynstr_free(&lock_tables_query);
+ free_root(&glob_root, MYF(0));
+ }
+ DB_error(mysql, "when doing LOCK TABLES");
+ /* We shall countinue here, if --force was given */
+ }
+ }
+ dynstr_free(&lock_tables_query);
+ if (flush_logs)
+ {
+ if (mysql_refresh(mysql, REFRESH_LOG))
+ {
+ if (!ignore_errors)
+ free_root(&glob_root, MYF(0));
+ DB_error(mysql, "when doing refresh");
+ }
+ /* We shall countinue here, if --force was given */
+ else
+ verbose_msg("-- dump_selected_tables : logs flushed successfully!\n");
+ }
+ 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);
+ }
+ }
+
+ if (mysql_get_server_version(mysql) >= FIRST_SEQUENCE_VERSION)
+ {
+ /* Dump Sequence first */
+ for (pos= dump_tables; pos < end; pos++)
+ {
+ DBUG_PRINT("info",("Dumping sequence(?) %s", *pos));
+ if (check_if_ignore_table(*pos, table_type) & IGNORE_SEQUENCE_TABLE)
+ get_sequence_structure(*pos, db);
+ }
+ }
+ /* Dump each selected table */
+ for (pos= dump_tables; pos < end; pos++)
+ {
+ if (check_if_ignore_table(*pos, table_type) & IGNORE_SEQUENCE_TABLE)
+ continue;
+ DBUG_PRINT("info",("Dumping table %s", *pos));
+ dump_table(*pos, db, NULL, 0);
+ if (opt_dump_triggers &&
+ mysql_get_server_version(mysql) >= 50009)
+ {
+ if (dump_triggers_for_table(*pos, db))
+ {
+ 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 */
+ if (seen_views)
+ {
+ for (pos= dump_tables; pos < end; pos++)
+ get_view_structure(*pos, db);
+ }
+ if (opt_events && mysql_get_server_version(mysql) >= 50106)
+ {
+ DBUG_PRINT("info", ("Dumping events for database %s", db));
+ dump_events_for_db(db);
+ }
+ free_root(&glob_root, MYF(0));
+ if (opt_xml)
+ {
+ fputs("</database>\n", md_result_file);
+ check_io(md_result_file);
+ }
+ if (lock_tables)
+ (void) mysql_query_with_error_report(mysql, 0, "UNLOCK TABLES");
+ DBUG_RETURN(0);
+} /* dump_selected_tables */
+
+
+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) ? "-- " : "";
+
+ if (consistent_binlog_pos)
+ {
+ if(!check_consistent_binlog_pos(binlog_pos_file, binlog_pos_offset))
+ 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"))
+ return 1;
+
+ row= mysql_fetch_row(master);
+ if (row && row[0] && row[1])
+ {
+ file= row[0];
+ offset= row[1];
+ }
+ else
+ {
+ mysql_free_result(master);
+ if (!ignore_errors)
+ {
+ /* SHOW MASTER STATUS reports nothing and --force is not enabled */
+ fprintf(stderr, "%s: Error: Binlogging on server not active\n",
+ my_progname_short);
+ maybe_exit(EX_MYSQLERR);
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ if (have_mariadb_gtid && get_gtid_pos(gtid_pos, 1))
+ {
+ mysql_free_result(master);
+ return 1;
+ }
+
+ }
+
+ /* SHOW MASTER STATUS reports file and position */
+ print_comment(md_result_file, 0,
+ "\n--\n-- Position to start replication or point-in-time "
+ "recovery from\n--\n\n");
+ fprintf(md_result_file,
+ "%sCHANGE MASTER TO MASTER_LOG_FILE='%s', MASTER_LOG_POS=%s;\n",
+ (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)
+ mysql_free_result(master);
+
+ return 0;
+}
+
+static int do_stop_slave_sql(MYSQL *mysql_con)
+{
+ MYSQL_RES *slave;
+ MYSQL_ROW row;
+
+ if (mysql_query_with_error_report(mysql_con, &slave,
+ multi_source ?
+ "SHOW ALL SLAVES STATUS" :
+ "SHOW SLAVE STATUS"))
+ return(1);
+
+ /* Loop over all slaves */
+ while ((row= mysql_fetch_row(slave)))
+ {
+ if (row[11 + multi_source])
+ {
+ /* if SLAVE SQL is not running, we don't stop it */
+ if (strcmp(row[11 + multi_source], "No"))
+ {
+ 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);
+ return(0);
+}
+
+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");
+ if (multi_source)
+ fprintf(md_result_file, "STOP ALL SLAVES;\n");
+ else
+ fprintf(md_result_file, "STOP SLAVE;\n");
+ return(0);
+}
+
+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");
+ 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, int use_gtid,
+ int have_mariadb_gtid)
+{
+ MYSQL_RES *UNINIT_VAR(slave);
+ MYSQL_ROW row;
+ const char *comment_prefix=
+ (opt_slave_data == MYSQL_OPT_SLAVE_DATA_COMMENTED_SQL) ? "-- " : "";
+ 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)
+ {
+ /* SHOW SLAVE STATUS reports nothing and --force is not enabled */
+ fprintf(stderr, "%s: Error: Slave not set up\n", my_progname_short);
+ }
+ mysql_free_result(slave);
+ return 1;
+ }
+
+ while ((row= mysql_fetch_row(slave)))
+ {
+ if (multi_source && !set_gtid_done)
+ {
+ char gtid_pos[MAX_GTID_LENGTH];
+ if (have_mariadb_gtid && get_gtid_pos(gtid_pos, 0))
+ {
+ mysql_free_result(slave);
+ 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");
+
+ 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 + 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 + multi_source]);
+ }
+ fprintf(md_result_file,
+ "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);
+ 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,
+ multi_source ?
+ "SHOW ALL SLAVES STATUS" :
+ "SHOW SLAVE STATUS"))
+ DBUG_RETURN(1);
+
+ while ((row= mysql_fetch_row(slave)))
+ {
+ 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 + multi_source], "Yes"))
+ {
+ 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);
+ DBUG_RETURN(error);
+}
+
+
+
+static int do_flush_tables_read_lock(MYSQL *mysql_con)
+{
+ /*
+ We do first a FLUSH TABLES. If a long update is running, the FLUSH TABLES
+ will wait but will not stall the whole mysqld, and when the long update is
+ done the FLUSH TABLES WITH READ LOCK will start and succeed quickly. So,
+ 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,
+ "FLUSH /*!40101 LOCAL */ TABLES") ||
+ mysql_query_with_error_report(mysql_con, 0,
+ "FLUSH TABLES WITH READ LOCK") );
+}
+
+
+static int do_unlock_tables(MYSQL *mysql_con)
+{
+ return mysql_query_with_error_report(mysql_con, 0, "UNLOCK TABLES");
+}
+
+static int get_bin_log_name(MYSQL *mysql_con,
+ char* buff_log_name, uint buff_len)
+{
+ MYSQL_RES *res;
+ MYSQL_ROW row;
+
+ if (mysql_query(mysql_con, "SHOW MASTER STATUS") ||
+ !(res= mysql_store_result(mysql)))
+ return 1;
+
+ if (!(row= mysql_fetch_row(res)))
+ {
+ mysql_free_result(res);
+ return 1;
+ }
+ /*
+ Only one row is returned, and the first column is the name of the
+ active log.
+ */
+ strmake(buff_log_name, row[0], buff_len - 1);
+
+ mysql_free_result(res);
+ return 0;
+}
+
+static int purge_bin_logs_to(MYSQL *mysql_con, char* log_name)
+{
+ DYNAMIC_STRING str;
+ int err;
+ init_dynamic_string_checked(&str, "PURGE BINARY LOGS TO '", 1024, 1024);
+ dynstr_append_checked(&str, log_name);
+ dynstr_append_checked(&str, "'");
+ err = mysql_query_with_error_report(mysql_con, 0, str.str);
+ dynstr_free(&str);
+ return err;
+}
+
+
+static int start_transaction(MYSQL *mysql_con)
+{
+ verbose_msg("-- Starting transaction...\n");
+ /*
+ We use BEGIN for old servers. --single-transaction --master-data will fail
+ on old servers, but that's ok as it was already silently broken (it didn't
+ do a consistent read, so better tell people frankly, with the error).
+
+ We want the first consistent read to be used for all tables to dump so we
+ need the REPEATABLE READ level (not anything lower, for example READ
+ COMMITTED would give one new consistent read per dumped table).
+ */
+ if ((mysql_get_server_version(mysql_con) < 40100) && opt_master_data)
+ {
+ fprintf(stderr, "-- %s: the combination of --single-transaction and "
+ "--master-data requires a MariaDB server version of at least 4.1 "
+ "(current server's version is %s). %s\n",
+ ignore_errors ? "Warning" : "Error",
+ mysql_con->server_version ? mysql_con->server_version : "unknown",
+ ignore_errors ? "Continuing due to --force, backup may not be consistent across all tables!" : "Aborting.");
+ if (!ignore_errors)
+ exit(EX_MYSQLERR);
+ }
+
+ return (mysql_query_with_error_report(mysql_con, 0,
+ "SET SESSION TRANSACTION ISOLATION "
+ "LEVEL REPEATABLE READ") ||
+ mysql_query_with_error_report(mysql_con, 0,
+ "START TRANSACTION "
+ "/*!40100 WITH CONSISTENT SNAPSHOT */"));
+}
+
+
+static ulong find_set(TYPELIB *lib, const char *x, size_t length,
+ char **err_pos, uint *err_len)
+{
+ const char *end= x + length;
+ ulong found= 0;
+ int find;
+ char buff[255];
+
+ *err_pos= 0; /* No error yet */
+ while (end > x && my_isspace(charset_info, end[-1]))
+ end--;
+
+ *err_len= 0;
+ if (x != end)
+ {
+ const char *start= x;
+ for (;;)
+ {
+ const char *pos= start;
+ uint var_len;
+
+ for (; pos != end && *pos != ','; pos++) ;
+ var_len= (uint) (pos - start);
+ strmake(buff, start, MY_MIN(sizeof(buff) - 1, var_len));
+ find= find_type(buff, lib, FIND_TYPE_BASIC);
+ if (find <= 0)
+ {
+ *err_pos= (char*) start;
+ *err_len= var_len;
+ }
+ else
+ found|= 1UL << (find - 1);
+ if (pos == end)
+ break;
+ start= pos + 1;
+ }
+ }
+ return found;
+}
+
+
+/* Print a value with a prefix on file */
+static void print_value(FILE *file, MYSQL_RES *result, MYSQL_ROW row,
+ const char *prefix, const char *name,
+ int string_value)
+{
+ MYSQL_FIELD *field;
+ mysql_field_seek(result, 0);
+
+ for ( ; (field= mysql_fetch_field(result)) ; row++)
+ {
+ if (!strcmp(field->name,name))
+ {
+ if (row[0] && row[0][0] && strcmp(row[0],"0")) /* Skip default */
+ {
+ fputc(' ',file);
+ fputs(prefix, file);
+ if (string_value)
+ unescape(file,row[0], strlen(row[0]));
+ else
+ fputs(row[0], file);
+ check_io(file);
+ return;
+ }
+ }
+ }
+ return; /* This shouldn't happen */
+} /* print_value */
+
+
+/*
+ SYNOPSIS
+
+ Check if we the table is one of the table types that should be ignored:
+ MRG_ISAM, MRG_MYISAM, if opt_delayed, if that table supports delayed inserts.
+ If the table should be altogether ignored, it returns a TRUE, FALSE if it
+ should not be ignored. If the user has selected to use INSERT DELAYED, it
+ sets the value of the bool pointer supports_delayed_inserts to 0 if not
+ supported, 1 if it is supported.
+
+ ARGS
+
+ check_if_ignore_table()
+ table_name Table name to check
+ table_type Type of table
+
+ GLOBAL VARIABLES
+ mysql MySQL connection
+ verbose Write warning messages
+
+ RETURN
+ char (bit value) See IGNORE_ values at top
+*/
+
+char check_if_ignore_table(const char *table_name, char *table_type)
+{
+ char result= IGNORE_NONE;
+ char buff[FN_REFLEN+80], show_name_buff[FN_REFLEN];
+ MYSQL_RES *res= NULL;
+ MYSQL_ROW row;
+ DBUG_ENTER("check_if_ignore_table");
+
+ /* Check memory for quote_for_like() */
+ DBUG_ASSERT(2*sizeof(table_name) < sizeof(show_name_buff));
+ my_snprintf(buff, sizeof(buff),
+ "SELECT engine, table_type FROM INFORMATION_SCHEMA.TABLES "
+ "WHERE table_schema = DATABASE() AND table_name = %s",
+ quote_for_equal(table_name, show_name_buff));
+ if (mysql_query_with_error_report(mysql, &res, buff))
+ {
+ if (mysql_errno(mysql) != ER_PARSE_ERROR)
+ { /* If old MySQL version */
+ verbose_msg("-- Warning: Couldn't get status information for "
+ "table %s (%s)\n", table_name, mysql_error(mysql));
+ DBUG_RETURN(result); /* assume table is ok */
+ }
+ }
+ if (!(row= mysql_fetch_row(res)))
+ {
+ fprintf(stderr,
+ "Error: Couldn't read status information for table %s (%s)\n",
+ table_name, mysql_error(mysql));
+ mysql_free_result(res);
+ DBUG_RETURN(result); /* assume table is ok */
+ }
+ if (!(row[0]))
+ strmake(table_type, "VIEW", NAME_LEN-1);
+ else
+ {
+ /*
+ If the table type matches any of these, we do support delayed inserts.
+ Note: we do not want to skip dumping this table if if is not one of
+ these types, but we do want to use delayed inserts in the dump if
+ the table type is _NOT_ one of these types
+ */
+ strmake(table_type, row[0], NAME_LEN-1);
+ if (opt_delayed)
+ {
+ if (strcmp(table_type,"MyISAM") &&
+ strcmp(table_type,"ISAM") &&
+ strcmp(table_type,"ARCHIVE") &&
+ strcmp(table_type,"HEAP") &&
+ strcmp(table_type,"MEMORY"))
+ result= IGNORE_INSERT_DELAYED;
+ }
+ if (!strcmp(row[1],"SEQUENCE"))
+ result|= IGNORE_SEQUENCE_TABLE;
+
+ if (!strcmp(table_type, "S3"))
+ result|= IGNORE_S3_TABLE;
+
+ /*
+ If these two types, we do want to skip dumping the table
+ */
+ 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);
+}
+
+
+/*
+ Get string of comma-separated primary key field names
+
+ SYNOPSIS
+ char *primary_key_fields(const char *table_name)
+ RETURNS pointer to allocated buffer (must be freed by caller)
+ table_name quoted table name
+
+ DESCRIPTION
+ Use SHOW KEYS FROM table_name, allocate a buffer to hold the
+ field names, and then build that string and return the pointer
+ to that buffer.
+
+ Returns NULL if there is no PRIMARY or UNIQUE key on the table,
+ or if there is some failure. It is better to continue to dump
+ the table unsorted, rather than exit without dumping the data.
+*/
+
+static char *primary_key_fields(const char *table_name)
+{
+ MYSQL_RES *res= NULL;
+ MYSQL_ROW row;
+ /* SHOW KEYS FROM + table name * 2 (escaped) + 2 quotes + \0 */
+ char show_keys_buff[15 + NAME_LEN * 2 + 3];
+ size_t result_length= 0;
+ char *result= 0;
+ char buff[NAME_LEN * 2 + 3];
+ char *quoted_field;
+
+ my_snprintf(show_keys_buff, sizeof(show_keys_buff),
+ "SHOW KEYS FROM %s", table_name);
+ if (mysql_query(mysql, show_keys_buff) ||
+ !(res= mysql_store_result(mysql)))
+ {
+ fprintf(stderr, "Warning: Couldn't read keys from table %s;"
+ " records are NOT sorted (%s)\n",
+ table_name, mysql_error(mysql));
+ /* Don't exit, because it's better to print out unsorted records */
+ goto cleanup;
+ }
+
+ /*
+ * Figure out the length of the ORDER BY clause result.
+ * Note that SHOW KEYS is ordered: a PRIMARY key is always the first
+ * row, and UNIQUE keys come before others. So we only need to check
+ * the first key, not all keys.
+ */
+ if ((row= mysql_fetch_row(res)) && atoi(row[1]) == 0)
+ {
+ /* Key is unique */
+ do
+ {
+ quoted_field= quote_name(row[4], buff, 0);
+ result_length+= strlen(quoted_field) + 1; /* + 1 for ',' or \0 */
+ } while ((row= mysql_fetch_row(res)) && atoi(row[3]) > 1);
+ }
+
+ /* Build the ORDER BY clause result */
+ if (result_length)
+ {
+ char *end;
+ /* result (terminating \0 is already in result_length) */
+ result= my_malloc(PSI_NOT_INSTRUMENTED, result_length + 10, MYF(MY_WME));
+ if (!result)
+ {
+ fprintf(stderr, "Error: Not enough memory to store ORDER BY clause\n");
+ goto cleanup;
+ }
+ mysql_data_seek(res, 0);
+ row= mysql_fetch_row(res);
+ quoted_field= quote_name(row[4], buff, 0);
+ end= strmov(result, quoted_field);
+ while ((row= mysql_fetch_row(res)) && atoi(row[3]) > 1)
+ {
+ quoted_field= quote_name(row[4], buff, 0);
+ end= strxmov(end, ",", quoted_field, NullS);
+ }
+ }
+
+cleanup:
+ if (res)
+ mysql_free_result(res);
+
+ return result;
+}
+
+
+/*
+ Replace a substring
+
+ SYNOPSIS
+ replace
+ ds_str The string to search and perform the replace in
+ search_str The string to search for
+ search_len Length of the string to search for
+ replace_str The string to replace with
+ replace_len Length of the string to replace with
+
+ RETURN
+ 0 String replaced
+ 1 Could not find search_str in str
+*/
+
+static int replace(DYNAMIC_STRING *ds_str,
+ const char *search_str, ulong search_len,
+ const char *replace_str, ulong replace_len)
+{
+ DYNAMIC_STRING ds_tmp;
+ const char *start= strstr(ds_str->str, search_str);
+ if (!start)
+ return 1;
+ init_dynamic_string_checked(&ds_tmp, "",
+ ds_str->length + replace_len, 256);
+ dynstr_append_mem_checked(&ds_tmp, ds_str->str, (uint)(start - ds_str->str));
+ dynstr_append_mem_checked(&ds_tmp, replace_str, replace_len);
+ dynstr_append_checked(&ds_tmp, start + search_len);
+ dynstr_set_checked(ds_str, ds_tmp.str);
+ dynstr_free(&ds_tmp);
+ return 0;
+}
+
+
+/*
+ Getting VIEW structure
+
+ SYNOPSIS
+ get_view_structure()
+ table view name
+ db db name
+
+ RETURN
+ 0 OK
+ 1 ERROR
+*/
+
+static my_bool get_view_structure(char *table, char* db)
+{
+ MYSQL_RES *table_res;
+ MYSQL_ROW row;
+ MYSQL_FIELD *field;
+ char *result_table, *opt_quoted_table;
+ char table_buff[NAME_LEN*2+3];
+ char table_buff2[NAME_LEN*2+3];
+ char query[QUERY_LENGTH];
+ FILE *sql_file= md_result_file;
+ DBUG_ENTER("get_view_structure");
+
+ if (opt_no_create_info) /* Don't write table creation info */
+ DBUG_RETURN(0);
+
+ verbose_msg("-- Retrieving view structure for table %s...\n", table);
+
+#ifdef NOT_REALLY_USED_YET
+ dynstr_append_checked(&insert_pat, "SET SQL_QUOTE_SHOW_CREATE=");
+ dynstr_append_checked(&insert_pat, (opt_quoted || opt_keywords)? "1":"0");
+#endif
+
+ result_table= quote_name(table, table_buff, 1);
+ opt_quoted_table= quote_name(table, table_buff2, 0);
+
+ if (switch_character_set_results(mysql, "binary"))
+ DBUG_RETURN(1);
+
+ my_snprintf(query, sizeof(query), "SHOW CREATE TABLE %s", result_table);
+
+ if (mysql_query_with_error_report(mysql, &table_res, query))
+ {
+ switch_character_set_results(mysql, default_charset);
+ DBUG_RETURN(0);
+ }
+
+ /* Check if this is a view */
+ field= mysql_fetch_field_direct(table_res, 0);
+ if (strcmp(field->name, "View") != 0)
+ {
+ mysql_free_result(table_res);
+ switch_character_set_results(mysql, default_charset);
+ verbose_msg("-- It's base table, skipped\n");
+ DBUG_RETURN(0);
+ }
+
+ /* If requested, open separate .sql file for this view */
+ if (path)
+ {
+ if (!(sql_file= open_sql_file_for_table(table, O_WRONLY)))
+ {
+ mysql_free_result(table_res);
+ DBUG_RETURN(1);
+ }
+ write_header(sql_file, db);
+ }
+
+ print_comment(sql_file, 0,
+ "\n--\n-- Final view structure for view %s\n--\n\n",
+ fix_for_comment(result_table));
+
+ /* View might not exist if this view was dumped with --tab. */
+ fprintf(sql_file, "/*!50001 DROP VIEW IF EXISTS %s*/;\n", opt_quoted_table);
+
+ my_snprintf(query, sizeof(query),
+ "SELECT CHECK_OPTION, DEFINER, SECURITY_TYPE, "
+ " CHARACTER_SET_CLIENT, COLLATION_CONNECTION "
+ "FROM information_schema.views "
+ "WHERE table_name=\"%s\" AND table_schema=\"%s\"", table, db);
+
+ if (mysql_query(mysql, query))
+ {
+ /*
+ Use the raw output from SHOW CREATE TABLE if
+ information_schema query fails.
+ */
+ row= mysql_fetch_row(table_res);
+ fprintf(sql_file, "/*!50001 %s */;\n", row[1]);
+ check_io(sql_file);
+ mysql_free_result(table_res);
+ }
+ else
+ {
+ char *ptr;
+ ulong *lengths;
+ char search_buf[256], replace_buf[256];
+ ulong search_len, replace_len;
+ DYNAMIC_STRING ds_view;
+
+ /* Save the result of SHOW CREATE TABLE in ds_view */
+ row= mysql_fetch_row(table_res);
+ lengths= mysql_fetch_lengths(table_res);
+ init_dynamic_string_checked(&ds_view, row[1], lengths[1] + 1, 1024);
+ mysql_free_result(table_res);
+
+ /* Get the result from "select ... information_schema" */
+ if (!(table_res= mysql_store_result(mysql)) ||
+ !(row= mysql_fetch_row(table_res)))
+ {
+ if (table_res)
+ mysql_free_result(table_res);
+ dynstr_free(&ds_view);
+ DB_error(mysql, "when trying to save the result of SHOW CREATE TABLE in ds_view.");
+ DBUG_RETURN(1);
+ }
+
+ lengths= mysql_fetch_lengths(table_res);
+
+ /*
+ "WITH %s CHECK OPTION" is available from 5.0.2
+ Surround it with !50002 comments
+ */
+ if (strcmp(row[0], "NONE"))
+ {
+
+ ptr= search_buf;
+ search_len= (ulong)(strxmov(ptr, "WITH ", row[0],
+ " CHECK OPTION", NullS) - ptr);
+ ptr= replace_buf;
+ replace_len=(ulong)(strxmov(ptr, "*/\n/*!50002 WITH ", row[0],
+ " CHECK OPTION", NullS) - ptr);
+ replace(&ds_view, search_buf, search_len, replace_buf, replace_len);
+ }
+
+ /*
+ "DEFINER=%s SQL SECURITY %s" is available from 5.0.13
+ Surround it with !50013 comments
+ */
+ {
+ size_t user_name_len;
+ char user_name_str[USERNAME_LENGTH + 1];
+ char quoted_user_name_str[USERNAME_LENGTH * 2 + 3];
+ size_t host_name_len;
+ char host_name_str[HOSTNAME_LENGTH + 1];
+ char quoted_host_name_str[HOSTNAME_LENGTH * 2 + 3];
+
+ parse_user(row[1], lengths[1], user_name_str, &user_name_len,
+ host_name_str, &host_name_len);
+
+ ptr= search_buf;
+ search_len=
+ (ulong)(strxmov(ptr, "DEFINER=",
+ quote_name(user_name_str, quoted_user_name_str, FALSE),
+ "@",
+ quote_name(host_name_str, quoted_host_name_str, FALSE),
+ " SQL SECURITY ", row[2], NullS) - ptr);
+ ptr= replace_buf;
+ replace_len=
+ (ulong)(strxmov(ptr, "*/\n/*!50013 DEFINER=",
+ quote_name(user_name_str, quoted_user_name_str, FALSE),
+ "@",
+ quote_name(host_name_str, quoted_host_name_str, FALSE),
+ " SQL SECURITY ", row[2],
+ " */\n/*!50001", NullS) - ptr);
+ replace(&ds_view, search_buf, search_len, replace_buf, replace_len);
+ }
+
+ /* Dump view structure to file */
+
+ fprintf(sql_file,
+ "/*!50001 SET @saved_cs_client = @@character_set_client */;\n"
+ "/*!50001 SET @saved_cs_results = @@character_set_results */;\n"
+ "/*!50001 SET @saved_col_connection = @@collation_connection */;\n"
+ "/*!50001 SET character_set_client = %s */;\n"
+ "/*!50001 SET character_set_results = %s */;\n"
+ "/*!50001 SET collation_connection = %s */;\n"
+ "/*!50001 %s */;\n"
+ "/*!50001 SET character_set_client = @saved_cs_client */;\n"
+ "/*!50001 SET character_set_results = @saved_cs_results */;\n"
+ "/*!50001 SET collation_connection = @saved_col_connection */;\n",
+ (const char *) row[3],
+ (const char *) row[3],
+ (const char *) row[4],
+ (const char *) ds_view.str);
+
+ check_io(sql_file);
+ mysql_free_result(table_res);
+ dynstr_free(&ds_view);
+ }
+
+ switch_character_set_results(mysql, default_charset);
+
+ /* If a separate .sql file was opened, close it now */
+ if (sql_file != md_result_file)
+ {
+ fputs("\n", sql_file);
+ write_footer(sql_file);
+ my_fclose(sql_file, MYF(MY_WME));
+ }
+ DBUG_RETURN(0);
+}
+
+/*
+ The following functions are wrappers for the dynamic string functions
+ and if they fail, the wrappers will terminate the current process.
+*/
+
+#define DYNAMIC_STR_ERROR_MSG "Couldn't perform DYNAMIC_STRING operation"
+
+static void init_dynamic_string_checked(DYNAMIC_STRING *str, const char *init_str,
+ size_t init_alloc, size_t alloc_increment)
+{
+ if (init_dynamic_string(str, init_str, init_alloc, alloc_increment))
+ die(EX_MYSQLERR, DYNAMIC_STR_ERROR_MSG);
+}
+
+static void dynstr_append_checked(DYNAMIC_STRING* dest, const char* src)
+{
+ if (dynstr_append(dest, src))
+ die(EX_MYSQLERR, DYNAMIC_STR_ERROR_MSG);
+}
+
+static void dynstr_set_checked(DYNAMIC_STRING *str, const char *init_str)
+{
+ if (dynstr_set(str, init_str))
+ die(EX_MYSQLERR, DYNAMIC_STR_ERROR_MSG);
+}
+
+static void dynstr_append_mem_checked(DYNAMIC_STRING *str, const char *append,
+ uint length)
+{
+ if (dynstr_append_mem(str, append, length))
+ die(EX_MYSQLERR, DYNAMIC_STR_ERROR_MSG);
+}
+
+static void dynstr_realloc_checked(DYNAMIC_STRING *str, ulong additional_size)
+{
+ if (dynstr_realloc(str, additional_size))
+ die(EX_MYSQLERR, DYNAMIC_STR_ERROR_MSG);
+}
+
+
+int main(int argc, char **argv)
+{
+ char query[48];
+ 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 */
+ compatible_mode_normal_str[0]= 0;
+ default_charset= (char *)mysql_universal_client_charset;
+
+ exit_code= get_options(&argc, &argv);
+ if (exit_code)
+ {
+ free_resources();
+ exit(exit_code);
+ }
+ sf_leaking_memory=0; /* from now on we cleanup properly */
+
+ /*
+ Disable comments in xml mode if 'comments' option is not explicitly used.
+ */
+ if (opt_xml && !opt_comments_used)
+ opt_comments= 0;
+
+ if (log_error_file)
+ {
+ if(!(stderror_file= freopen(log_error_file, "a+", stderr)))
+ {
+ free_resources();
+ exit(EX_MYSQLERR);
+ }
+ }
+
+ if (connect_to_db(current_host, current_user, opt_password))
+ {
+ free_resources();
+ exit(EX_MYSQLERR);
+ }
+ if (!path)
+ write_header(md_result_file, *argv);
+
+ /* Set MAX_STATEMENT_TIME to 0 unless set in client */
+ my_snprintf(query, sizeof(query), "/*!100100 SET @@MAX_STATEMENT_TIME=%f */", opt_max_statement_time);
+ mysql_query(mysql, query);
+
+ /* Set server side timeout between client commands to server compiled-in default */
+ mysql_query(mysql, "/*!100100 SET WAIT_TIMEOUT=DEFAULT */");
+
+ /* 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;
+
+ if (opt_single_transaction && opt_master_data)
+ {
+ /* See if we can avoid FLUSH TABLES WITH READ LOCK (MariaDB 5.3+). */
+ consistent_binlog_pos= check_consistent_binlog_pos(NULL, NULL);
+ }
+
+ if ((opt_lock_all_tables || (opt_master_data && !consistent_binlog_pos) ||
+ (opt_single_transaction && flush_logs)) &&
+ do_flush_tables_read_lock(mysql))
+ goto err;
+
+ /*
+ Flush logs before starting transaction since
+ this causes implicit commit starting mysql-5.5.
+ */
+ if (opt_lock_all_tables || opt_master_data ||
+ (opt_single_transaction && flush_logs) ||
+ opt_delete_master_logs)
+ {
+ if (flush_logs || opt_delete_master_logs)
+ {
+ if (mysql_refresh(mysql, REFRESH_LOG))
+ {
+ fprintf(stderr,
+ "Flush logs or delete master logs failure in server \n");
+ first_error= EX_MYSQLERR;
+ goto err;
+ }
+ verbose_msg("-- main : logs flushed successfully!\n");
+ }
+
+ /* Not anymore! That would not be sensible. */
+ flush_logs= 0;
+ }
+
+ if (opt_delete_master_logs)
+ {
+ if (get_bin_log_name(mysql, bin_log_name, sizeof(bin_log_name)))
+ goto err;
+ }
+
+ if (opt_single_transaction && start_transaction(mysql))
+ goto err;
+
+ /* 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,
+ have_mariadb_gtid, opt_use_gtid))
+ goto err;
+ 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;
+
+ if (opt_alltspcs)
+ dump_all_tablespaces();
+
+ if (extended_insert)
+ init_dynamic_string_checked(&extended_row, "", 1024, 1024);
+
+ if (opt_alldbs)
+ {
+ if (!opt_alltspcs && !opt_notspcs)
+ dump_all_tablespaces();
+ dump_all_databases();
+ }
+ else
+ {
+ // Check all arguments meet length condition. Currently database and table
+ // names are limited to NAME_LEN bytes and stack-based buffers assumes
+ // that escaped name will be not longer than NAME_LEN*2 + 2 bytes long.
+ int argument;
+ for (argument= 0; argument < argc; argument++)
+ {
+ size_t argument_length= strlen(argv[argument]);
+ if (argument_length > NAME_LEN)
+ {
+ die(EX_CONSCHECK, "[ERROR] Argument '%s' is too long, it cannot be "
+ "name for any table or database.\n", argv[argument]);
+ }
+ }
+
+ if (argc > 1 && !opt_databases)
+ {
+ /* Only one database and selected table(s) */
+ if (!opt_alltspcs && !opt_notspcs)
+ dump_tablespaces_for_tables(*argv, (argv + 1), (argc - 1));
+ dump_selected_tables(*argv, (argv + 1), (argc - 1));
+ }
+ else if (argc > 0)
+ {
+ /* One or more databases, all tables */
+ if (!opt_alltspcs && !opt_notspcs)
+ dump_tablespaces_for_databases(argv);
+ dump_databases(argv);
+ }
+ }
+
+ if (opt_system & OPT_SYSTEM_PLUGINS)
+ dump_all_plugins();
+
+ if (opt_system & OPT_SYSTEM_USERS)
+ dump_all_users_roles_and_grants();
+
+ if (opt_system & OPT_SYSTEM_UDFS)
+ dump_all_udfs();
+
+ if (opt_system & OPT_SYSTEM_SERVERS)
+ dump_all_servers();
+
+ /* These must be last as they explicitly change the current database to mysql */
+ if (opt_system & OPT_SYSTEM_STATS)
+ dump_all_stats();
+
+ if (opt_system & OPT_SYSTEM_TIMEZONES)
+ dump_all_timezones();
+
+ /* add 'START SLAVE' to end of dump */
+ if (opt_slave_apply && add_slave_statements())
+ goto err;
+
+ /* ensure dumped data flushed */
+ if (md_result_file && fflush(md_result_file))
+ {
+ if (!first_error)
+ first_error= EX_MYSQLERR;
+ goto err;
+ }
+ /* everything successful, purge the old logs files */
+ if (opt_delete_master_logs && purge_bin_logs_to(mysql, bin_log_name))
+ goto err;
+
+ /*
+ No reason to explicitly COMMIT the transaction, neither to explicitly
+ UNLOCK TABLES: these will be automatically be done by the server when we
+ disconnect now. Saves some code here, some network trips, adds nothing to
+ server.
+ */
+err:
+ /* if --dump-slave , start the slave sql thread */
+ if (opt_slave_data)
+ do_start_slave_sql(mysql);
+
+ dbDisconnect(current_host);
+ if (!path)
+ write_footer(md_result_file);
+ free_resources();
+
+ if (stderror_file)
+ fclose(stderror_file);
+
+ return(first_error);
+} /* main */