/*
   Copyright (c) 2000, 2013, Oracle and/or its affiliates.
   Copyright (c) 2010, 2024, 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 VER "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 */
#include "connection_pool.h"
/* 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_update_history= 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=CREATE_TYPELIB_FOR(opt_system_type_values);
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=0;
static DYNAMIC_STRING insert_pat, select_field_names,
                      select_field_names_for_header, insert_field_names;
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_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;
static uint opt_parallel= 0;
static char *opt_dir;

/**
 A flag to indicate that backup uses multiple files for output.

 Usual backup outputs backup to stdout, or to a single file.

 However, using  --tab and --dir will produce multiple files per table
 (afile with extension ".sql" containing DDL and file with extension ".txt" containing
 tab-separated data).
*/
static bool multi_file_output;

/*
  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= CREATE_TYPELIB_FOR(compatible_mode_names);

#define MED_ENGINES "MRG_MyISAM, MRG_ISAM, CONNECT, OQGRAPH, SPIDER, VP, FEDERATED"

static HASH ignore_table, ignore_data;

static HASH ignore_database;

static async_pool::connection_pool connection_pool;

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", 0, "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", 0, "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", 0, "Add locks around INSERT statements.",
   &opt_lock, &opt_lock, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
  {"allow-keywords", 0,
   "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", 0,
   "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", 0,
   "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", 0,
   "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", 0,
   "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", 0, "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", 0, "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", 0, "Insert rows with INSERT DELAYED.",
   &opt_delayed, &opt_delayed, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"delete-master-logs", 0,
   "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},
  {"dir", 'D',
   "directory to store the backup. Table data is stored in tab-separated file, similar to --tab option,"
   "in a subdirectory with database name.",
   &opt_dir, &opt_dir, 0,
   GET_STR, REQUIRED_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", 0, "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). Use also --update-history if "
   "upgrading to MariaDB 11.5 or newer from a version before 11.5",
   &opt_dump_history, &opt_dump_history, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"update-history", OPT_UPDATE_HISTORY,
   "Update row_end history timestamp to support dates up to year 2106. "
   "This option will also enable tz-utc. "
   "Should be used when upgrading to MariaDB 11.5 or above.",
   &opt_update_history, &opt_update_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", 0,
   "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", 0,
   "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", 0,
   "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", 0,
   "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", 0, "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", 0, "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", 0,
   "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", 0, "Insert rows with INSERT IGNORE.",
   &opt_ignore, &opt_ignore, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
  {"lines-terminated-by", 0,
   "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", 0, "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", 0,
   "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, 0, 1024, 0},
  {"max-statement-time", 0,
   "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", 0,
   "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, 0, 1024, 0},
  {"no-autocommit", 0,
   "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", 0,
   "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},
  {"parallel", 'j', "Number of dump table jobs executed in parallel (only with --tab option)",
   &opt_parallel, &opt_parallel, 0, GET_UINT, REQUIRED_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", 0, "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", 0,
   "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", 0,
   "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", 0, "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", 0, "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", 0,
   "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", 0, "Directory for client-side plugins.",
   &opt_plugin_dir, &opt_plugin_dir, 0,
   GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
  {"default_auth", 0, "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 ensure_out_dir_exists(const char *db);
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 *, ...);


static inline int cmp_database(const char *a, const char *b)
{
  return my_strcasecmp_latin1(a, b);
}


static inline int cmp_table(const char *a, const char *b)
{
  return my_strcasecmp_latin1(a, b);
}


/*
  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 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
  {
    fprintf(sql_file, "/*M!999999\\- enable the sandbox mode */ \n");
    if (!opt_compact)
    {
      print_comment(sql_file, 0, "-- MariaDB dump %s-%s, for %s (%s)\n--\n",
                    VER, 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));

      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 (!multi_file_output)
      {
        /* 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"
              "/*M!100616 SET @OLD_NOTE_VERBOSITY=@@NOTE_VERBOSITY, NOTE_VERBOSITY=0 */;\n",
              multi_file_output?"":"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 (!multi_file_output)
    {
      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,
            "/*M!100616 SET NOTE_VERBOSITY=@OLD_NOTE_VERBOSITY */;\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 != 0 && opt_dir != 0)
  {
    fprintf(stderr, "%s: Options --tab and --dir are mutually exclusive\n",
      my_progname_short);
    return(EX_USAGE);
  }

  multi_file_output= path != 0 || opt_dir != 0;

  if (multi_file_output)
  {
    const char *outdir= path ? path : opt_dir;
    int stat_err;
    struct stat st;
    if ((stat_err = stat(outdir, &st)) != 0 || (st.st_mode & S_IFDIR) == 0)
    {
      fprintf(stderr,
        "%s: Path '%s' specified by option '%s' %s\n",
        my_progname_short, outdir, path ? "--tab" : "--dir",
        stat_err?"does not exist":"is not a directory");
      return(EX_CONSCHECK);
    }
  }

  if (!multi_file_output && (enclosed || opt_enclosed || escaped || lines_terminated ||
                fields_terminated))
  {
    fprintf(stderr,
            "%s: You must use option --tab or --dir with --fields-...\n", my_progname_short);
    return(EX_USAGE);
  }
  if (!multi_file_output && opt_header)
  {
    fprintf(stderr,
            "%s: You must use option --tab or --dir 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_delete_master_logs= 0;
    if (opt_slave_data != MYSQL_OPT_SLAVE_DATA_COMMENTED_SQL)
      opt_master_data= 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;
  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 && multi_file_output)
  {
    fprintf(stderr, "%s: --xml can't be used with --tab or --dir.\n", my_progname_short);
    return(EX_USAGE);
  }
  if (opt_dump_history)
  {
    if (opt_update_history)
    {
      /* dump history requires timezone "+00:00" */
      opt_tz_utc= 1;
    }
    if (opt_xml)
    {
      fprintf(stderr, "%s: --xml can't be used with --dump-history.\n",
              my_progname_short);
      return(EX_USAGE);
    }
    if (opt_replace_into)
    {
      fprintf(stderr, "%s: --dump-history can't be used with --replace.\n",
              my_progname_short);
      return(EX_USAGE);
    }
    if (opt_asof_timestamp)
    {
      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)
{
  DBUG_ASSERT(mysql_con);
  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 *db, const char* table, int flags)
{
  FILE* res;
  char filename[FN_REFLEN], tmp_path[FN_REFLEN];
  char out_dir_buf[FN_REFLEN];

  char *out_dir= path;
  if (opt_dir)
  {
    out_dir= out_dir_buf;
    my_snprintf(out_dir_buf, sizeof(out_dir_buf), "%s/%s", opt_dir, db);
  }

  convert_dirname(tmp_path, out_dir, 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 (first_error && mysql)
  {
    connection_pool.for_each_connection(
        [](MYSQL *c) { mysql_kill(mysql,c->thread_id);});
  }
  connection_pool.close();

  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);
  dynstr_free(&insert_field_names);
  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 && mysql)
    do_start_slave_sql(mysql);
  free_resources();
  exit(error);
}


/*
  db_connect -- connects to the host and selects DB.
*/

static MYSQL* 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* con = mysql_init(NULL);
  if (opt_compress)
    mysql_options(con,MYSQL_OPT_COMPRESS,NullS);
  SET_SSL_OPTS_WITH_CHECK(con);
  if (opt_protocol)
    mysql_options(con,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol);
  mysql_options(con, MYSQL_SET_CHARSET_NAME, default_charset);

  if (opt_plugin_dir && *opt_plugin_dir)
    mysql_options(con, MYSQL_PLUGIN_DIR, opt_plugin_dir);

  if (opt_default_auth && *opt_default_auth)
    mysql_options(con, MYSQL_DEFAULT_AUTH, opt_default_auth);

  mysql_options(con, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
  mysql_options4(con, MYSQL_OPT_CONNECT_ATTR_ADD,
                 "program_name", "mysqldump");
  if (!mysql_real_connect(con,host,user,passwd,
                          NULL,opt_mysql_port,opt_mysql_unix_port, 0))
  {
    DB_error(con, "when trying to connect");
    goto err;
  }

  if ((mysql_get_server_version(con) < 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(con, MYSQL_OPT_RECONNECT, &reconnect);
  my_snprintf(buff, sizeof(buff), "/*!40100 SET @@SQL_MODE='%s' */",
              compatible_mode_normal_str);
  if (mysql_query_with_error_report(con, 0, buff))
    goto err;
  /*
    set time_zone to UTC to allow dumping date types between servers with
    different time zone settings
  */
  if (opt_tz_utc)
  {
    if (mysql_query_with_error_report(con, 0,
                                    "/*!40103 SET TIME_ZONE='+00:00' */"))
      goto err;
  }

  /* Set MAX_STATEMENT_TIME to 0 unless set in client */
  my_snprintf(buff, sizeof(buff), "/*M!100100 SET @@MAX_STATEMENT_TIME=%f */",
              opt_max_statement_time);
  if (mysql_query_with_error_report(con, 0, buff))
    goto err;

  /* Set server side timeout between client commands to server compiled-in default */
  if(mysql_query_with_error_report(con,0, "/*!100100 SET WAIT_TIMEOUT=DEFAULT */"))
    goto err;

  DBUG_RETURN(con);

err:
  if (con)
    mysql_close(con);
  DBUG_RETURN(NULL);
} /* 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, 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 (!cmp_database(db, "mysql")) &&
          (!cmp_table(table, "general_log") ||
           !cmp_table(table, "slow_log") ||
           !cmp_table(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, "DO 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*2+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];
  char       *last_name;
  FILE       *sql_file= md_result_file;
  size_t     len;
  my_bool    is_log_table, dummy_versioned;
  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);
    init_dynamic_string_checked(&insert_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, "");
    dynstr_set_checked(&insert_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= 0;
    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;
    }
  }
  else
  {
    versioned= &dummy_versioned;
    dummy_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;
    /* 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 (multi_file_output)
      {
        if (!(sql_file= open_sql_file_for_table(db, 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 (multi_file_output)
            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 (multi_file_output)
          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 (multi_file_output)
        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, ", ");
        dynstr_append_checked(&insert_field_names, ", ");
        if (opt_header)
          dynstr_append_checked(&select_field_names_for_header, ", ");
      }
      init=1;

      last_name= quote_name(row[0], name_buff, 0);
      if (opt_dump_history && *versioned && opt_update_history &&
          row[2] && strcmp(row[2], "ROW END") == 0)
      {
        dynstr_append_checked(&select_field_names, "if(");
        dynstr_append_checked(&select_field_names, last_name);
        dynstr_append_checked(&select_field_names,
                              "= \"2038-01-19 03:14:07.999999\","
                              "\"2106-02-07 06:28:15.999999\", ");
        dynstr_append_checked(&select_field_names, last_name);
        dynstr_append_checked(&select_field_names, ") as ");
        dynstr_append_checked(&select_field_names, last_name);
      }
      else
        dynstr_append_checked(&select_field_names, last_name);
      dynstr_append_checked(&insert_field_names, last_name);
      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,");
      dynstr_append_checked(&select_field_names,
                            opt_update_history ?
                            "if(row_end = \"2038-01-19 03:14:07.999999\","
                            "\"2106-02-07 06:28:15.999999\", row_end) as "
                            "row_end" :
                            "row_end");
      dynstr_append_checked(&insert_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, insert_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 (multi_file_output)
      {
        if (!(sql_file= open_sql_file_for_table(db, 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 (multi_file_output)
          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 (multi_file_output &&
      !(sql_file= open_sql_file_for_table(db_name, 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 (multi_file_output)
    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, "'");
  }
}

/**
  Completion handler for async queries in the pool.
  Dies in case query produced an error.

  @param mysql    The connection that executed the query.
  @param query    The query that was executed.
  @param success  Whether the query was successful.
*/
static void send_query_completion_func(MYSQL* mysql, const char* query,
    bool success, void*)
{
  if (!success)
    maybe_die(EX_MYSQLERR, "Couldn't execute async query '%s' (%s)", query,
      mysql_error(mysql));
}

/*

 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 && !cmp_database(db, "mysql") &&
      !cmp_table(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 (multi_file_output)
  {
    char filename[FN_REFLEN], tmp_path[FN_REFLEN];
    char out_dir_buf[FN_REFLEN];
    char *out_dir= path;
    if (!out_dir)
    {
      my_snprintf(out_dir_buf, sizeof(out_dir_buf), "%s/%s", opt_dir, db);
      out_dir= out_dir_buf;
    }

    /*
      Convert the path to native os format
      and resolve to the full filepath.
    */
    convert_dirname(tmp_path,out_dir,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 ");
    char quoted_db_buf[NAME_LEN * 2 + 3];
    char *qdatabase= quote_name(db, quoted_db_buf, opt_quoted);
    dynstr_append_checked(&query_string, qdatabase);
    dynstr_append_checked(&query_string, ".");
    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 (opt_parallel)
    {
      if (connection_pool.execute_async(query_string.str,send_query_completion_func,nullptr,true))
      {
        dynstr_free(&query_string);
        DB_error(mysql, "when executing send_query 'SELECT INTO OUTFILE'");
        DBUG_VOID_RETURN;
      }
    }
    else 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,
                                           &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 &&
        !cmp_database(row[0], INFORMATION_SCHEMA_DB_NAME))
      continue;

    if (mysql_get_server_version(mysql) >= FIRST_PERFORMANCE_SCHEMA_VERSION &&
        !cmp_database(row[0], PERFORMANCE_SCHEMA_DB_NAME))
      continue;

   if (mysql_get_server_version(mysql) >= FIRST_SYS_SCHEMA_VERSION &&
       !cmp_database(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 &&
          !cmp_database(row[0], INFORMATION_SCHEMA_DB_NAME))
        continue;

      if (mysql_get_server_version(mysql) >= FIRST_PERFORMANCE_SCHEMA_VERSION &&
          !cmp_database(row[0], PERFORMANCE_SCHEMA_DB_NAME))
        continue;

     if (mysql_get_server_version(mysql) >= FIRST_SYS_SCHEMA_VERSION &&
        !cmp_database(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 (!multi_file_output && !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= !cmp_database(database, "mysql");
  DBUG_ENTER("dump_all_tables_in_db");

  afterdot= strmov(hash_key, database);
  *afterdot++= '.';

  if (opt_dir)
    ensure_out_dir_exists(database);

  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 (multi_file_output)
            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 && !cmp_table(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);

  if (opt_dir)
    ensure_out_dir_exists(db);

  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 continue 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 &&
        !cmp_database(db, INFORMATION_SCHEMA_DB_NAME)) &&
      !(mysql_get_server_version(mysql) >= FIRST_PERFORMANCE_SCHEMA_VERSION &&
        !cmp_database(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 continue 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 continue 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 (multi_file_output)
          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 */


const char fmt_gtid_pos[]= "%sSET GLOBAL gtid_slave_pos='%s';\n";

static int do_show_master_status(MYSQL *mysql_con, int consistent_binlog_pos,
                                 int have_mariadb_gtid, int use_gtid,
                                 char *set_gtid_pos)
{
  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;
    }

  }

  /* gtid */
  if (have_mariadb_gtid)
  {
    if (use_gtid)
    {
      fprintf(md_result_file,
              "%sCHANGE MASTER TO MASTER_USE_GTID=slave_pos;\n",
              comment_prefix);
      /*
        When --gtid is specified defer print of SET gtid_slave_pos until
        after its placeholder table is guaranteed to have been dumped.
      */
      print_comment(md_result_file, 0,
                    "\n-- A corresponding to the above master-data "
                    "CHANGE-MASTER settings to the slave gtid state is printed "
                    "later in the file.\n");
    }
    sprintf(set_gtid_pos, fmt_gtid_pos,
            (!use_gtid ? "-- " : comment_prefix), gtid_pos);
  }

  /* SHOW MASTER STATUS reports file and position */
  print_comment(md_result_file, 0,
                "\n--\n-- Alternately, following is the position of the binary "
                "logging from SHOW MASTER STATUS at point of backup."
                "\n-- Use this when creating a replica of the primary server "
                "where the backup was made."
                "\n-- The new server will be connecting to the primary server "
                "where the backup was taken."
                "\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 && !use_gtid)
    print_comment(md_result_file, 0,
                  "\n-- A corresponding to the above master-data CHANGE-MASTER "
                  "settings to the slave gtid state is printed as comments "
                  "later in the file.\n");
  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 have_mariadb_gtid,
                                int use_gtid, char* set_gtid_pos)
{
  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 : "-- ");

  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;
  }

  print_comment(md_result_file, 0,
                "\n--\n-- The following is the SQL position of the replication "
                "taken from SHOW SLAVE STATUS at the time of backup.\n"
                "-- Use this position when creating a clone of, or replacement "
                "server, from where the backup was taken."
                "\n-- This new server will connects to the same primary "
                "server%s.\n--\n",
                multi_source ? "(s)" : "");

  if (multi_source)
  {
    char gtid_pos[MAX_GTID_LENGTH];
    if (have_mariadb_gtid && get_gtid_pos(gtid_pos, 0))
    {
      mysql_free_result(slave);
      return 1;
    }
    /* defer print similarly to do_show_master_status */
    print_comment(md_result_file, 0,
                  "\n-- A corresponding to the below dump-slave "
                  "CHANGE-MASTER settings to the slave gtid state is printed "
                  "later in the file.\n");
    sprintf(set_gtid_pos, fmt_gtid_pos, gtid_comment_prefix, gtid_pos);
  }
  if (use_gtid)
    print_comment(md_result_file, 0,
                  "\n-- Use only the MASTER_USE_GTID=slave_pos or "
                  "MASTER_LOG_FILE/MASTER_LOG_POS in the statements below."
                  "\n\n");

  while ((row= mysql_fetch_row(slave)))
  {
    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 (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);
    }
  }
  fprintf(md_result_file, "\n");
  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= (char *)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 (multi_file_output)
  {
    if (!(sql_file= open_sql_file_for_table(db, 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);
}

#define MAX_POOL_CONNECTIONS 256
static void init_connection_pool(uint n_connections)
{
  MYSQL *conn[MAX_POOL_CONNECTIONS];

  if (n_connections > array_elements(conn))
    die(EX_USAGE, "Too many connections");

  for (uint i= 0; i < n_connections; i++)
  {
    MYSQL *c= connect_to_db(current_host, current_user, opt_password);
    if (!c)
    {
      for (uint j= 0; j < i; j++)
        mysql_close(conn[j]);
      die(EX_MYSQLERR, "Error during connection to DB");
    }
    conn[i]= c;
  }
  connection_pool.init(conn, n_connections);
}

/*
  If --dir option is in use, ensure that output directory for given db
  exists.
*/
static void ensure_out_dir_exists(const char *db)
{
  DBUG_ASSERT(opt_dir);
  char outdir[FN_REFLEN];
  my_snprintf(outdir, sizeof(outdir), "%s/%s", opt_dir, db);
  struct stat st;
  if (stat(outdir, &st) == 0)
  {
    if (st.st_mode & S_IFDIR)
      return;
    die(EX_CONSCHECK, "Error: path '%s' exists, but it is not a directory",
        outdir);
  }
  if (my_mkdir(outdir, 0777, MYF(MY_WME)))
    die(EX_MYSQLERR, "Error creating directory %s", outdir);
}


/**
  Print earlier prepared SET @@global.gtid_slave_pos.

  @param set_gtid_pos[in]  formatted sql set statement
**/
static void do_print_set_gtid_slave_pos(const char *set_gtid_pos,
                                        my_bool is_master_data)
{
  DBUG_ASSERT(opt_master_data || opt_slave_data);
  if (is_master_data)
  {
    print_comment(md_result_file, 0,
                  "\n-- The deferred gtid setting for slave corresponding to "
                  "the master-data CHANGE-MASTER follows\n");
    print_comment(md_result_file, 0,
                  "\n-- Preferably use GTID to start replication from GTID "
                  "position:\n\n");
  }
  else
  {
    print_comment(md_result_file, 0,
                  "\n-- The deferred gtid setting for slave corresponding to "
                  "the dump-slave CHANGE-MASTER follows\n");
    print_comment(md_result_file, 0,
                  "-- GTID position to start replication:\n");
  }
  fprintf(md_result_file, "%s", set_gtid_pos);
}

int main(int argc, char **argv)
{
  char bin_log_name[FN_REFLEN];
  int exit_code;
  int consistent_binlog_pos= 0;
  int have_mariadb_gtid= 0;
  /*
    to hold SET @@global.gtid_slave_pos which is deferred to print
    until the function epilogue.
  */
  char master_set_gtid_pos[3 + sizeof(fmt_gtid_pos) + MAX_GTID_LENGTH]= {0};
  char slave_set_gtid_pos[3 + sizeof(fmt_gtid_pos) + MAX_GTID_LENGTH]= {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);
    }
  }

  mysql= connect_to_db(current_host, current_user, opt_password);
  if (!mysql)
  {
    free_resources();
    exit(EX_MYSQLERR);
  }
  if (!multi_file_output)
  {
    write_header(md_result_file, *argv);
    if (opt_parallel)
    {
      verbose_msg("-- Warning: ignoring --parallel setting, it currently only "
                  "works together with --tab\n");
      opt_parallel= 0;
    }
  }
  else if (opt_parallel)
    init_connection_pool(opt_parallel);

  /* 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)
  {
    if (start_transaction(mysql))
      goto err;
    connection_pool.for_each_connection([](MYSQL *c) {
      if (start_transaction(c))
        maybe_die(EX_MYSQLERR, "Failed to start transaction on connection ID %u", mysql->thread_id);
    });
  }

  /* 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, master_set_gtid_pos))
    goto err;
  if (opt_slave_data && do_show_slave_status(mysql,
                                             have_mariadb_gtid,
                                             opt_use_gtid, slave_set_gtid_pos))
    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();

  if (opt_master_data && master_set_gtid_pos[0])
    do_print_set_gtid_slave_pos(master_set_gtid_pos, TRUE);
  if (opt_slave_data && slave_set_gtid_pos[0])
    do_print_set_gtid_slave_pos(slave_set_gtid_pos, FALSE);

  /* 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;

  /* wait for outstanding asynchronous queries */
  connection_pool.wait_all();

  /*
    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 (!multi_file_output)
    write_footer(md_result_file);
  free_resources();

  if (stderror_file)
    fclose(stderror_file);

  return(first_error);
} /* main */
