1 /*
2    Copyright (c) 2000, 2021, Oracle and/or its affiliates.
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License, version 2.0,
6    as published by the Free Software Foundation.
7 
8    This program is also distributed with certain software (including
9    but not limited to OpenSSL) that is licensed under separate terms,
10    as designated in a particular file or component or in included license
11    documentation.  The authors of MySQL hereby grant you an additional
12    permission to link the program and your derivative works with the
13    separately licensed software that they have included with MySQL.
14 
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License, version 2.0, for more details.
19 
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24 
25 /* mysqldump.c  - Dump a tables contents and format to an ASCII file
26 **
27 ** The author's original notes follow :-
28 **
29 ** AUTHOR: Igor Romanenko (igor@frog.kiev.ua)
30 ** DATE:   December 3, 1994
31 ** WARRANTY: None, expressed, impressed, implied
32 **          or other
33 ** STATUS: Public domain
34 ** Adapted and optimized for MySQL by
35 ** Michael Widenius, Sinisa Milivojevic, Jani Tolonen
36 ** -w --where added 9/10/98 by Jim Faucette
37 ** slave code by David Saez Padros <david@ols.es>
38 ** master/autocommit code by Brian Aker <brian@tangent.org>
39 ** SSL by
40 ** Andrei Errapart <andreie@no.spam.ee>
41 ** Tõnu Samuel  <tonu@please.do.not.remove.this.spam.ee>
42 ** XML by Gary Huntress <ghuntress@mediaone.net> 10/10/01, cleaned up
43 ** and adapted to mysqldump 05/11/01 by Jani Tolonen
44 ** Added --single-transaction option 06/06/2002 by Peter Zaitsev
45 ** 10 Jun 2003: SET NAMES and --no-set-names by Alexander Barkov
46 */
47 
48 #define DUMP_VERSION "10.13"
49 
50 #include <my_global.h>
51 #include <my_sys.h>
52 #include <my_user.h>
53 #include <m_string.h>
54 #include <m_ctype.h>
55 #include <hash.h>
56 #include <stdarg.h>
57 #include <my_list.h>
58 
59 #include "client_priv.h"
60 #include "my_default.h"
61 #include "mysql.h"
62 #include "mysql_version.h"
63 #include "mysqld_error.h"
64 
65 #include <welcome_copyright_notice.h> /* ORACLE_WELCOME_COPYRIGHT_NOTICE */
66 
67 /* Exit codes */
68 
69 #define EX_USAGE 1
70 #define EX_MYSQLERR 2
71 #define EX_CONSCHECK 3
72 #define EX_EOM 4
73 #define EX_EOF 5 /* ferror for output file was got */
74 #define EX_ILLEGAL_TABLE 6
75 
76 /* index into 'show fields from table' */
77 
78 #define SHOW_FIELDNAME  0
79 #define SHOW_TYPE  1
80 #define SHOW_NULL  2
81 #define SHOW_DEFAULT  4
82 #define SHOW_EXTRA  5
83 
84 /* Size of buffer for dump's select query */
85 #define QUERY_LENGTH 1536
86 
87 /* Size of comment buffer. */
88 #define COMMENT_LENGTH 2048
89 
90 /* ignore table flags */
91 #define IGNORE_NONE 0x00 /* no ignore */
92 #define IGNORE_DATA 0x01 /* don't dump data for this table */
93 
94 /* Chars needed to store LONGLONG, excluding trailing '\0'. */
95 #define LONGLONG_LEN 20
96 
97 typedef enum {
98   KEY_TYPE_NONE,
99   KEY_TYPE_PRIMARY,
100   KEY_TYPE_UNIQUE,
101   KEY_TYPE_NON_UNIQUE,
102   KEY_TYPE_CONSTRAINT
103 } key_type_t;
104 
105 /* Maximum number of fields per table */
106 #define MAX_FIELDS 4000
107 
108 static void add_load_option(DYNAMIC_STRING *str, const char *option,
109                              const char *option_value);
110 static ulong find_set(TYPELIB *lib, const char *x, size_t length,
111                       char **err_pos, uint *err_len);
112 static char *alloc_query_str(size_t size);
113 
114 static void field_escape(DYNAMIC_STRING* in, const char *from);
115 static my_bool  verbose= 0, opt_no_create_info= 0, opt_no_data= 0,
116                 quick= 1, extended_insert= 1,
117                 lock_tables= 1, opt_force= 0, flush_logs= 0,
118                 flush_privileges= 0,
119                 opt_drop=1,opt_keywords=0,opt_lock=1,opt_compress=0,
120                 create_options=1,opt_quoted=0,opt_databases=0,
121                 opt_alldbs=0,opt_create_db=0,opt_lock_all_tables=0,
122                 opt_set_charset=0, opt_dump_date=1,
123                 opt_autocommit=0,opt_disable_keys=1,opt_xml=0,
124                 opt_delete_master_logs=0, tty_password=0,
125                 opt_single_transaction=0, opt_comments= 0, opt_compact= 0,
126                 opt_hex_blob=0, opt_order_by_primary=0, opt_ignore=0,
127                 opt_complete_insert= 0, opt_drop_database= 0,
128                 opt_replace_into= 0,
129                 opt_dump_triggers= 0, opt_routines=0, opt_tz_utc=1,
130                 opt_slave_apply= 0,
131                 opt_include_master_host_port= 0,
132                 opt_events= 0, opt_comments_used= 0,
133                 opt_alltspcs=0, opt_notspcs= 0, opt_drop_trigger= 0,
134                 opt_compressed_columns= 0,
135                 opt_compressed_columns_with_dictionaries= 0,
136                 opt_drop_compression_dictionary= 1,
137                 opt_order_by_primary_desc= 0,
138                 opt_skip_mysql_schema=0, opt_secure_auth= TRUE;
139 static my_bool insert_pat_inited= 0, debug_info_flag= 0, debug_check_flag= 0;
140 static ulong opt_max_allowed_packet, opt_net_buffer_length;
141 static MYSQL mysql_connection,*mysql=0;
142 static DYNAMIC_STRING insert_pat;
143 static char  *opt_password=0,*current_user=0,
144              *current_host=0,*path=0,*fields_terminated=0,
145              *lines_terminated=0, *enclosed=0, *opt_enclosed=0, *escaped=0,
146              *where=0, *order_by=0,
147              *opt_compatible_mode_str= 0,
148              *err_ptr= 0, *opt_ignore_error= 0,
149              *log_error_file= NULL;
150 #ifndef NDEBUG
151 static char  *start_sql_file= NULL, *finish_sql_file= NULL;
152 #endif
153 static char **defaults_argv= 0;
154 static char compatible_mode_normal_str[255];
155 /* Server supports character_set_results session variable? */
156 static my_bool server_supports_switching_charsets= TRUE;
157 static ulong opt_compatible_mode= 0;
158 #define MYSQL_OPT_MASTER_DATA_EFFECTIVE_SQL 1
159 #define MYSQL_OPT_MASTER_DATA_COMMENTED_SQL 2
160 #define MYSQL_OPT_SLAVE_DATA_EFFECTIVE_SQL 1
161 #define MYSQL_OPT_SLAVE_DATA_COMMENTED_SQL 2
162 static uint opt_enable_cleartext_plugin= 0;
163 static my_bool using_opt_enable_cleartext_plugin= 0;
164 static uint opt_mysql_port= 0, opt_master_data;
165 static uint opt_slave_data;
166 static uint my_end_arg;
167 static char * opt_mysql_unix_port=0;
168 static char *opt_bind_addr = NULL;
169 static int   first_error=0;
170 static uint opt_lock_for_backup= 0;
171 #include <sslopt-vars.h>
172 #include <caching_sha2_passwordopt-vars.h>
173 FILE *md_result_file= 0;
174 FILE *stderror_file=0;
175 
176 const char *set_gtid_purged_mode_names[]=
177 {"OFF", "AUTO", "ON", NullS};
178 static TYPELIB set_gtid_purged_mode_typelib=
179                {array_elements(set_gtid_purged_mode_names) -1, "",
180                 set_gtid_purged_mode_names, NULL};
181 static enum enum_set_gtid_purged_mode {
182   SET_GTID_PURGED_OFF= 0,
183   SET_GTID_PURGED_AUTO =1,
184   SET_GTID_PURGED_ON=2
185 } opt_set_gtid_purged_mode= SET_GTID_PURGED_AUTO;
186 
187 #if defined (_WIN32) && !defined (EMBEDDED_LIBRARY)
188 static char *shared_memory_base_name=0;
189 #endif
190 static uint opt_protocol= 0;
191 static char *opt_plugin_dir= 0, *opt_default_auth= 0;
192 
193 DYNAMIC_ARRAY ignore_error;
194 static int parse_ignore_error();
195 
196 static my_bool opt_innodb_optimize_keys= FALSE;
197 
198 /*
199 Dynamic_string wrapper functions. In this file use these
200 wrappers, they will terminate the process if there is
201 an allocation failure.
202 */
203 static void init_dynamic_string_checked(DYNAMIC_STRING *str, const char *init_str,
204 			    size_t init_alloc, size_t alloc_increment);
205 static void dynstr_append_checked(DYNAMIC_STRING* dest, const char* src);
206 static void dynstr_set_checked(DYNAMIC_STRING *str, const char *init_str);
207 static void dynstr_append_mem_checked(DYNAMIC_STRING *str, const char *append,
208 			  size_t length);
209 static void dynstr_realloc_checked(DYNAMIC_STRING *str, size_t additional_size);
210 /*
211   Constant for detection of default value of default_charset.
212   If default_charset is equal to mysql_universal_client_charset, then
213   it is the default value which assigned at the very beginning of main().
214 */
215 static const char *mysql_universal_client_charset=
216   MYSQL_UNIVERSAL_CLIENT_CHARSET;
217 static char *default_charset;
218 static CHARSET_INFO *charset_info= &my_charset_latin1;
219 const char *default_dbug_option="d:t:o,/tmp/mysqldump.trace";
220 /* have we seen any VIEWs during table scanning? */
221 my_bool seen_views= 0;
222 static DYNAMIC_STRING gtid_executed_buffer;
223 static my_bool gtid_executed_buffer_inited= 0;
224 const char *compatible_mode_names[]=
225 {
226   "MYSQL323", "MYSQL40", "POSTGRESQL", "ORACLE", "MSSQL", "DB2",
227   "MAXDB", "NO_KEY_OPTIONS", "NO_TABLE_OPTIONS", "NO_FIELD_OPTIONS",
228   "ANSI",
229   NullS
230 };
231 #define MASK_ANSI_QUOTES \
232 (\
233  (1<<2)  | /* POSTGRESQL */\
234  (1<<3)  | /* ORACLE     */\
235  (1<<4)  | /* MSSQL      */\
236  (1<<5)  | /* DB2        */\
237  (1<<6)  | /* MAXDB      */\
238  (1<<10)   /* ANSI       */\
239 )
240 TYPELIB compatible_mode_typelib= {array_elements(compatible_mode_names) - 1,
241                                   "", compatible_mode_names, NULL};
242 
243 HASH ignore_table;
244 static HASH processed_compression_dictionaries;
245 
246 static LIST *skipped_keys_list = NULL;
247 static LIST *alter_constraints_list = NULL;
248 
249 static struct my_option my_long_options[] =
250 {
251   {"all-databases", 'A',
252    "Dump all the databases. This will be same as --databases with all databases selected.",
253    &opt_alldbs, &opt_alldbs, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
254    0, 0},
255   {"all-tablespaces", 'Y',
256    "Dump all the tablespaces.",
257    &opt_alltspcs, &opt_alltspcs, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
258    0, 0},
259   {"no-tablespaces", 'y',
260    "Do not dump any tablespace information.",
261    &opt_notspcs, &opt_notspcs, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
262    0, 0},
263   {"add-drop-database", OPT_DROP_DATABASE, "Add a DROP DATABASE before each create.",
264    &opt_drop_database, &opt_drop_database, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0,
265    0},
266   {"add-drop-table", OPT_DROP, "Add a DROP TABLE before each create.",
267    &opt_drop, &opt_drop, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0,
268    0},
269   {"add-drop-trigger", 0, "Add a DROP TRIGGER before each create.",
270    &opt_drop_trigger, &opt_drop_trigger, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0,
271    0},
272   {"add-locks", OPT_LOCKS, "Add locks around INSERT statements.",
273    &opt_lock, &opt_lock, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0,
274    0},
275   {"allow-keywords", OPT_KEYWORDS,
276    "Allow creation of column names that are keywords.", &opt_keywords,
277    &opt_keywords, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
278   {"apply-slave-statements", OPT_MYSQLDUMP_SLAVE_APPLY,
279    "Adds 'STOP SLAVE' prior to 'CHANGE MASTER' and 'START SLAVE' to bottom of dump.",
280    &opt_slave_apply, &opt_slave_apply, 0, GET_BOOL, NO_ARG,
281    0, 0, 0, 0, 0, 0},
282   {"lock-for-backup", OPT_LOCK_FOR_BACKUP, "Use lightweight metadata locks "
283    "to block updates to non-transactional tables and DDL to all tables. "
284    "This works only with --single-transaction, otherwise this option is "
285    "automatically converted to --lock-all-tables.", &opt_lock_for_backup,
286    &opt_lock_for_backup, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
287   {"bind-address", 0, "IP address to bind to.",
288    (uchar**) &opt_bind_addr, (uchar**) &opt_bind_addr, 0, GET_STR,
289    REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
290   {"character-sets-dir", OPT_CHARSETS_DIR,
291    "Directory for character set files.", &charsets_dir,
292    &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
293   {"comments", 'i', "Write additional information.",
294    &opt_comments, &opt_comments, 0, GET_BOOL, NO_ARG,
295    1, 0, 0, 0, 0, 0},
296   {"compatible", OPT_COMPATIBLE,
297    "Change the dump to be compatible with a given mode. By default tables "
298    "are dumped in a format optimized for MySQL. Legal modes are: ansi, "
299    "mysql323, mysql40, postgresql, oracle, mssql, db2, maxdb, no_key_options, "
300    "no_table_options, no_field_options. One can use several modes separated "
301    "by commas. Note: Requires MySQL server version 4.1.0 or higher. "
302    "This option is ignored with earlier server versions.",
303    &opt_compatible_mode_str, &opt_compatible_mode_str, 0,
304    GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
305   {"compact", OPT_COMPACT,
306    "Give less verbose output (useful for debugging). Disables structure "
307    "comments and header/footer constructs.  Enables options --skip-add-"
308    "drop-table --skip-add-locks --skip-comments --skip-disable-keys "
309    "--skip-set-charset.",
310    &opt_compact, &opt_compact, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
311   {"complete-insert", 'c', "Use complete insert statements.",
312    &opt_complete_insert, &opt_complete_insert, 0, GET_BOOL,
313    NO_ARG, 0, 0, 0, 0, 0, 0},
314   {"compress", 'C', "Use compression in server/client protocol.",
315    &opt_compress, &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0,
316    0, 0, 0},
317   {"create-options", 'a',
318    "Include all MySQL specific create options.",
319    &create_options, &create_options, 0, GET_BOOL, NO_ARG, 1,
320    0, 0, 0, 0, 0},
321   {"databases", 'B',
322    "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.",
323    &opt_databases, &opt_databases, 0, GET_BOOL, NO_ARG, 0, 0,
324    0, 0, 0, 0},
325 #ifdef NDEBUG
326   {"debug", '#', "This is a non-debug version. Catch this and exit.",
327    0,0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0},
328   {"debug-check", OPT_DEBUG_CHECK, "This is a non-debug version. Catch this and exit.",
329    0, 0, 0,
330    GET_DISABLED, NO_ARG, 0, 0, 0, 0, 0, 0},
331   {"debug-info", OPT_DEBUG_INFO, "This is a non-debug version. Catch this and exit.", 0,
332    0, 0, GET_DISABLED, NO_ARG, 0, 0, 0, 0, 0, 0},
333 #else
334   {"debug", '#', "Output debug log.", &default_dbug_option,
335    &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
336   {"debug-check", OPT_DEBUG_CHECK, "Check memory and open file usage at exit.",
337    &debug_check_flag, &debug_check_flag, 0,
338    GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
339   {"debug-info", OPT_DEBUG_INFO, "Print some debug info at exit.",
340    &debug_info_flag, &debug_info_flag,
341    0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
342 #endif
343   {"default-character-set", OPT_DEFAULT_CHARSET,
344    "Set the default character set.", &default_charset,
345    &default_charset, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
346   {"delete-master-logs", OPT_DELETE_MASTER_LOGS,
347    "Delete logs on master after backup. This automatically enables --master-data.",
348    &opt_delete_master_logs, &opt_delete_master_logs, 0,
349    GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
350   {"disable-keys", 'K',
351    "'/*!40000 ALTER TABLE tb_name DISABLE KEYS */; and '/*!40000 ALTER "
352    "TABLE tb_name ENABLE KEYS */; will be put in the output.", &opt_disable_keys,
353    &opt_disable_keys, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
354   {"dump-slave", OPT_MYSQLDUMP_SLAVE_DATA,
355    "This causes the binary log position and filename of the master to be "
356    "appended to the dumped data output. Setting the value to 1, will print"
357    "it as a CHANGE MASTER command in the dumped data output; if equal"
358    " to 2, that command will be prefixed with a comment symbol. "
359    "This option will turn --lock-all-tables on, unless "
360    "--single-transaction is specified too (in which case a "
361    "global read lock is only taken a short time at the beginning of the dump "
362    "- don't forget to read about --single-transaction below). In all cases "
363    "any action on logs will happen at the exact moment of the dump."
364    "Option automatically turns --lock-tables off.",
365    &opt_slave_data, &opt_slave_data, 0,
366    GET_UINT, OPT_ARG, 0, 0, MYSQL_OPT_SLAVE_DATA_COMMENTED_SQL, 0, 0, 0},
367   {"events", 'E', "Dump events.",
368      &opt_events, &opt_events, 0, GET_BOOL,
369      NO_ARG, 0, 0, 0, 0, 0, 0},
370   {"extended-insert", 'e',
371    "Use multiple-row INSERT syntax that include several VALUES lists.",
372    &extended_insert, &extended_insert, 0, GET_BOOL, NO_ARG,
373    1, 0, 0, 0, 0, 0},
374   {"fields-terminated-by", OPT_FTB,
375    "Fields in the output file are terminated by the given string.",
376    &fields_terminated, &fields_terminated, 0,
377    GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
378   {"fields-enclosed-by", OPT_ENC,
379    "Fields in the output file are enclosed by the given character.",
380    &enclosed, &enclosed, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0 ,0, 0},
381   {"fields-optionally-enclosed-by", OPT_O_ENC,
382    "Fields in the output file are optionally enclosed by the given character.",
383    &opt_enclosed, &opt_enclosed, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0 ,0, 0},
384   {"fields-escaped-by", OPT_ESC,
385    "Fields in the output file are escaped by the given character.",
386    &escaped, &escaped, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
387   {"flush-logs", 'F', "Flush logs file in server before starting dump. "
388    "Note that if you dump many databases at once (using the option "
389    "--databases= or --all-databases), the logs will be flushed for "
390    "each database dumped. The exception is when using --lock-all-tables "
391    "or --master-data: "
392    "in this case the logs will be flushed only once, corresponding "
393    "to the moment all tables are locked. So if you want your dump and "
394    "the log flush to happen at the same exact moment you should use "
395    "--lock-all-tables or --master-data with --flush-logs.",
396    &flush_logs, &flush_logs, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
397    0, 0},
398   {"flush-privileges", OPT_ESC, "Emit a FLUSH PRIVILEGES statement "
399    "after dumping the mysql database.  This option should be used any "
400    "time the dump contains the mysql database and any other database "
401    "that depends on the data in the mysql database for proper restore. ",
402    &flush_privileges, &flush_privileges, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
403    0, 0},
404   {"force", 'f', "Continue even if we get an SQL error.",
405    &opt_force, &opt_force, 0, GET_BOOL, NO_ARG,
406    0, 0, 0, 0, 0, 0},
407   {"help", '?', "Display this help message and exit.", 0, 0, 0, GET_NO_ARG,
408    NO_ARG, 0, 0, 0, 0, 0, 0},
409   {"hex-blob", OPT_HEXBLOB, "Dump binary strings (BINARY, "
410     "VARBINARY, BLOB) in hexadecimal format.",
411    &opt_hex_blob, &opt_hex_blob, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
412   {"host", 'h', "Connect to host.", &current_host,
413    &current_host, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
414   {"ignore-error", OPT_MYSQLDUMP_IGNORE_ERROR, "A comma-separated list of "
415    "error numbers to be ignored if encountered during dump.",
416    &opt_ignore_error, &opt_ignore_error, 0, GET_STR_ALLOC, REQUIRED_ARG, 0,
417    0, 0, 0, 0, 0},
418   {"ignore-table", OPT_IGNORE_TABLE,
419    "Do not dump the specified table. To specify more than one table to ignore, "
420    "use the directive multiple times, once for each table.  Each table must "
421    "be specified with both database and table names, e.g., "
422    "--ignore-table=database.table.",
423    0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
424   {"include-master-host-port", OPT_MYSQLDUMP_INCLUDE_MASTER_HOST_PORT,
425    "Adds 'MASTER_HOST=<host>, MASTER_PORT=<port>' to 'CHANGE MASTER TO..' "
426    "in dump produced with --dump-slave.", &opt_include_master_host_port,
427    &opt_include_master_host_port, 0, GET_BOOL, NO_ARG,
428    0, 0, 0, 0, 0, 0},
429    {"innodb-optimize-keys", OPT_INNODB_OPTIMIZE_KEYS,
430     "Use InnoDB fast index creation by creating secondary indexes after "
431     "dumping the data.",
432     &opt_innodb_optimize_keys, &opt_innodb_optimize_keys, 0, GET_BOOL, NO_ARG,
433     0, 0, 0, 0, 0, 0},
434   {"insert-ignore", OPT_INSERT_IGNORE, "Insert rows with INSERT IGNORE.",
435    &opt_ignore, &opt_ignore, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
436    0, 0},
437   {"lines-terminated-by", OPT_LTB,
438    "Lines in the output file are terminated by the given string.",
439    &lines_terminated, &lines_terminated, 0, GET_STR,
440    REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
441   {"lock-all-tables", 'x', "Locks all tables across all databases. This "
442    "is achieved by taking a global read lock for the duration of the whole "
443    "dump. Automatically turns --single-transaction and --lock-tables off.",
444    &opt_lock_all_tables, &opt_lock_all_tables, 0, GET_BOOL, NO_ARG,
445    0, 0, 0, 0, 0, 0},
446   {"lock-tables", 'l', "Lock all tables for read.", &lock_tables,
447    &lock_tables, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
448   {"log-error", OPT_ERROR_LOG_FILE, "Append warnings and errors to given file.",
449    &log_error_file, &log_error_file, 0, GET_STR,
450    REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
451   {"master-data", OPT_MASTER_DATA,
452    "This causes the binary log position and filename to be appended to the "
453    "output. If equal to 1, will print it as a CHANGE MASTER command; if equal"
454    " to 2, that command will be prefixed with a comment symbol. "
455    "This option will turn --lock-all-tables on, unless --single-transaction "
456    "is specified too (on servers that don't provide Binlog_snapshot_file and "
457    "Binlog_snapshot_position status variables this will still take a "
458    "global read lock for a short time at the beginning of the dump; "
459    "don't forget to read about --single-transaction below). In all cases, "
460    "any action on logs will happen at the exact moment of the dump. "
461    "Option automatically turns --lock-tables off.",
462    &opt_master_data, &opt_master_data, 0,
463    GET_UINT, OPT_ARG, 0, 0, MYSQL_OPT_MASTER_DATA_COMMENTED_SQL, 0, 0, 0},
464   {"max_allowed_packet", OPT_MAX_ALLOWED_PACKET,
465    "The maximum packet length to send to or receive from server.",
466     &opt_max_allowed_packet, &opt_max_allowed_packet, 0,
467     GET_ULONG, REQUIRED_ARG, 24*1024*1024, 4096,
468    (longlong) 2L*1024L*1024L*1024L, MALLOC_OVERHEAD, 1024, 0},
469   {"net_buffer_length", OPT_NET_BUFFER_LENGTH,
470    "The buffer size for TCP/IP and socket communication.",
471     &opt_net_buffer_length, &opt_net_buffer_length, 0,
472     GET_ULONG, REQUIRED_ARG, 1024*1024L-1025, 4096, 16*1024L*1024L,
473    MALLOC_OVERHEAD-1024, 1024, 0},
474   {"no-autocommit", OPT_AUTOCOMMIT,
475    "Wrap tables with autocommit/commit statements.",
476    &opt_autocommit, &opt_autocommit, 0, GET_BOOL, NO_ARG,
477    0, 0, 0, 0, 0, 0},
478   {"no-create-db", 'n',
479    "Suppress the CREATE DATABASE ... IF EXISTS statement that normally is "
480    "output for each dumped database if --all-databases or --databases is "
481    "given.",
482    &opt_create_db, &opt_create_db, 0,
483    GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
484   {"no-create-info", 't', "Don't write table creation info.",
485    &opt_no_create_info, &opt_no_create_info, 0, GET_BOOL,
486    NO_ARG, 0, 0, 0, 0, 0, 0},
487   {"no-data", 'd', "No row information.", &opt_no_data,
488    &opt_no_data, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
489   {"no-set-names", 'N', "Same as --skip-set-charset.",
490    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
491   {"opt", OPT_OPTIMIZE,
492    "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.",
493    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
494   {"order-by-primary", OPT_ORDER_BY_PRIMARY,
495    "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.",
496    &opt_order_by_primary, &opt_order_by_primary, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
497   {"order-by-primary-desc", OPT_ORDER_BY_PRIMARY_DESC,
498    "Taking backup ORDER BY primary key DESC.",
499    &opt_order_by_primary_desc, &opt_order_by_primary_desc, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
500   {"password", 'p',
501    "Password to use when connecting to server. If password is not given it's solicited on the tty.",
502    0, 0, 0, GET_PASSWORD, OPT_ARG, 0, 0, 0, 0, 0, 0},
503 #ifdef _WIN32
504   {"pipe", 'W', "Use named pipes to connect to server.", 0, 0, 0, GET_NO_ARG,
505    NO_ARG, 0, 0, 0, 0, 0, 0},
506 #endif
507   {"port", 'P', "Port number to use for connection.", &opt_mysql_port,
508    &opt_mysql_port, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0,
509    0},
510   {"protocol", OPT_MYSQL_PROTOCOL,
511    "The protocol to use for connection (tcp, socket, pipe, memory).",
512    0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
513   {"quick", 'q', "Don't buffer query, dump directly to stdout.",
514    &quick, &quick, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
515   {"quote-names",'Q', "Quote table and column names with backticks (`).",
516    &opt_quoted, &opt_quoted, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0,
517    0, 0},
518   {"replace", OPT_MYSQL_REPLACE_INTO, "Use REPLACE INTO instead of INSERT INTO.",
519    &opt_replace_into, &opt_replace_into, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
520    0, 0},
521   {"result-file", 'r',
522    "Direct output to a given file. This option should be used in systems "
523    "(e.g., DOS, Windows) that use carriage-return linefeed pairs (\\r\\n) "
524    "to separate text lines. This option ensures that only a single newline "
525    "is used.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
526   {"routines", 'R', "Dump stored routines (functions and procedures).",
527    &opt_routines, &opt_routines, 0, GET_BOOL,
528    NO_ARG, 0, 0, 0, 0, 0, 0},
529   {"set-charset", OPT_SET_CHARSET,
530    "Add 'SET NAMES default_character_set' to the output.",
531    &opt_set_charset, &opt_set_charset, 0, GET_BOOL, NO_ARG, 1,
532    0, 0, 0, 0, 0},
533   {"set-gtid-purged", OPT_SET_GTID_PURGED,
534     "Add 'SET @@GLOBAL.GTID_PURGED' to the output. Possible values for "
535     "this option are ON, OFF and AUTO. If ON is used and GTIDs "
536     "are not enabled on the server, an error is generated. If OFF is "
537     "used, this option does nothing. If AUTO is used and GTIDs are enabled "
538     "on the server, 'SET @@GLOBAL.GTID_PURGED' is added to the output. "
539     "If GTIDs are disabled, AUTO does nothing. If no value is supplied "
540     "then the default (AUTO) value will be considered.",
541     0, 0, 0, GET_STR, OPT_ARG,
542     0, 0, 0, 0, 0, 0},
543 #if defined (_WIN32) && !defined (EMBEDDED_LIBRARY)
544   {"shared-memory-base-name", OPT_SHARED_MEMORY_BASE_NAME,
545    "Base name of shared memory.", &shared_memory_base_name, &shared_memory_base_name,
546    0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
547 #endif
548   /*
549     Note that the combination --single-transaction --master-data
550     will give bullet-proof binlog position only if server >=4.1.3. That's the
551     old "FLUSH TABLES WITH READ LOCK does not block commit" fixed bug.
552   */
553   {"single-transaction", OPT_TRANSACTION,
554    "Creates a consistent snapshot by dumping all tables in a single "
555    "transaction. Works ONLY for tables stored in storage engines which "
556    "support multiversioning (currently only InnoDB does); the dump is NOT "
557    "guaranteed to be consistent for other storage engines. "
558    "While a --single-transaction dump is in process, to ensure a valid "
559    "dump file (correct table contents and binary log position), no other "
560    "connection should use the following statements: ALTER TABLE, DROP "
561    "TABLE, RENAME TABLE, TRUNCATE TABLE, as consistent snapshot is not "
562    "isolated from them. Option automatically turns off --lock-tables.",
563    &opt_single_transaction, &opt_single_transaction, 0,
564    GET_BOOL, NO_ARG,  0, 0, 0, 0, 0, 0},
565   {"dump-date", OPT_DUMP_DATE, "Put a dump date to the end of the output.",
566    &opt_dump_date, &opt_dump_date, 0,
567    GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
568   {"skip_mysql_schema", OPT_SKIP_MYSQL_SCHEMA, "Skip adding DROP DATABASE for mysql schema.",
569    &opt_skip_mysql_schema, &opt_skip_mysql_schema, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0,
570    0},
571   {"skip-opt", OPT_SKIP_OPTIMIZATION,
572    "Disable --opt. Disables --add-drop-table, --add-locks, --create-options, --quick, --extended-insert, --lock-tables, --set-charset, and --disable-keys.",
573    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
574   {"socket", 'S', "The socket file to use for connection.",
575    &opt_mysql_unix_port, &opt_mysql_unix_port, 0,
576    GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
577   {"secure-auth", OPT_SECURE_AUTH, "Refuse client connecting to server if it"
578     " uses old (pre-4.1.1) protocol. Deprecated. Always TRUE",
579     &opt_secure_auth, &opt_secure_auth, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
580 #ifndef NDEBUG
581 	{"start-sql-file", OPT_START_SQL_FILE, "Execute SQL statements from the file"
582     " at the mysqldump start. Each line has to contain one statement terminated"
583     " with a semicolon. Line length limit is 1023 characters.",
584     &start_sql_file, &start_sql_file, 0, GET_STR,
585    REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
586   {"finish-sql-file", OPT_FINISH_SQL_FILE, "Execute SQL statements from the file"
587     " at the mysqldump finish. Each line has to contain one statement terminated"
588     " with a semicolon. Line length limit is 1023 characters.",
589     &finish_sql_file, &finish_sql_file, 0, GET_STR,
590     REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
591 #endif  // DEBUF_OFF
592 #include <sslopt-longopts.h>
593 #include <caching_sha2_passwordopt-longopts.h>
594   {"tab",'T',
595    "Create tab-separated textfile for each table to given path. (Create .sql "
596    "and .txt files.) NOTE: This only works if mysqldump is run on the same "
597    "machine as the mysqld server.",
598    &path, &path, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
599   {"tables", OPT_TABLES, "Overrides option --databases (-B).",
600    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
601   {"triggers", OPT_TRIGGERS, "Dump triggers for each dumped table.",
602    &opt_dump_triggers, &opt_dump_triggers, 0, GET_BOOL,
603    NO_ARG, 1, 0, 0, 0, 0, 0},
604   {"tz-utc", OPT_TZ_UTC,
605     "SET TIME_ZONE='+00:00' at top of dump to allow dumping of TIMESTAMP data when a server has data in different time zones or data is being moved between servers with different time zones.",
606     &opt_tz_utc, &opt_tz_utc, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
607   {"user", 'u', "User for login if not current user.",
608    &current_user, &current_user, 0, GET_STR, REQUIRED_ARG,
609    0, 0, 0, 0, 0, 0},
610   {"verbose", 'v', "Print info about the various stages.",
611    &verbose, &verbose, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
612   {"version",'V', "Output version information and exit.", 0, 0, 0,
613    GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
614   {"where", 'w', "Dump only selected records. Quotes are mandatory.",
615    &where, &where, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
616   {"xml", 'X', "Dump a database as well formed XML.", 0, 0, 0, GET_NO_ARG,
617    NO_ARG, 0, 0, 0, 0, 0, 0},
618   {"plugin_dir", OPT_PLUGIN_DIR, "Directory for client-side plugins.",
619    &opt_plugin_dir, &opt_plugin_dir, 0,
620    GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
621   {"default_auth", OPT_DEFAULT_AUTH,
622    "Default authentication client-side plugin to use.",
623    &opt_default_auth, &opt_default_auth, 0,
624    GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
625   {"enable_cleartext_plugin", OPT_ENABLE_CLEARTEXT_PLUGIN,
626    "Enable/disable the clear text authentication plugin.",
627    &opt_enable_cleartext_plugin, &opt_enable_cleartext_plugin,
628    0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0},
629   {"enable-compressed-columns", OPT_ENABLE_COMPRESSED_COLUMNS,
630    "Enable compressed columns extensions.",
631    &opt_compressed_columns, &opt_compressed_columns, 0, GET_BOOL, NO_ARG,
632    0, 0, 0, 0, 0, 0},
633   {"enable-compressed-columns-with-dictionaries",
634    OPT_ENABLE_COMPRESSED_COLUMNS_WITH_DICTIONARIES,
635    "Enable dictionaries for compressed columns extensions.",
636    &opt_compressed_columns_with_dictionaries,
637    &opt_compressed_columns_with_dictionaries, 0, GET_BOOL, NO_ARG,
638    0, 0, 0, 0, 0, 0},
639    {"add-drop-compression-dictionary", OPT_DROP_COMPRESSION_DICTIONARY,
640     "Add a DROP COMPRESSION_DICTIONARY before each create.",
641     &opt_drop_compression_dictionary,
642     &opt_drop_compression_dictionary, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0,
643     0},
644   {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
645 };
646 
647 static const char *load_default_groups[]= { "mysqldump","client",0 };
648 
649 static void maybe_exit(int error);
650 static void die(int error, const char* reason, ...);
651 static void maybe_die(int error, const char* reason, ...);
652 static void write_header(FILE *sql_file, char *db_name);
653 static void print_value(FILE *file, MYSQL_RES  *result, MYSQL_ROW row,
654                         const char *prefix,const char *name,
655                         int string_value);
656 static int dump_selected_tables(char *db, char **table_names, int tables);
657 static int dump_all_tables_in_db(char *db);
658 static int init_dumping_views(char *, my_bool);
659 static int init_dumping_tables(char *, my_bool);
660 static int init_dumping(char *, int init_func(char*, my_bool));
661 static int dump_databases(char **);
662 static int dump_all_databases();
663 static char *quote_name(const char *name, char *buff, my_bool force);
664 char check_if_ignore_table(const char *table_name, char *table_type);
665 static char *primary_key_fields(const char *table_name, const my_bool desc);
666 static my_bool get_view_structure(char *table, char* db);
667 static my_bool dump_all_views_in_db(char *database);
668 static int dump_all_tablespaces();
669 static int dump_tablespaces_for_tables(char *db, char **table_names, int tables);
670 static int dump_tablespaces_for_databases(char** databases);
671 static int dump_tablespaces(char* ts_where);
672 static void print_comment(FILE *sql_file, my_bool is_error, const char *format,
673                           ...);
674 static char const* fix_identifier_with_newline(char const* object_name,
675                                                my_bool* freemem);
676 
677 /*
678   Print the supplied message if in verbose mode
679 
680   SYNOPSIS
681     verbose_msg()
682     fmt   format specifier
683     ...   variable number of parameters
684 */
685 
verbose_msg(const char * fmt,...)686 static void verbose_msg(const char *fmt, ...)
687 {
688   va_list args;
689   DBUG_ENTER("verbose_msg");
690 
691   if (!verbose)
692     DBUG_VOID_RETURN;
693 
694   va_start(args, fmt);
695   vfprintf(stderr, fmt, args);
696   va_end(args);
697 
698   fflush(stderr);
699 
700   DBUG_VOID_RETURN;
701 }
702 
703 /*
704   exit with message if ferror(file)
705 
706   SYNOPSIS
707     check_io()
708     file        - checked file
709 */
710 
check_io(FILE * file)711 void check_io(FILE *file)
712 {
713   if (ferror(file) || errno == 5)
714     die(EX_EOF, "Got errno %d on write", errno);
715 }
716 
print_version(void)717 static void print_version(void)
718 {
719   printf("%s  Ver %s Distrib %s, for %s (%s)\n",my_progname,DUMP_VERSION,
720          MYSQL_SERVER_VERSION,SYSTEM_TYPE,MACHINE_TYPE);
721 } /* print_version */
722 
723 
short_usage_sub(void)724 static void short_usage_sub(void)
725 {
726   printf("Usage: %s [OPTIONS] database [tables]\n", my_progname);
727   printf("OR     %s [OPTIONS] --databases [OPTIONS] DB1 [DB2 DB3...]\n",
728          my_progname);
729   printf("OR     %s [OPTIONS] --all-databases [OPTIONS]\n", my_progname);
730 }
731 
732 
usage(void)733 static void usage(void)
734 {
735   struct my_option *optp;
736   print_version();
737   puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000"));
738   puts("Dumping structure and contents of MySQL databases and tables.");
739   short_usage_sub();
740   print_defaults("my",load_default_groups);
741   /*
742     Turn default for zombies off so that the help on how to
743     turn them off text won't show up.
744     This is safe to do since it's followed by a call to exit().
745   */
746   for (optp= my_long_options; optp->name; optp++)
747   {
748     if (optp->id == OPT_SECURE_AUTH)
749     {
750       optp->def_value= 0;
751       break;
752     }
753   }
754   my_print_help(my_long_options);
755   my_print_variables(my_long_options);
756 } /* usage */
757 
758 
short_usage(void)759 static void short_usage(void)
760 {
761   short_usage_sub();
762   printf("For more options, use %s --help\n", my_progname);
763 }
764 
765 
write_header(FILE * sql_file,char * db_name)766 static void write_header(FILE *sql_file, char *db_name)
767 {
768   if (opt_xml)
769   {
770     fputs("<?xml version=\"1.0\"?>\n", sql_file);
771     /*
772       Schema reference.  Allows use of xsi:nil for NULL values and
773       xsi:type to define an element's data type.
774     */
775     fputs("<mysqldump ", sql_file);
776     fputs("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"",
777           sql_file);
778     fputs(">\n", sql_file);
779     check_io(sql_file);
780   }
781   else if (!opt_compact)
782   {
783     my_bool freemem= FALSE;
784     char const* text= fix_identifier_with_newline(db_name, &freemem);
785 
786     print_comment(sql_file, 0,
787                   "-- MySQL dump %s  Distrib %s, for %s (%s)\n--\n",
788                   DUMP_VERSION, MYSQL_SERVER_VERSION, SYSTEM_TYPE,
789                   MACHINE_TYPE);
790     print_comment(sql_file, 0, "-- Host: %s    Database: %s\n",
791                   current_host ? current_host : "localhost",
792                   text);
793 
794     if (freemem)
795       my_free((void*)text);
796 
797     print_comment(sql_file, 0,
798                   "-- ------------------------------------------------------\n"
799                  );
800     print_comment(sql_file, 0, "-- Server version\t%s\n",
801                   mysql_get_server_info(&mysql_connection));
802 
803     if (opt_set_charset)
804       fprintf(sql_file,
805 "\n/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;"
806 "\n/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;"
807 "\n/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;"
808 "\n/*!40101 SET NAMES %s */;\n",default_charset);
809 
810     if (opt_tz_utc)
811     {
812       fprintf(sql_file, "/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;\n");
813       fprintf(sql_file, "/*!40103 SET TIME_ZONE='+00:00' */;\n");
814     }
815 
816     if (!path)
817     {
818       fprintf(md_result_file,"\
819 /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;\n\
820 /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;\n\
821 ");
822     }
823     fprintf(sql_file,
824             "/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='%s%s%s' */;\n"
825             "/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;\n",
826             path?"":"NO_AUTO_VALUE_ON_ZERO",compatible_mode_normal_str[0]==0?"":",",
827             compatible_mode_normal_str);
828 
829     // This is specifically to allow MyRocks to bulk load a dump faster
830     // We have no interest in anything earlier than 5.7 and 17 being the
831     // current release. 5.7.8 and after can only use P_S for session_variables
832     // and never I_S. So we first check that P_S is present and the
833     // session_variables table exists. If no, we simply skip the optimization
834     // assuming that MyRocks isn't present either. If it is, ohh well, bulk
835     // loader will not be invoked.
836     fprintf(sql_file,
837             "/*!50717 SELECT COUNT(*) INTO @rocksdb_has_p_s_session_variables"
838             " FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA ="
839             " 'performance_schema' AND TABLE_NAME = 'session_variables'"
840             " */;\n"
841             "/*!50717 SET @rocksdb_get_is_supported = IF"
842             " (@rocksdb_has_p_s_session_variables, 'SELECT COUNT(*) INTO"
843             " @rocksdb_is_supported FROM performance_schema.session_variables"
844             " WHERE VARIABLE_NAME=\\'rocksdb_bulk_load\\'', 'SELECT 0') */;\n"
845             "/*!50717 PREPARE s FROM @rocksdb_get_is_supported */;\n"
846             "/*!50717 EXECUTE s */;\n"
847             "/*!50717 DEALLOCATE PREPARE s */;\n"
848             "/*!50717 SET @rocksdb_enable_bulk_load = IF"
849             " (@rocksdb_is_supported, 'SET SESSION rocksdb_bulk_load = 1',"
850             " 'SET @rocksdb_dummy_bulk_load = 0') */;\n"
851             "/*!50717 PREPARE s FROM @rocksdb_enable_bulk_load */;\n"
852             "/*!50717 EXECUTE s */;\n"
853             "/*!50717 DEALLOCATE PREPARE s */;\n");
854 
855 
856     check_io(sql_file);
857   }
858 } /* write_header */
859 
860 
write_footer(FILE * sql_file)861 static void write_footer(FILE *sql_file)
862 {
863   if (opt_xml)
864   {
865     fputs("</mysqldump>\n", sql_file);
866     check_io(sql_file);
867   }
868   else if (!opt_compact)
869   {
870     fprintf(sql_file,
871             "/*!50112 SET @disable_bulk_load = IF (@is_rocksdb_supported,"
872             " 'SET SESSION rocksdb_bulk_load = @old_rocksdb_bulk_load',"
873             " 'SET @dummy_rocksdb_bulk_load = 0') */;\n"
874             "/*!50112 PREPARE s FROM @disable_bulk_load */;\n"
875             "/*!50112 EXECUTE s */;\n"
876             "/*!50112 DEALLOCATE PREPARE s */;\n");
877 
878 
879     if (opt_tz_utc)
880       fprintf(sql_file,"/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;\n");
881 
882     fprintf(sql_file,"\n/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;\n");
883     if (!path)
884     {
885       fprintf(md_result_file,"\
886 /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;\n\
887 /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;\n");
888     }
889     if (opt_set_charset)
890       fprintf(sql_file,
891 "/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;\n"
892 "/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;\n"
893 "/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;\n");
894     fprintf(sql_file,
895             "/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;\n");
896     fputs("\n", sql_file);
897 
898     if (opt_dump_date)
899     {
900       char time_str[20];
901       get_date(time_str, GETDATE_DATE_TIME, 0);
902       print_comment(sql_file, 0, "-- Dump completed on %s\n", time_str);
903     }
904     else
905       print_comment(sql_file, 0, "-- Dump completed\n");
906 
907     check_io(sql_file);
908   }
909 } /* write_footer */
910 
911 
get_table_key(const char * entry,size_t * length,my_bool not_used MY_ATTRIBUTE ((unused)))912 uchar* get_table_key(const char *entry, size_t *length,
913                      my_bool not_used MY_ATTRIBUTE((unused)))
914 {
915   *length= strlen(entry);
916   return (uchar*) entry;
917 }
918 
919 
920 static my_bool
get_one_option(int optid,const struct my_option * opt MY_ATTRIBUTE ((unused)),char * argument)921 get_one_option(int optid, const struct my_option *opt MY_ATTRIBUTE((unused)),
922                char *argument)
923 {
924   switch (optid) {
925   case 'p':
926     if (argument == disabled_my_option)
927       argument= (char*) "";                     /* Don't require password */
928     if (argument)
929     {
930       char *start=argument;
931       my_free(opt_password);
932       opt_password=my_strdup(PSI_NOT_INSTRUMENTED,
933                              argument,MYF(MY_FAE));
934       while (*argument) *argument++= 'x';               /* Destroy argument */
935       if (*start)
936         start[1]=0;                             /* Cut length of argument */
937       tty_password= 0;
938     }
939     else
940       tty_password=1;
941     break;
942   case 'r':
943     if (!(md_result_file= my_fopen(argument, O_WRONLY | FILE_BINARY,
944                                     MYF(MY_WME))))
945       exit(1);
946     break;
947   case 'W':
948 #ifdef _WIN32
949     opt_protocol= MYSQL_PROTOCOL_PIPE;
950 #endif
951     break;
952   case 'N':
953     opt_set_charset= 0;
954     break;
955   case 'T':
956     opt_disable_keys=0;
957 
958     if (strlen(argument) >= FN_REFLEN)
959     {
960       /*
961         This check is made because the some the file functions below
962         have FN_REFLEN sized stack allocated buffers and will cause
963         a crash even if the input destination buffer is large enough
964         to hold the output.
965       */
966       die(EX_USAGE, "Input filename too long: %s", argument);
967     }
968 
969     break;
970   case '#':
971     DBUG_PUSH(argument ? argument : default_dbug_option);
972     debug_check_flag= 1;
973     break;
974 #include <sslopt-case.h>
975   case 'V': print_version(); exit(0);
976   case 'X':
977     opt_xml= 1;
978     extended_insert= opt_drop= opt_lock=
979       opt_disable_keys= opt_autocommit= opt_create_db= 0;
980     break;
981   case 'i':
982     opt_comments_used= 1;
983     break;
984   case 'I':
985   case '?':
986     usage();
987     exit(0);
988   case (int) OPT_MASTER_DATA:
989     if (!argument) /* work like in old versions */
990       opt_master_data= MYSQL_OPT_MASTER_DATA_EFFECTIVE_SQL;
991     break;
992   case (int) OPT_MYSQLDUMP_SLAVE_DATA:
993     if (!argument) /* work like in old versions */
994       opt_slave_data= MYSQL_OPT_SLAVE_DATA_EFFECTIVE_SQL;
995     break;
996   case (int) OPT_OPTIMIZE:
997     extended_insert= opt_drop= opt_lock= quick= create_options=
998       opt_disable_keys= lock_tables= opt_set_charset= 1;
999     break;
1000   case (int) OPT_SKIP_OPTIMIZATION:
1001     extended_insert= opt_drop= opt_lock= quick= create_options=
1002       opt_disable_keys= lock_tables= opt_set_charset= 0;
1003     break;
1004   case (int) OPT_COMPACT:
1005     if (opt_compact)
1006     {
1007        opt_comments= opt_drop= opt_disable_keys= opt_lock= 0;
1008        opt_set_charset= 0;
1009     }
1010     break;
1011   case (int) OPT_TABLES:
1012     opt_databases=0;
1013     break;
1014   case (int) OPT_IGNORE_TABLE:
1015   {
1016     if (!strchr(argument, '.'))
1017     {
1018       fprintf(stderr, "Illegal use of option --ignore-table=<database>.<table>\n");
1019       exit(1);
1020     }
1021     if (my_hash_insert(&ignore_table, (uchar*)my_strdup(PSI_NOT_INSTRUMENTED,
1022                                                         argument, MYF(0))))
1023       exit(EX_EOM);
1024     break;
1025   }
1026   case (int) OPT_COMPATIBLE:
1027     {
1028       char buff[255];
1029       char *end= compatible_mode_normal_str;
1030       int i;
1031       ulong mode;
1032       uint err_len;
1033 
1034       opt_quoted= 1;
1035       opt_set_charset= 0;
1036       opt_compatible_mode_str= argument;
1037       opt_compatible_mode= find_set(&compatible_mode_typelib,
1038                                     argument, strlen(argument),
1039                                     &err_ptr, &err_len);
1040       if (err_len)
1041       {
1042         strmake(buff, err_ptr, MY_MIN(sizeof(buff) - 1, err_len));
1043         fprintf(stderr, "Invalid mode to --compatible: %s\n", buff);
1044         exit(1);
1045       }
1046 #if !defined(NDEBUG)
1047       {
1048         size_t size_for_sql_mode= 0;
1049         const char **ptr;
1050         for (ptr= compatible_mode_names; *ptr; ptr++)
1051           size_for_sql_mode+= strlen(*ptr);
1052         size_for_sql_mode+= sizeof(compatible_mode_names)-1;
1053         assert(sizeof(compatible_mode_normal_str)>=size_for_sql_mode);
1054       }
1055 #endif
1056       mode= opt_compatible_mode;
1057       for (i= 0, mode= opt_compatible_mode; mode; mode>>= 1, i++)
1058       {
1059         if (mode & 1)
1060         {
1061           end= my_stpcpy(end, compatible_mode_names[i]);
1062           end= my_stpcpy(end, ",");
1063         }
1064       }
1065       if (end!=compatible_mode_normal_str)
1066         end[-1]= 0;
1067       /*
1068         Set charset to the default compiled value if it hasn't
1069         been reset yet by --default-character-set=xxx.
1070       */
1071       if (default_charset == mysql_universal_client_charset)
1072         default_charset= (char*) MYSQL_DEFAULT_CHARSET_NAME;
1073       break;
1074     }
1075   case (int) OPT_ENABLE_CLEARTEXT_PLUGIN:
1076     using_opt_enable_cleartext_plugin= TRUE;
1077     break;
1078   case (int) OPT_MYSQL_PROTOCOL:
1079     opt_protocol= find_type_or_exit(argument, &sql_protocol_typelib,
1080                                     opt->name);
1081     break;
1082   case (int) OPT_SET_GTID_PURGED:
1083     {
1084       if (argument)
1085         opt_set_gtid_purged_mode= find_type_or_exit(argument,
1086                                                     &set_gtid_purged_mode_typelib,
1087                                                     opt->name)-1;
1088       break;
1089     }
1090   case (int) OPT_MYSQLDUMP_IGNORE_ERROR:
1091     /* Store the supplied list of errors into an array. */
1092     if (parse_ignore_error())
1093       exit(EX_EOM);
1094     break;
1095   case OPT_SECURE_AUTH:
1096     /* --secure-auth is a zombie option. */
1097     if (!opt_secure_auth)
1098     {
1099       fprintf(stderr, "mysqldump: [ERROR] --skip-secure-auth is not supported.\n");
1100       exit(1);
1101     }
1102     else
1103       CLIENT_WARN_DEPRECATED_NO_REPLACEMENT("--secure-auth");
1104     break;
1105   }
1106   return 0;
1107 }
1108 
get_options(int * argc,char *** argv)1109 static int get_options(int *argc, char ***argv)
1110 {
1111   int ho_error;
1112 
1113   if (mysql_get_option(NULL, MYSQL_OPT_MAX_ALLOWED_PACKET, &opt_max_allowed_packet) ||
1114       mysql_get_option(NULL, MYSQL_OPT_NET_BUFFER_LENGTH, &opt_max_allowed_packet))
1115   {
1116     exit(1);
1117   }
1118 
1119   md_result_file= stdout;
1120   my_getopt_use_args_separator= TRUE;
1121   if (load_defaults("my",load_default_groups,argc,argv))
1122     return 1;
1123   my_getopt_use_args_separator= FALSE;
1124 
1125   defaults_argv= *argv;
1126 
1127   if (my_hash_init(&ignore_table, charset_info, 16, 0, 0,
1128                    (my_hash_get_key) get_table_key, my_free, 0,
1129                    PSI_NOT_INSTRUMENTED))
1130     return(EX_EOM);
1131   /* Don't copy internal log tables */
1132   if (my_hash_insert(&ignore_table,
1133                      (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
1134                                         "mysql.apply_status", MYF(MY_WME))) ||
1135       my_hash_insert(&ignore_table,
1136                      (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
1137                                         "mysql.schema", MYF(MY_WME))) ||
1138       my_hash_insert(&ignore_table,
1139                      (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
1140                                         "mysql.general_log", MYF(MY_WME))) ||
1141       my_hash_insert(&ignore_table,
1142                      (uchar*) my_strdup(PSI_NOT_INSTRUMENTED,
1143                                         "mysql.slow_log", MYF(MY_WME))))
1144     return(EX_EOM);
1145 
1146   if (my_hash_init(&processed_compression_dictionaries, charset_info, 16, 0,
1147     0, (my_hash_get_key)get_table_key, my_free, 0, PSI_NOT_INSTRUMENTED))
1148     return(EX_EOM);
1149 
1150   if ((ho_error= handle_options(argc, argv, my_long_options, get_one_option)))
1151     return(ho_error);
1152 
1153   if (mysql_options(NULL, MYSQL_OPT_MAX_ALLOWED_PACKET, &opt_max_allowed_packet) ||
1154       mysql_options(NULL, MYSQL_OPT_NET_BUFFER_LENGTH, &opt_net_buffer_length))
1155   {
1156     exit(1);
1157   }
1158 
1159   if (debug_info_flag)
1160     my_end_arg= MY_CHECK_ERROR | MY_GIVE_INFO;
1161   if (debug_check_flag)
1162     my_end_arg= MY_CHECK_ERROR;
1163 
1164   if (!path && (enclosed || opt_enclosed || escaped || lines_terminated ||
1165                 fields_terminated))
1166   {
1167     fprintf(stderr,
1168             "%s: You must use option --tab with --fields-...\n", my_progname);
1169     return(EX_USAGE);
1170   }
1171 
1172   if (opt_lock_for_backup && opt_lock_all_tables)
1173   {
1174     fprintf(stderr, "%s: You can't use --lock-for-backup and "
1175             "--lock-all-tables at the same time.\n", my_progname);
1176     return(EX_USAGE);
1177   }
1178 
1179   /*
1180      Convert --lock-for-backup to --lock-all-tables if --single-transaction is
1181      not specified.
1182   */
1183   if (!opt_single_transaction && opt_lock_for_backup)
1184   {
1185     opt_lock_all_tables= 1;
1186     opt_lock_for_backup= 0;
1187   }
1188 
1189   /* We don't delete master logs if slave data option */
1190   if (opt_slave_data)
1191   {
1192     opt_lock_all_tables= !opt_single_transaction;
1193     opt_master_data= 0;
1194     opt_delete_master_logs= 0;
1195   }
1196 
1197   /* Ensure consistency of the set of binlog & locking options */
1198   if (opt_delete_master_logs && !opt_master_data)
1199     opt_master_data= MYSQL_OPT_MASTER_DATA_COMMENTED_SQL;
1200   if (opt_single_transaction && opt_lock_all_tables)
1201   {
1202     fprintf(stderr, "%s: You can't use --single-transaction and "
1203             "--lock-all-tables at the same time.\n", my_progname);
1204     return(EX_USAGE);
1205   }
1206   if (opt_master_data)
1207   {
1208     opt_lock_all_tables= !opt_single_transaction;
1209     opt_slave_data= 0;
1210   }
1211   if (opt_single_transaction || opt_lock_all_tables)
1212     lock_tables= 0;
1213   if (enclosed && opt_enclosed)
1214   {
1215     fprintf(stderr, "%s: You can't use ..enclosed.. and ..optionally-enclosed.. at the same time.\n", my_progname);
1216     return(EX_USAGE);
1217   }
1218   if ((opt_databases || opt_alldbs) && path)
1219   {
1220     fprintf(stderr,
1221             "%s: --databases or --all-databases can't be used with --tab.\n",
1222             my_progname);
1223     return(EX_USAGE);
1224   }
1225   if (strcmp(default_charset, charset_info->csname) &&
1226       !(charset_info= get_charset_by_csname(default_charset,
1227                                             MY_CS_PRIMARY, MYF(MY_WME))))
1228     exit(1);
1229   if ((*argc < 1 && !opt_alldbs) || (*argc > 0 && opt_alldbs))
1230   {
1231     short_usage();
1232     return EX_USAGE;
1233   }
1234   if (tty_password)
1235     opt_password=get_tty_password(NullS);
1236   return(0);
1237 } /* get_options */
1238 
1239 
1240 /*
1241 ** DB_error -- prints mysql error message and exits the program.
1242 */
DB_error(MYSQL * mysql_arg,const char * when)1243 static void DB_error(MYSQL *mysql_arg, const char *when)
1244 {
1245   DBUG_ENTER("DB_error");
1246   maybe_die(EX_MYSQLERR, "Got error: %d: %s %s",
1247           mysql_errno(mysql_arg), mysql_error(mysql_arg), when);
1248   DBUG_VOID_RETURN;
1249 }
1250 
1251 
1252 
1253 /*
1254   Prints out an error message and kills the process.
1255 
1256   SYNOPSIS
1257     die()
1258     error_num   - process return value
1259     fmt_reason  - a format string for use by my_vsnprintf.
1260     ...         - variable arguments for above fmt_reason string
1261 
1262   DESCRIPTION
1263     This call prints out the formatted error message to stderr and then
1264     terminates the process.
1265 */
die(int error_num,const char * fmt_reason,...)1266 static void die(int error_num, const char* fmt_reason, ...)
1267 {
1268   char buffer[1000];
1269   va_list args;
1270   va_start(args,fmt_reason);
1271   my_vsnprintf(buffer, sizeof(buffer), fmt_reason, args);
1272   va_end(args);
1273 
1274   fprintf(stderr, "%s: %s\n", my_progname, buffer);
1275   fflush(stderr);
1276 
1277   /* force the exit */
1278   opt_force= 0;
1279   if (opt_ignore_error)
1280     my_free(opt_ignore_error);
1281   opt_ignore_error= 0;
1282 
1283   maybe_exit(error_num);
1284 }
1285 
1286 
1287 /*
1288   Prints out an error message and maybe kills the process.
1289 
1290   SYNOPSIS
1291     maybe_die()
1292     error_num   - process return value
1293     fmt_reason  - a format string for use by my_vsnprintf.
1294     ...         - variable arguments for above fmt_reason string
1295 
1296   DESCRIPTION
1297     This call prints out the formatted error message to stderr and then
1298     terminates the process, unless the --force command line option is used.
1299 
1300     This call should be used for non-fatal errors (such as database
1301     errors) that the code may still be able to continue to the next unit
1302     of work.
1303 
1304 */
maybe_die(int error_num,const char * fmt_reason,...)1305 static void maybe_die(int error_num, const char* fmt_reason, ...)
1306 {
1307   char buffer[1000];
1308   va_list args;
1309   va_start(args,fmt_reason);
1310   my_vsnprintf(buffer, sizeof(buffer), fmt_reason, args);
1311   va_end(args);
1312 
1313   fprintf(stderr, "%s: %s\n", my_progname, buffer);
1314   fflush(stderr);
1315 
1316   maybe_exit(error_num);
1317 }
1318 
1319 
1320 
1321 /*
1322   Sends a query to server, optionally reads result, prints error message if
1323   some.
1324 
1325   SYNOPSIS
1326     mysql_query_with_error_report()
1327     mysql_con       connection to use
1328     res             if non zero, result will be put there with
1329                     mysql_store_result()
1330     query           query to send to server
1331 
1332   RETURN VALUES
1333     0               query sending and (if res!=0) result reading went ok
1334     1               error
1335 */
1336 
mysql_query_with_error_report(MYSQL * mysql_con,MYSQL_RES ** res,const char * query)1337 static int mysql_query_with_error_report(MYSQL *mysql_con, MYSQL_RES **res,
1338                                          const char *query)
1339 {
1340   if (mysql_query(mysql_con, query) ||
1341       (res && !((*res)= mysql_store_result(mysql_con))))
1342   {
1343     maybe_die(EX_MYSQLERR, "Couldn't execute '%s': %s (%d)",
1344             query, mysql_error(mysql_con), mysql_errno(mysql_con));
1345     return 1;
1346   }
1347   return 0;
1348 }
1349 
1350 
fetch_db_collation(const char * db_name,char * db_cl_name,int db_cl_size)1351 static int fetch_db_collation(const char *db_name,
1352                               char *db_cl_name,
1353                               int db_cl_size)
1354 {
1355   my_bool err_status= FALSE;
1356   char query[QUERY_LENGTH];
1357   MYSQL_RES *db_cl_res;
1358   MYSQL_ROW db_cl_row;
1359   char quoted_database_buf[NAME_LEN*2+3];
1360   char *qdatabase= quote_name(db_name, quoted_database_buf, 1);
1361 
1362   my_snprintf(query, sizeof (query), "use %s", qdatabase);
1363 
1364   if (mysql_query_with_error_report(mysql, NULL, query))
1365     return 1;
1366 
1367   if (mysql_query_with_error_report(mysql, &db_cl_res,
1368                                     "select @@collation_database"))
1369     return 1;
1370 
1371   do
1372   {
1373     if (mysql_num_rows(db_cl_res) != 1)
1374     {
1375       err_status= TRUE;
1376       break;
1377     }
1378 
1379     if (!(db_cl_row= mysql_fetch_row(db_cl_res)))
1380     {
1381       err_status= TRUE;
1382       break;
1383     }
1384 
1385     strncpy(db_cl_name, db_cl_row[0], db_cl_size-1);
1386     db_cl_name[db_cl_size - 1]= 0;
1387 
1388   } while (FALSE);
1389 
1390   mysql_free_result(db_cl_res);
1391 
1392   return err_status ? 1 : 0;
1393 }
1394 
1395 /*
1396   Check if server supports binlog_snapshot_gtid_executed.
1397   Returns 1 supported, 0 if not.
1398  */
1399 static int
consistent_gtid_executed_supported(MYSQL * mysql_con)1400 consistent_gtid_executed_supported(MYSQL *mysql_con)
1401 {
1402   MYSQL_RES *res;
1403   MYSQL_ROW row;
1404   int found = 0;
1405 
1406   if (mysql_query_with_error_report(
1407 		  mysql_con, &res, "SHOW STATUS LIKE 'binlog_snapshot_gtid_executed'"))
1408     return 0;
1409 
1410   while ((row= mysql_fetch_row(res)))
1411   {
1412     if (0 == strcmp(row[0], "Binlog_snapshot_gtid_executed"))
1413     {
1414       found= 1;
1415       break;
1416     }
1417   }
1418   mysql_free_result(res);
1419 
1420   return found;
1421 }
1422 
1423 /*
1424   Check if server supports non-blocking binlog position using the
1425   binlog_snapshot_file and binlog_snapshot_position status variables. If it
1426   does, also return the position obtained if output pointers are non-NULL.
1427   Returns 1 if position available, 0 if not.
1428 */
1429 static int
check_consistent_binlog_pos(char * binlog_pos_file,char * binlog_pos_offset)1430 check_consistent_binlog_pos(char *binlog_pos_file, char *binlog_pos_offset)
1431 {
1432   MYSQL_RES *res;
1433   MYSQL_ROW row;
1434   int found;
1435 
1436   if (mysql_query_with_error_report(mysql, &res,
1437                                     "SHOW STATUS LIKE 'binlog_snapshot_%'"))
1438     return 0;
1439 
1440   found= 0;
1441   while ((row= mysql_fetch_row(res)))
1442   {
1443     if (0 == strcmp(row[0], "Binlog_snapshot_file"))
1444     {
1445       if (binlog_pos_file)
1446         strmake(binlog_pos_file, row[1], FN_REFLEN-1);
1447       found++;
1448     }
1449     else if (0 == strcmp(row[0], "Binlog_snapshot_position"))
1450     {
1451       if (binlog_pos_offset)
1452         strmake(binlog_pos_offset, row[1], LONGLONG_LEN);
1453       found++;
1454     }
1455   }
1456   mysql_free_result(res);
1457 
1458   return (found == 2);
1459 }
1460 
my_case_str(const char * str,size_t str_len,const char * token,size_t token_len)1461 static char *my_case_str(const char *str,
1462                          size_t str_len,
1463                          const char *token,
1464                          size_t token_len)
1465 {
1466   my_match_t match;
1467 
1468   uint status= my_charset_latin1.coll->instr(&my_charset_latin1,
1469                                              str, str_len,
1470                                              token, token_len,
1471                                              &match, 1);
1472 
1473   return status ? (char *) str + match.end : NULL;
1474 }
1475 
1476 
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)1477 static int switch_db_collation(FILE *sql_file,
1478                                const char *db_name,
1479                                const char *delimiter,
1480                                const char *current_db_cl_name,
1481                                const char *required_db_cl_name,
1482                                int *db_cl_altered)
1483 {
1484   if (strcmp(current_db_cl_name, required_db_cl_name) != 0)
1485   {
1486     char quoted_db_buf[NAME_LEN * 2 + 3];
1487     char *quoted_db_name= quote_name(db_name, quoted_db_buf, FALSE);
1488 
1489     CHARSET_INFO *db_cl= get_charset_by_name(required_db_cl_name, MYF(0));
1490 
1491     if (!db_cl)
1492       return 1;
1493 
1494     fprintf(sql_file,
1495             "ALTER DATABASE %s CHARACTER SET %s COLLATE %s %s\n",
1496             (const char *) quoted_db_name,
1497             (const char *) db_cl->csname,
1498             (const char *) db_cl->name,
1499             (const char *) delimiter);
1500 
1501     *db_cl_altered= 1;
1502 
1503     return 0;
1504   }
1505 
1506   *db_cl_altered= 0;
1507 
1508   return 0;
1509 }
1510 
1511 
restore_db_collation(FILE * sql_file,const char * db_name,const char * delimiter,const char * db_cl_name)1512 static int restore_db_collation(FILE *sql_file,
1513                                 const char *db_name,
1514                                 const char *delimiter,
1515                                 const char *db_cl_name)
1516 {
1517   char quoted_db_buf[NAME_LEN * 2 + 3];
1518   char *quoted_db_name= quote_name(db_name, quoted_db_buf, FALSE);
1519 
1520   CHARSET_INFO *db_cl= get_charset_by_name(db_cl_name, MYF(0));
1521 
1522   if (!db_cl)
1523     return 1;
1524 
1525   fprintf(sql_file,
1526           "ALTER DATABASE %s CHARACTER SET %s COLLATE %s %s\n",
1527           (const char *) quoted_db_name,
1528           (const char *) db_cl->csname,
1529           (const char *) db_cl->name,
1530           (const char *) delimiter);
1531 
1532   return 0;
1533 }
1534 
1535 
switch_cs_variables(FILE * sql_file,const char * delimiter,const char * character_set_client,const char * character_set_results,const char * collation_connection)1536 static void switch_cs_variables(FILE *sql_file,
1537                                 const char *delimiter,
1538                                 const char *character_set_client,
1539                                 const char *character_set_results,
1540                                 const char *collation_connection)
1541 {
1542   fprintf(sql_file,
1543           "/*!50003 SET @saved_cs_client      = @@character_set_client */ %s\n"
1544           "/*!50003 SET @saved_cs_results     = @@character_set_results */ %s\n"
1545           "/*!50003 SET @saved_col_connection = @@collation_connection */ %s\n"
1546           "/*!50003 SET character_set_client  = %s */ %s\n"
1547           "/*!50003 SET character_set_results = %s */ %s\n"
1548           "/*!50003 SET collation_connection  = %s */ %s\n",
1549           (const char *) delimiter,
1550           (const char *) delimiter,
1551           (const char *) delimiter,
1552 
1553           (const char *) character_set_client,
1554           (const char *) delimiter,
1555 
1556           (const char *) character_set_results,
1557           (const char *) delimiter,
1558 
1559           (const char *) collation_connection,
1560           (const char *) delimiter);
1561 }
1562 
1563 
restore_cs_variables(FILE * sql_file,const char * delimiter)1564 static void restore_cs_variables(FILE *sql_file,
1565                                  const char *delimiter)
1566 {
1567   fprintf(sql_file,
1568           "/*!50003 SET character_set_client  = @saved_cs_client */ %s\n"
1569           "/*!50003 SET character_set_results = @saved_cs_results */ %s\n"
1570           "/*!50003 SET collation_connection  = @saved_col_connection */ %s\n",
1571           (const char *) delimiter,
1572           (const char *) delimiter,
1573           (const char *) delimiter);
1574 }
1575 
1576 /*
1577  This function will remove specific sql mode.
1578 
1579   @param[in]   sql_mode      Original sql mode from where input mode needs to
1580                              be removed.
1581   @param[in]   replace_mode  sql mode which needs to be removed from original
1582                              sql mode.
1583   @param[in]   replace_len   length of sql mode which needs to be removed.
1584 
1585   @retval  1 replace_mode is not present
1586            0 replace_mode is removed successfully
1587 */
remove_sql_mode(char * sql_mode,const char * replace_mode,size_t replace_len)1588 static int remove_sql_mode(char* sql_mode, const char* replace_mode,
1589   size_t replace_len) {
1590   char *start = strstr(sql_mode, replace_mode);
1591   /* nothing to replace */
1592   if (!start)
1593     return 1;
1594   /* sql mode to replace is the only sql mode present or the last one */
1595   if (strlen(start) == replace_len) {
1596     if (start == sql_mode)
1597       *start = 0;
1598     else
1599       start[-1] = 0;
1600   }
1601   else {
1602     const char *next = start + replace_len + 1;
1603     memmove(start, next, strlen(next) + 1);
1604   }
1605   return 0;
1606 }
1607 
switch_sql_mode(FILE * sql_file,const char * delimiter,const char * sql_mode)1608 static void switch_sql_mode(FILE *sql_file,
1609                             const char *delimiter,
1610                             const char *sql_mode)
1611 {
1612   fprintf(sql_file,
1613           "/*!50003 SET @saved_sql_mode       = @@sql_mode */ %s\n"
1614           "/*!50003 SET sql_mode              = '%s' */ %s\n",
1615           (const char *) delimiter,
1616 
1617           (const char *) sql_mode,
1618           (const char *) delimiter);
1619 }
1620 
1621 
restore_sql_mode(FILE * sql_file,const char * delimiter)1622 static void restore_sql_mode(FILE *sql_file,
1623                              const char *delimiter)
1624 {
1625   fprintf(sql_file,
1626           "/*!50003 SET sql_mode              = @saved_sql_mode */ %s\n",
1627           (const char *) delimiter);
1628 }
1629 
1630 
switch_time_zone(FILE * sql_file,const char * delimiter,const char * time_zone)1631 static void switch_time_zone(FILE *sql_file,
1632                              const char *delimiter,
1633                              const char *time_zone)
1634 {
1635   fprintf(sql_file,
1636           "/*!50003 SET @saved_time_zone      = @@time_zone */ %s\n"
1637           "/*!50003 SET time_zone             = '%s' */ %s\n",
1638           (const char *) delimiter,
1639 
1640           (const char *) time_zone,
1641           (const char *) delimiter);
1642 }
1643 
1644 
restore_time_zone(FILE * sql_file,const char * delimiter)1645 static void restore_time_zone(FILE *sql_file,
1646                               const char *delimiter)
1647 {
1648   fprintf(sql_file,
1649           "/*!50003 SET time_zone             = @saved_time_zone */ %s\n",
1650           (const char *) delimiter);
1651 }
1652 
1653 
1654 /**
1655   Switch charset for results to some specified charset.  If the server does not
1656   support character_set_results variable, nothing can be done here.  As for
1657   whether something should be done here, future new callers of this function
1658   should be aware that the server lacking the facility of switching charsets is
1659   treated as success.
1660 
1661   @note  If the server lacks support, then nothing is changed and no error
1662          condition is returned.
1663 
1664   @returns  whether there was an error or not
1665 */
switch_character_set_results(MYSQL * mysql,const char * cs_name)1666 static int switch_character_set_results(MYSQL *mysql, const char *cs_name)
1667 {
1668   char query_buffer[QUERY_LENGTH];
1669   size_t query_length;
1670 
1671   /* Server lacks facility.  This is not an error, by arbitrary decision . */
1672   if (!server_supports_switching_charsets)
1673     return FALSE;
1674 
1675   query_length= my_snprintf(query_buffer,
1676                             sizeof (query_buffer),
1677                             "SET SESSION character_set_results = '%s'",
1678                             (const char *) cs_name);
1679 
1680   return mysql_real_query(mysql, query_buffer, (ulong)query_length);
1681 }
1682 
1683 /**
1684   Rewrite statement, enclosing DEFINER clause in version-specific comment.
1685 
1686   This function parses any CREATE statement and encloses DEFINER-clause in
1687   version-specific comment:
1688     input query:     CREATE DEFINER=a@b FUNCTION ...
1689     rewritten query: CREATE * / / *!50020 DEFINER=a@b * / / *!50003 FUNCTION ...
1690 
1691   @note This function will go away when WL#3995 is implemented.
1692 
1693   @param[in] stmt_str                 CREATE statement string.
1694   @param[in] stmt_length              Length of the stmt_str.
1695   @param[in] definer_version_str      Minimal MySQL version number when
1696                                       DEFINER clause is supported in the
1697                                       given statement.
1698   @param[in] definer_version_length   Length of definer_version_str.
1699   @param[in] stmt_version_str         Minimal MySQL version number when the
1700                                       given statement is supported.
1701   @param[in] stmt_version_length      Length of stmt_version_str.
1702   @param[in] keyword_str              Keyword to look for after CREATE.
1703   @param[in] keyword_length           Length of keyword_str.
1704 
1705   @return pointer to the new allocated query string.
1706 */
1707 
cover_definer_clause(const char * stmt_str,size_t stmt_length,const char * definer_version_str,size_t definer_version_length,const char * stmt_version_str,size_t stmt_version_length,const char * keyword_str,size_t keyword_length)1708 static char *cover_definer_clause(const char *stmt_str,
1709                                   size_t stmt_length,
1710                                   const char *definer_version_str,
1711                                   size_t definer_version_length,
1712                                   const char *stmt_version_str,
1713                                   size_t stmt_version_length,
1714                                   const char *keyword_str,
1715                                   size_t keyword_length)
1716 {
1717   char *definer_begin= my_case_str(stmt_str, stmt_length,
1718                                    C_STRING_WITH_LEN(" DEFINER"));
1719   char *definer_end= NULL;
1720 
1721   char *query_str= NULL;
1722   char *query_ptr;
1723   LEX_CSTRING comment= { C_STRING_WITH_LEN("*/ /*!") };
1724 
1725   if (!definer_begin)
1726     return NULL;
1727 
1728   definer_end= my_case_str(definer_begin, strlen(definer_begin),
1729                            keyword_str, keyword_length);
1730 
1731   if (!definer_end)
1732     return NULL;
1733 
1734   /*
1735     Allocate memory for new query string: original string
1736     from SHOW statement and version-specific comments.
1737   */
1738   query_str= alloc_query_str(stmt_length + 23);
1739 
1740   query_ptr= my_stpncpy(query_str, stmt_str, definer_begin - stmt_str);
1741   query_ptr= my_stpncpy(query_ptr, comment.str, comment.length + 1);
1742   query_ptr= my_stpncpy(query_ptr, definer_version_str, definer_version_length);
1743   query_ptr= my_stpncpy(query_ptr, definer_begin, definer_end - definer_begin);
1744   query_ptr= my_stpncpy(query_ptr, comment.str, comment.length + 1);
1745   query_ptr= my_stpncpy(query_ptr, stmt_version_str, stmt_version_length);
1746   query_ptr= strxmov(query_ptr, definer_end, NullS);
1747 
1748   return query_str;
1749 }
1750 
1751 /*
1752   Open a new .sql file to dump the table or view into
1753 
1754   SYNOPSIS
1755     open_sql_file_for_table
1756     name      name of the table or view
1757     flags     flags (as per "man 2 open")
1758 
1759   RETURN VALUES
1760     0        Failed to open file
1761     > 0      Handle of the open file
1762 */
open_sql_file_for_table(const char * table,int flags)1763 static FILE* open_sql_file_for_table(const char* table, int flags)
1764 {
1765   FILE* res;
1766   char filename[FN_REFLEN], tmp_path[FN_REFLEN];
1767   /*
1768     We need to reset processed compression dictionaries container
1769     each time a new SQL file is created (for --tab option).
1770   */
1771   my_hash_reset(&processed_compression_dictionaries);
1772   convert_dirname(tmp_path,path,NullS);
1773   res= my_fopen(fn_format(filename, table, tmp_path, ".sql", 4),
1774                 flags, MYF(MY_WME));
1775   return res;
1776 }
1777 
1778 
free_resources()1779 static void free_resources()
1780 {
1781   if (md_result_file && md_result_file != stdout)
1782     my_fclose(md_result_file, MYF(0));
1783   my_free(opt_password);
1784   if (my_hash_inited(&ignore_table))
1785     my_hash_free(&ignore_table);
1786   if (my_hash_inited(&processed_compression_dictionaries))
1787     my_hash_free(&processed_compression_dictionaries);
1788   if (insert_pat_inited)
1789     dynstr_free(&insert_pat);
1790   if (defaults_argv)
1791     free_defaults(defaults_argv);
1792   if (opt_ignore_error)
1793     my_free(opt_ignore_error);
1794   delete_dynamic(&ignore_error);
1795   if (gtid_executed_buffer_inited)
1796     dynstr_free(&gtid_executed_buffer);
1797   my_end(my_end_arg);
1798 }
1799 
1800 /**
1801   Parse the list of error numbers to be ignored and store into a dynamic
1802   array.
1803 
1804   @return Operation status
1805       @retval 0    Success
1806       @retval >0   Failure
1807 */
1808 static
parse_ignore_error()1809 int parse_ignore_error()
1810 {
1811   const char *search= ",";
1812   char *token;
1813   uint my_err;
1814 
1815   DBUG_ENTER("parse_ignore_error");
1816 
1817   if (my_init_dynamic_array(&ignore_error,
1818                            PSI_NOT_INSTRUMENTED,
1819                            sizeof(uint), NULL, 12, 12))
1820     goto error;
1821 
1822   token= strtok(opt_ignore_error, search);
1823 
1824   while (token != NULL)
1825   {
1826     my_err= atoi(token);
1827     // filter out 0s, if any
1828     if (my_err != 0)
1829     {
1830       if (insert_dynamic(&ignore_error, &my_err))
1831         goto error;
1832     }
1833     token= strtok(NULL, search);
1834   }
1835   DBUG_RETURN(0);
1836 
1837 error:
1838   DBUG_RETURN(EX_EOM);
1839 }
1840 
1841 /**
1842   Check if the last error should be ignored.
1843       @retval 1     yes
1844               0     no
1845 */
do_ignore_error()1846 static my_bool do_ignore_error()
1847 {
1848   uint i, last_errno, *my_err;
1849   my_bool found= 0;
1850 
1851   DBUG_ENTER("do_ignore_error");
1852 
1853   last_errno= mysql_errno(mysql);
1854 
1855   if (last_errno == 0)
1856     goto done;
1857 
1858   for (i= 0; i < ignore_error.elements; i++)
1859   {
1860     my_err= dynamic_element(&ignore_error, i, uint *);
1861     if (last_errno == *my_err)
1862     {
1863       found= 1;
1864       break;
1865     }
1866   }
1867 done:
1868   DBUG_RETURN(found);
1869 }
1870 
maybe_exit(int error)1871 static void maybe_exit(int error)
1872 {
1873   if (!first_error)
1874     first_error= error;
1875 
1876   /*
1877     Return if --force is used; else return only if the
1878     last error number is in the list of error numbers
1879     specified using --ignore-error option.
1880   */
1881   if (opt_force || (opt_ignore_error && do_ignore_error()))
1882     return;
1883   if (mysql)
1884     mysql_close(mysql);
1885   free_resources();
1886   exit(error);
1887 }
1888 
1889 
1890 /*
1891   db_connect -- connects to the host and selects DB.
1892 */
1893 
connect_to_db(char * host,char * user,char * passwd)1894 static int connect_to_db(char *host, char *user,char *passwd)
1895 {
1896   char buff[20+FN_REFLEN];
1897   DBUG_ENTER("connect_to_db");
1898 
1899   verbose_msg("-- Connecting to %s...\n", host ? host : "localhost");
1900   mysql_init(&mysql_connection);
1901   if (opt_compress)
1902     mysql_options(&mysql_connection,MYSQL_OPT_COMPRESS,NullS);
1903   SSL_SET_OPTIONS(&mysql_connection);
1904   if (opt_protocol)
1905     mysql_options(&mysql_connection,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol);
1906   if (opt_bind_addr)
1907     mysql_options(&mysql_connection,MYSQL_OPT_BIND,opt_bind_addr);
1908 #if defined (_WIN32) && !defined (EMBEDDED_LIBRARY)
1909   if (shared_memory_base_name)
1910     mysql_options(&mysql_connection,MYSQL_SHARED_MEMORY_BASE_NAME,shared_memory_base_name);
1911 #endif
1912   mysql_options(&mysql_connection, MYSQL_SET_CHARSET_NAME, default_charset);
1913 
1914   if (opt_plugin_dir && *opt_plugin_dir)
1915     mysql_options(&mysql_connection, MYSQL_PLUGIN_DIR, opt_plugin_dir);
1916 
1917   if (opt_default_auth && *opt_default_auth)
1918     mysql_options(&mysql_connection, MYSQL_DEFAULT_AUTH, opt_default_auth);
1919 
1920   if (using_opt_enable_cleartext_plugin)
1921     mysql_options(&mysql_connection, MYSQL_ENABLE_CLEARTEXT_PLUGIN,
1922                   (char *) &opt_enable_cleartext_plugin);
1923 
1924   mysql_options(&mysql_connection, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
1925   mysql_options4(&mysql_connection, MYSQL_OPT_CONNECT_ATTR_ADD,
1926                  "program_name", "mysqldump");
1927 
1928   set_server_public_key(&mysql_connection);
1929   set_get_server_public_key_option(&mysql_connection);
1930 
1931   if (!(mysql= mysql_real_connect(&mysql_connection,host,user,passwd,
1932                                   NULL,opt_mysql_port,opt_mysql_unix_port,
1933                                   0)))
1934   {
1935     DB_error(&mysql_connection, "when trying to connect");
1936     DBUG_RETURN(1);
1937   }
1938   if ((mysql_get_server_version(&mysql_connection) < 40100) ||
1939       (opt_compatible_mode & 3))
1940   {
1941     /* Don't dump SET NAMES with a pre-4.1 server (bug#7997).  */
1942     opt_set_charset= 0;
1943 
1944     /* Don't switch charsets for 4.1 and earlier.  (bug#34192). */
1945     server_supports_switching_charsets= FALSE;
1946   }
1947   /*
1948     As we're going to set SQL_MODE, it would be lost on reconnect, so we
1949     cannot reconnect.
1950   */
1951   mysql->reconnect= 0;
1952   my_snprintf(buff, sizeof(buff), "/*!40100 SET @@SQL_MODE='%s' */",
1953               compatible_mode_normal_str);
1954   if (mysql_query_with_error_report(mysql, 0, buff))
1955     DBUG_RETURN(1);
1956   /*
1957     set time_zone to UTC to allow dumping date types between servers with
1958     different time zone settings
1959   */
1960   if (opt_tz_utc)
1961   {
1962     my_snprintf(buff, sizeof(buff), "/*!40103 SET TIME_ZONE='+00:00' */");
1963     if (mysql_query_with_error_report(mysql, 0, buff))
1964       DBUG_RETURN(1);
1965   }
1966   DBUG_RETURN(0);
1967 } /* connect_to_db */
1968 
1969 
1970 /*
1971 ** dbDisconnect -- disconnects from the host.
1972 */
dbDisconnect(char * host)1973 static void dbDisconnect(char *host)
1974 {
1975   verbose_msg("-- Disconnecting from %s...\n", host ? host : "localhost");
1976   mysql_close(mysql);
1977 } /* dbDisconnect */
1978 
1979 
unescape(FILE * file,char * pos,size_t length)1980 static void unescape(FILE *file,char *pos, size_t length)
1981 {
1982   char *tmp;
1983   DBUG_ENTER("unescape");
1984   if (!(tmp=(char*) my_malloc(PSI_NOT_INSTRUMENTED,
1985                               length*2+1, MYF(MY_WME))))
1986     die(EX_MYSQLERR, "Couldn't allocate memory");
1987 
1988   mysql_real_escape_string_quote(&mysql_connection, tmp, pos, (ulong)length, '\'');
1989   fputc('\'', file);
1990   fputs(tmp, file);
1991   fputc('\'', file);
1992   check_io(file);
1993   my_free(tmp);
1994   DBUG_VOID_RETURN;
1995 } /* unescape */
1996 
1997 
test_if_special_chars(const char * str)1998 static my_bool test_if_special_chars(const char *str)
1999 {
2000   for ( ; *str ; str++)
2001     if (!my_isvar(charset_info,*str) && *str != '$')
2002       return 1;
2003   return 0;
2004 } /* test_if_special_chars */
2005 
2006 
2007 
2008 /*
2009   quote_name(name, buff, force)
2010 
2011   Quotes char string, taking into account compatible mode
2012 
2013   Args
2014 
2015   name                 Unquoted string containing that which will be quoted
2016   buff                 The buffer that contains the quoted value, also returned
2017   force                Flag to make it ignore 'test_if_special_chars'
2018 
2019   Returns
2020 
2021   buff                 quoted string
2022 
2023 */
quote_name(const char * name,char * buff,my_bool force)2024 static char *quote_name(const char *name, char *buff, my_bool force)
2025 {
2026   char *to= buff;
2027   char qtype= (opt_compatible_mode & MASK_ANSI_QUOTES) ? '\"' : '`';
2028 
2029   if (!force && !opt_quoted && !test_if_special_chars(name))
2030     return (char*) name;
2031   *to++= qtype;
2032   while (*name)
2033   {
2034     if (*name == qtype)
2035       *to++= qtype;
2036     *to++= *name++;
2037   }
2038   to[0]= qtype;
2039   to[1]= 0;
2040   return buff;
2041 } /* quote_name */
2042 
2043 /**
2044   Unquotes char string, taking into account compatible mode
2045 
2046   @param opt_quoted_name   Optionally quoted string
2047   @param buff              The buffer that will contain the unquoted value,
2048                            may be returned
2049 
2050   @return Pointer to unquoted string (either original opt_quoted_name or
2051           buff).
2052 */
unquote_name(const char * opt_quoted_name,char * buff)2053 static char *unquote_name(const char *opt_quoted_name, char *buff)
2054 {
2055   char *to= buff;
2056   const char qtype= (opt_compatible_mode & MASK_ANSI_QUOTES) ? '\"' : '`';
2057 
2058   if (!opt_quoted)
2059     return (char*)opt_quoted_name;
2060   if (*opt_quoted_name != qtype)
2061   {
2062     assert(strchr(opt_quoted_name, qtype) == 0);
2063     return (char*)opt_quoted_name;
2064   }
2065 
2066   ++opt_quoted_name;
2067   while (*opt_quoted_name)
2068   {
2069     if (*opt_quoted_name == qtype)
2070     {
2071       ++opt_quoted_name;
2072       if (*opt_quoted_name == qtype)
2073         *to++= qtype;
2074       else
2075       {
2076         assert(*opt_quoted_name == '\0');
2077       }
2078     }
2079     else
2080     {
2081       *to++= *opt_quoted_name++;
2082     }
2083   }
2084   to[0]= 0;
2085   return buff;
2086 } /* unquote_name */
2087 
2088 
2089 /*
2090   Quote a table name so it can be used in "SHOW TABLES LIKE <tabname>"
2091 
2092   SYNOPSIS
2093     quote_for_like()
2094     name     name of the table
2095     buff     quoted name of the table
2096 
2097   DESCRIPTION
2098     Quote \, _, ' and % characters
2099 
2100     Note: Because MySQL uses the C escape syntax in strings
2101     (for example, '\n' to represent newline), you must double
2102     any '\' that you use in your LIKE  strings. For example, to
2103     search for '\n', specify it as '\\n'. To search for '\', specify
2104     it as '\\\\' (the backslashes are stripped once by the parser
2105     and another time when the pattern match is done, leaving a
2106     single backslash to be matched).
2107 
2108     Example: "t\1" => "t\\\\1"
2109 
2110 */
quote_for_like(const char * name,char * buff)2111 static char *quote_for_like(const char *name, char *buff)
2112 {
2113   char *to= buff;
2114   *to++= '\'';
2115   while (*name)
2116   {
2117     if (*name == '\\')
2118     {
2119       *to++='\\';
2120       *to++='\\';
2121       *to++='\\';
2122     }
2123     else if (*name == '\'' || *name == '_'  || *name == '%')
2124       *to++= '\\';
2125     *to++= *name++;
2126   }
2127   to[0]= '\'';
2128   to[1]= 0;
2129   return buff;
2130 }
2131 
2132 
2133 /**
2134   Quote and print a string.
2135 
2136   @param xml_file          - Output file.
2137   @param str               - String to print.
2138   @param len               - Its length.
2139   @param is_attribute_name - A check for attribute name or value.
2140 
2141   @description
2142     Quote '<' '>' '&' '\"' chars and print a string to the xml_file.
2143 */
2144 
print_quoted_xml(FILE * xml_file,const char * str,size_t len,my_bool is_attribute_name)2145 static void print_quoted_xml(FILE *xml_file, const char *str, size_t len,
2146                              my_bool is_attribute_name)
2147 {
2148   const char *end;
2149 
2150   for (end= str + len; str != end; str++)
2151   {
2152     switch (*str) {
2153     case '<':
2154       fputs("&lt;", xml_file);
2155       break;
2156     case '>':
2157       fputs("&gt;", xml_file);
2158       break;
2159     case '&':
2160       fputs("&amp;", xml_file);
2161       break;
2162     case '\"':
2163       fputs("&quot;", xml_file);
2164       break;
2165     case ' ':
2166       /* Attribute names cannot contain spaces. */
2167       if (is_attribute_name)
2168       {
2169         fputs("_", xml_file);
2170         break;
2171       }
2172       /* fall through */
2173     default:
2174       fputc(*str, xml_file);
2175       break;
2176     }
2177   }
2178   check_io(xml_file);
2179 }
2180 
2181 
2182 /*
2183   Print xml tag. Optionally add attribute(s).
2184 
2185   SYNOPSIS
2186     print_xml_tag(xml_file, sbeg, send, tag_name, first_attribute_name,
2187                     ..., attribute_name_n, attribute_value_n, NullS)
2188     xml_file              - output file
2189     sbeg                  - line beginning
2190     line_end              - line ending
2191     tag_name              - XML tag name.
2192     first_attribute_name  - tag and first attribute
2193     first_attribute_value - (Implied) value of first attribute
2194     attribute_name_n      - attribute n
2195     attribute_value_n     - value of attribute n
2196 
2197   DESCRIPTION
2198     Print XML tag with any number of attribute="value" pairs to the xml_file.
2199 
2200     Format is:
2201       sbeg<tag_name first_attribute_name="first_attribute_value" ...
2202       attribute_name_n="attribute_value_n">send
2203   NOTE
2204     Additional arguments must be present in attribute/value pairs.
2205     The last argument should be the null character pointer.
2206     All attribute_value arguments MUST be NULL terminated strings.
2207     All attribute_value arguments will be quoted before output.
2208 */
2209 
print_xml_tag(FILE * xml_file,const char * sbeg,const char * line_end,const char * tag_name,const char * first_attribute_name,...)2210 static void print_xml_tag(FILE * xml_file, const char* sbeg,
2211                           const char* line_end,
2212                           const char* tag_name,
2213                           const char* first_attribute_name, ...)
2214 {
2215   va_list arg_list;
2216   const char *attribute_name, *attribute_value;
2217 
2218   fputs(sbeg, xml_file);
2219   fputc('<', xml_file);
2220   fputs(tag_name, xml_file);
2221 
2222   va_start(arg_list, first_attribute_name);
2223   attribute_name= first_attribute_name;
2224   while (attribute_name != NullS)
2225   {
2226     attribute_value= va_arg(arg_list, char *);
2227     assert(attribute_value != NullS);
2228 
2229     fputc(' ', xml_file);
2230     fputs(attribute_name, xml_file);
2231     fputc('\"', xml_file);
2232 
2233     print_quoted_xml(xml_file, attribute_value, strlen(attribute_value), 0);
2234     fputc('\"', xml_file);
2235 
2236     attribute_name= va_arg(arg_list, char *);
2237   }
2238   va_end(arg_list);
2239 
2240   fputc('>', xml_file);
2241   fputs(line_end, xml_file);
2242   check_io(xml_file);
2243 }
2244 
2245 
2246 /*
2247   Print xml tag with for a field that is null
2248 
2249   SYNOPSIS
2250     print_xml_null_tag()
2251     xml_file    - output file
2252     sbeg        - line beginning
2253     stag_atr    - tag and attribute
2254     sval        - value of attribute
2255     line_end        - line ending
2256 
2257   DESCRIPTION
2258     Print tag with one attribute to the xml_file. Format is:
2259       <stag_atr="sval" xsi:nil="true"/>
2260   NOTE
2261     sval MUST be a NULL terminated string.
2262     sval string will be qouted before output.
2263 */
2264 
print_xml_null_tag(FILE * xml_file,const char * sbeg,const char * stag_atr,const char * sval,const char * line_end)2265 static void print_xml_null_tag(FILE * xml_file, const char* sbeg,
2266                                const char* stag_atr, const char* sval,
2267                                const char* line_end)
2268 {
2269   fputs(sbeg, xml_file);
2270   fputs("<", xml_file);
2271   fputs(stag_atr, xml_file);
2272   fputs("\"", xml_file);
2273   print_quoted_xml(xml_file, sval, strlen(sval), 0);
2274   fputs("\" xsi:nil=\"true\" />", xml_file);
2275   fputs(line_end, xml_file);
2276   check_io(xml_file);
2277 }
2278 
2279 
2280 /**
2281   Print xml CDATA section.
2282 
2283   @param xml_file    - output file
2284   @param str         - string to print
2285   @param len         - length of the string
2286 
2287   @note
2288     This function also takes care of the presence of '[[>'
2289     string in the str. If found, the CDATA section is broken
2290     into two CDATA sections, <![CDATA[]]]]> and <![CDATA[>]].
2291 */
2292 
print_xml_cdata(FILE * xml_file,const char * str,ulong len)2293 static void print_xml_cdata(FILE *xml_file, const char *str, ulong len)
2294 {
2295   const char *end;
2296 
2297   fputs("<![CDATA[\n", xml_file);
2298   for (end= str + len; str != end; str++)
2299   {
2300     switch(*str) {
2301     case ']':
2302       if ((*(str + 1) == ']') && (*(str + 2) =='>'))
2303       {
2304         fputs("]]]]><![CDATA[>", xml_file);
2305         str += 2;
2306         continue;
2307       }
2308       /* fall through */
2309     default:
2310       fputc(*str, xml_file);
2311       break;
2312     }
2313   }
2314   fputs("\n]]>\n", xml_file);
2315   check_io(xml_file);
2316 }
2317 
2318 
2319 /*
2320   Print xml tag with many attributes.
2321 
2322   SYNOPSIS
2323     print_xml_row()
2324     xml_file    - output file
2325     row_name    - xml tag name
2326     tableRes    - query result
2327     row         - result row
2328     str_create  - create statement header string
2329 
2330   DESCRIPTION
2331     Print tag with many attribute to the xml_file. Format is:
2332       \t\t<row_name Atr1="Val1" Atr2="Val2"... />
2333   NOTE
2334     All atributes and values will be quoted before output.
2335 */
2336 
print_xml_row(FILE * xml_file,const char * row_name,MYSQL_RES * tableRes,MYSQL_ROW * row,const char * str_create)2337 static void print_xml_row(FILE *xml_file, const char *row_name,
2338                           MYSQL_RES *tableRes, MYSQL_ROW *row,
2339                           const char *str_create)
2340 {
2341   uint i;
2342   char *create_stmt_ptr= NULL;
2343   ulong create_stmt_len= 0;
2344   MYSQL_FIELD *field;
2345   ulong *lengths= mysql_fetch_lengths(tableRes);
2346 
2347   fprintf(xml_file, "\t\t<%s", row_name);
2348   check_io(xml_file);
2349   mysql_field_seek(tableRes, 0);
2350   for (i= 0; (field= mysql_fetch_field(tableRes)); i++)
2351   {
2352     if ((*row)[i])
2353     {
2354       /* For 'create' statements, dump using CDATA. */
2355       if ((str_create) && (strcmp(str_create, field->name) == 0))
2356       {
2357         create_stmt_ptr= (*row)[i];
2358         create_stmt_len= lengths[i];
2359       }
2360       else
2361       {
2362         fputc(' ', xml_file);
2363         print_quoted_xml(xml_file, field->name, field->name_length, 1);
2364         fputs("=\"", xml_file);
2365         print_quoted_xml(xml_file, (*row)[i], lengths[i], 0);
2366         fputc('"', xml_file);
2367         check_io(xml_file);
2368       }
2369     }
2370   }
2371 
2372   if (create_stmt_len)
2373   {
2374     fputs(">\n", xml_file);
2375     print_xml_cdata(xml_file, create_stmt_ptr, create_stmt_len);
2376     fprintf(xml_file, "\t\t</%s>\n", row_name);
2377   }
2378   else
2379     fputs(" />\n", xml_file);
2380 
2381   check_io(xml_file);
2382 }
2383 
2384 
2385 /**
2386   Print xml comments.
2387 
2388   @param xml_file       - output file
2389   @param len            - length of comment message
2390   @param comment_string - comment message
2391 
2392   @description
2393     Print the comment message in the format:
2394       "<!-- \n comment string  \n -->\n"
2395 
2396   @note
2397     Any occurrence of continuous hyphens will be
2398     squeezed to a single hyphen.
2399 */
2400 
print_xml_comment(FILE * xml_file,size_t len,const char * comment_string)2401 static void print_xml_comment(FILE *xml_file, size_t len,
2402                               const char *comment_string)
2403 {
2404   const char* end;
2405 
2406   fputs("<!-- ", xml_file);
2407 
2408   for (end= comment_string + len; comment_string != end; comment_string++)
2409   {
2410     /*
2411       The string "--" (double-hyphen) MUST NOT occur within xml comments.
2412     */
2413     switch (*comment_string) {
2414     case '-':
2415       if (*(comment_string + 1) == '-')         /* Only one hyphen allowed. */
2416         break;
2417       // Fall through.
2418     default:
2419       fputc(*comment_string, xml_file);
2420       break;
2421     }
2422   }
2423   fputs(" -->\n", xml_file);
2424   check_io(xml_file);
2425 }
2426 
2427 
2428 
2429 /* A common printing function for xml and non-xml modes. */
2430 
print_comment(FILE * sql_file,my_bool is_error,const char * format,...)2431 static void print_comment(FILE *sql_file, my_bool is_error, const char *format,
2432                           ...)
2433 {
2434   static char comment_buff[COMMENT_LENGTH];
2435   va_list args;
2436 
2437   /* If its an error message, print it ignoring opt_comments. */
2438   if (!is_error && !opt_comments)
2439     return;
2440 
2441   va_start(args, format);
2442   my_vsnprintf(comment_buff, COMMENT_LENGTH, format, args);
2443   va_end(args);
2444 
2445   if (!opt_xml)
2446   {
2447     fputs(comment_buff, sql_file);
2448     check_io(sql_file);
2449     return;
2450   }
2451 
2452   print_xml_comment(sql_file, strlen(comment_buff), comment_buff);
2453 }
2454 
2455 /**
2456   @brief Accepts object names and prefixes them with "-- " wherever
2457          end-of-line character ('\n') is found.
2458 
2459   @param[in]  object_name   object name list (concatenated string)
2460   @param[out] freemem       should buffer be released after usage
2461 
2462   @return
2463     @retval                 pointer to a string with prefixed objects
2464 */
fix_identifier_with_newline(char const * object_name,my_bool * freemem)2465 static char const* fix_identifier_with_newline(char const* object_name, my_bool* freemem)
2466 {
2467   const size_t PREFIX_LENGTH= 3; // strlen ("-- ")
2468 
2469   // static buffer for replacement procedure
2470   static char storage[NAME_LEN + 1];
2471   static char* buffer= storage;
2472   static size_t buffer_size= sizeof(storage) - 1;
2473   size_t index= 0;
2474   size_t required_size= 0;
2475 
2476   // we presume memory allocation won't be needed
2477   *freemem= FALSE;
2478 
2479   // traverse and reformat objects
2480   while (object_name && *object_name)
2481   {
2482     ++required_size;
2483     if (*object_name == '\n')
2484       required_size+= PREFIX_LENGTH;
2485 
2486     // do we need dynamic (re)allocation
2487     if (required_size > buffer_size)
2488     {
2489       // new alloc size increased in COMMENT_LENGTH multiple
2490       buffer_size= COMMENT_LENGTH * (1 + required_size/COMMENT_LENGTH);
2491 
2492       // is our buffer already dynamically allocated
2493       if (*freemem)
2494       {
2495         // just realloc
2496         buffer= (char*)my_realloc(PSI_NOT_INSTRUMENTED, buffer, buffer_size + 1,
2497                                   MYF(MY_WME));
2498         if (!buffer)
2499           exit(1);
2500       }
2501       else
2502       {
2503         // dynamic allocation + copy from static buffer
2504         buffer= (char*)my_malloc(PSI_NOT_INSTRUMENTED, buffer_size + 1,
2505                                  MYF(MY_WME));
2506         if (!buffer)
2507           exit(1);
2508 
2509         strncpy(buffer, storage, index);
2510         *freemem= TRUE;
2511       }
2512     }
2513 
2514     // copy a character
2515     buffer[index]= *object_name;
2516     ++index;
2517 
2518     // prefix new lines with double dash
2519     if (*object_name == '\n')
2520     {
2521       strcpy(buffer + index, "-- ");
2522       index += PREFIX_LENGTH;
2523     }
2524 
2525     ++object_name;
2526   }
2527 
2528   // don't forget null termination
2529   buffer[index]= '\0';
2530   return buffer;
2531 }
2532 
2533 
2534 /*
2535  create_delimiter
2536  Generate a new (null-terminated) string that does not exist in  query
2537  and is therefore suitable for use as a query delimiter.  Store this
2538  delimiter in  delimiter_buff .
2539 
2540  This is quite simple in that it doesn't even try to parse statements as an
2541  interpreter would.  It merely returns a string that is not in the query, which
2542  is much more than adequate for constructing a delimiter.
2543 
2544  RETURN
2545    ptr to the delimiter  on Success
2546    NULL                  on Failure
2547 */
create_delimiter(char * query,char * delimiter_buff,int delimiter_max_size)2548 static char *create_delimiter(char *query, char *delimiter_buff,
2549                               int delimiter_max_size)
2550 {
2551   int proposed_length;
2552   char *presence;
2553 
2554   delimiter_buff[0]= ';';  /* start with one semicolon, and */
2555 
2556   for (proposed_length= 2; proposed_length < delimiter_max_size;
2557       delimiter_max_size++) {
2558 
2559     delimiter_buff[proposed_length-1]= ';';  /* add semicolons, until */
2560     delimiter_buff[proposed_length]= '\0';
2561 
2562     presence = strstr(query, delimiter_buff);
2563     if (presence == NULL) { /* the proposed delimiter is not in the query. */
2564        return delimiter_buff;
2565     }
2566 
2567   }
2568   return NULL;  /* but if we run out of space, return nothing at all. */
2569 }
2570 
2571 
2572 /*
2573   dump_events_for_db
2574   -- retrieves list of events for a given db, and prints out
2575   the CREATE EVENT statement into the output (the dump).
2576 
2577   RETURN
2578     0  Success
2579     1  Error
2580 */
dump_events_for_db(char * db)2581 static uint dump_events_for_db(char *db)
2582 {
2583   char       query_buff[QUERY_LENGTH];
2584   char       db_name_buff[NAME_LEN*2+3], name_buff[NAME_LEN*2+3];
2585   char       *event_name;
2586   char       delimiter[QUERY_LENGTH];
2587   FILE       *sql_file= md_result_file;
2588   MYSQL_RES  *event_res, *event_list_res;
2589   MYSQL_ROW  row, event_list_row;
2590 
2591   char       db_cl_name[MY_CS_NAME_SIZE];
2592   int        db_cl_altered= FALSE;
2593   my_bool    freemem= FALSE;
2594   char const *text= fix_identifier_with_newline(db, &freemem);
2595 
2596   DBUG_ENTER("dump_events_for_db");
2597   DBUG_PRINT("enter", ("db: '%s'", db));
2598 
2599   mysql_real_escape_string_quote(mysql, db_name_buff,
2600                                  db, (ulong)strlen(db), '\'');
2601   /* nice comments */
2602   print_comment(sql_file, 0,
2603                 "\n--\n-- Dumping events for database '%s'\n--\n",
2604                 text);
2605   if (freemem)
2606     my_free((void*)text);
2607 
2608   /*
2609     not using "mysql_query_with_error_report" because we may have not
2610     enough privileges to lock mysql.events.
2611   */
2612   if (lock_tables)
2613     mysql_query(mysql, "LOCK TABLES mysql.event READ");
2614 
2615   if (mysql_query_with_error_report(mysql, &event_list_res, "show events"))
2616     DBUG_RETURN(0);
2617 
2618   strcpy(delimiter, ";");
2619   if (mysql_num_rows(event_list_res) > 0)
2620   {
2621     if (opt_xml)
2622       fputs("\t<events>\n", sql_file);
2623     else
2624     {
2625       fprintf(sql_file, "/*!50106 SET @save_time_zone= @@TIME_ZONE */ ;\n");
2626 
2627       /* Get database collation. */
2628 
2629       if (fetch_db_collation(db_name_buff, db_cl_name, sizeof (db_cl_name)))
2630         DBUG_RETURN(1);
2631     }
2632 
2633     if (switch_character_set_results(mysql, "binary"))
2634       DBUG_RETURN(1);
2635 
2636     while ((event_list_row= mysql_fetch_row(event_list_res)) != NULL)
2637     {
2638       event_name= quote_name(event_list_row[1], name_buff, 0);
2639       DBUG_PRINT("info", ("retrieving CREATE EVENT for %s", name_buff));
2640       my_snprintf(query_buff, sizeof(query_buff), "SHOW CREATE EVENT %s",
2641           event_name);
2642 
2643       if (mysql_query_with_error_report(mysql, &event_res, query_buff))
2644         DBUG_RETURN(1);
2645 
2646       while ((row= mysql_fetch_row(event_res)) != NULL)
2647       {
2648         if (opt_xml)
2649         {
2650           print_xml_row(sql_file, "event", event_res, &row,
2651                         "Create Event");
2652           continue;
2653         }
2654 
2655         /*
2656           if the user has EXECUTE privilege he can see event names, but not the
2657           event body!
2658         */
2659         if (strlen(row[3]) != 0)
2660         {
2661           char *query_str;
2662 
2663           if (opt_drop)
2664             fprintf(sql_file, "/*!50106 DROP EVENT IF EXISTS %s */%s\n",
2665                 event_name, delimiter);
2666 
2667           if (create_delimiter(row[3], delimiter, sizeof(delimiter)) == NULL)
2668           {
2669             fprintf(stderr, "%s: Warning: Can't create delimiter for event '%s'\n",
2670                     my_progname, event_name);
2671             DBUG_RETURN(1);
2672           }
2673 
2674           fprintf(sql_file, "DELIMITER %s\n", delimiter);
2675 
2676           if (mysql_num_fields(event_res) >= 7)
2677           {
2678             if (switch_db_collation(sql_file, db_name_buff, delimiter,
2679                                     db_cl_name, row[6], &db_cl_altered))
2680             {
2681               DBUG_RETURN(1);
2682             }
2683 
2684             switch_cs_variables(sql_file, delimiter,
2685                                 row[4],   /* character_set_client */
2686                                 row[4],   /* character_set_results */
2687                                 row[5]);  /* collation_connection */
2688           }
2689           else
2690           {
2691             /*
2692               mysqldump is being run against the server, that does not
2693               provide character set information in SHOW CREATE
2694               statements.
2695 
2696               NOTE: the dump may be incorrect, since character set
2697               information is required in order to restore event properly.
2698             */
2699 
2700             fprintf(sql_file,
2701                     "--\n"
2702                     "-- WARNING: old server version. "
2703                       "The following dump may be incomplete.\n"
2704                     "--\n");
2705           }
2706           remove_sql_mode(row[1], C_STRING_WITH_LEN("NO_AUTO_CREATE_USER"));
2707           switch_sql_mode(sql_file, delimiter, row[1]);
2708 
2709           switch_time_zone(sql_file, delimiter, row[2]);
2710 
2711           query_str= cover_definer_clause(row[3], strlen(row[3]),
2712                                           C_STRING_WITH_LEN("50117"),
2713                                           C_STRING_WITH_LEN("50106"),
2714                                           C_STRING_WITH_LEN(" EVENT"));
2715 
2716           fprintf(sql_file,
2717                   "/*!50106 %s */ %s\n",
2718                   (const char *) (query_str != NULL ? query_str : row[3]),
2719                   (const char *) delimiter);
2720 
2721           my_free(query_str);
2722           restore_time_zone(sql_file, delimiter);
2723           restore_sql_mode(sql_file, delimiter);
2724 
2725           if (mysql_num_fields(event_res) >= 7)
2726           {
2727             restore_cs_variables(sql_file, delimiter);
2728 
2729             if (db_cl_altered)
2730             {
2731               if (restore_db_collation(sql_file, db_name_buff, delimiter,
2732                                        db_cl_name))
2733                 DBUG_RETURN(1);
2734             }
2735           }
2736         }
2737       } /* end of event printing */
2738       mysql_free_result(event_res);
2739 
2740     } /* end of list of events */
2741     if (opt_xml)
2742     {
2743       fputs("\t</events>\n", sql_file);
2744       check_io(sql_file);
2745     }
2746     else
2747     {
2748       fprintf(sql_file, "DELIMITER ;\n");
2749       fprintf(sql_file, "/*!50106 SET TIME_ZONE= @save_time_zone */ ;\n");
2750     }
2751 
2752     if (switch_character_set_results(mysql, default_charset))
2753       DBUG_RETURN(1);
2754   }
2755   mysql_free_result(event_list_res);
2756 
2757   if (lock_tables)
2758     (void) mysql_query_with_error_report(mysql, 0, "UNLOCK TABLES");
2759   DBUG_RETURN(0);
2760 }
2761 
2762 
2763 /*
2764   Print hex value for blob data.
2765 
2766   SYNOPSIS
2767     print_blob_as_hex()
2768     output_file         - output file
2769     str                 - string to print
2770     len                 - its length
2771 
2772   DESCRIPTION
2773     Print hex value for blob data.
2774 */
2775 
print_blob_as_hex(FILE * output_file,const char * str,ulong len)2776 static void print_blob_as_hex(FILE *output_file, const char *str, ulong len)
2777 {
2778     /* sakaik got the idea to to provide blob's in hex notation. */
2779     const char *ptr= str, *end= ptr + len;
2780     for (; ptr < end ; ptr++)
2781       fprintf(output_file, "%02X", *((uchar *)ptr));
2782     check_io(output_file);
2783 }
2784 
2785 /*
2786   dump_routines_for_db
2787   -- retrieves list of routines for a given db, and prints out
2788   the CREATE PROCEDURE definition into the output (the dump).
2789 
2790   This function has logic to print the appropriate syntax depending on whether
2791   this is a procedure or functions
2792 
2793   RETURN
2794     0  Success
2795     1  Error
2796 */
2797 
dump_routines_for_db(char * db)2798 static uint dump_routines_for_db(char *db)
2799 {
2800   char       query_buff[QUERY_LENGTH];
2801   const char *routine_type[]= {"FUNCTION", "PROCEDURE"};
2802   char       db_name_buff[NAME_LEN*2+3], name_buff[NAME_LEN*2+3];
2803   char       *routine_name;
2804   int        i;
2805   FILE       *sql_file= md_result_file;
2806   MYSQL_RES  *routine_res, *routine_list_res;
2807   MYSQL_ROW  row, routine_list_row;
2808 
2809   char       db_cl_name[MY_CS_NAME_SIZE];
2810   int        db_cl_altered= FALSE;
2811   my_bool    freemem= FALSE;
2812   char const *text= fix_identifier_with_newline(db, &freemem);
2813 
2814   DBUG_ENTER("dump_routines_for_db");
2815   DBUG_PRINT("enter", ("db: '%s'", db));
2816 
2817   mysql_real_escape_string_quote(mysql, db_name_buff,
2818                                  db, (ulong)strlen(db), '\'');
2819   /* nice comments */
2820   print_comment(sql_file, 0,
2821                 "\n--\n-- Dumping routines for database '%s'\n--\n",
2822                 text);
2823 
2824   if (freemem)
2825     my_free((void*)text);
2826 
2827   /*
2828     not using "mysql_query_with_error_report" because we may have not
2829     enough privileges to lock mysql.proc.
2830   */
2831   if (lock_tables)
2832     mysql_query(mysql, "LOCK TABLES mysql.proc READ");
2833 
2834   /* Get database collation. */
2835 
2836   if (fetch_db_collation(db_name_buff, db_cl_name, sizeof (db_cl_name)))
2837     DBUG_RETURN(1);
2838 
2839   if (switch_character_set_results(mysql, "binary"))
2840     DBUG_RETURN(1);
2841 
2842   if (opt_xml)
2843     fputs("\t<routines>\n", sql_file);
2844 
2845   /* 0, retrieve and dump functions, 1, procedures */
2846   for (i= 0; i <= 1; i++)
2847   {
2848     my_snprintf(query_buff, sizeof(query_buff),
2849                 "SHOW %s STATUS WHERE Db = '%s'",
2850                 routine_type[i], db_name_buff);
2851 
2852     if (mysql_query_with_error_report(mysql, &routine_list_res, query_buff))
2853       DBUG_RETURN(1);
2854 
2855     if (mysql_num_rows(routine_list_res))
2856     {
2857 
2858       while ((routine_list_row= mysql_fetch_row(routine_list_res)))
2859       {
2860         routine_name= quote_name(routine_list_row[1], name_buff, 0);
2861         DBUG_PRINT("info", ("retrieving CREATE %s for %s", routine_type[i],
2862                             name_buff));
2863         my_snprintf(query_buff, sizeof(query_buff), "SHOW CREATE %s %s",
2864                     routine_type[i], routine_name);
2865 
2866         if (mysql_query_with_error_report(mysql, &routine_res, query_buff))
2867           DBUG_RETURN(1);
2868 
2869         while ((row= mysql_fetch_row(routine_res)))
2870         {
2871           /*
2872             if the user has EXECUTE privilege he see routine names, but NOT the
2873             routine body of other routines that are not the creator of!
2874           */
2875           DBUG_PRINT("info",("length of body for %s row[2] '%s' is %zu",
2876                              routine_name, row[2] ? row[2] : "(null)",
2877                              row[2] ? strlen(row[2]) : 0));
2878           if (row[2] == NULL)
2879           {
2880             my_bool freemem= FALSE;
2881             char const* text= fix_identifier_with_newline(current_user, &freemem);
2882 
2883             print_comment(sql_file, 1, "\n-- insufficient privileges to %s\n",
2884                           query_buff);
2885             print_comment(sql_file, 1,
2886                           "-- does %s have permissions on mysql.proc?\n\n",
2887                           text);
2888             if (freemem)
2889               my_free((void*)text);
2890 
2891             maybe_die(EX_MYSQLERR,"%s has insufficent privileges to %s!", current_user, query_buff);
2892           }
2893           else if (strlen(row[2]))
2894           {
2895             if (opt_xml)
2896             {
2897               if (i)                            // Procedures.
2898                 print_xml_row(sql_file, "routine", routine_res, &row,
2899                               "Create Procedure");
2900               else                              // Functions.
2901                 print_xml_row(sql_file, "routine", routine_res, &row,
2902                               "Create Function");
2903               continue;
2904             }
2905             if (opt_drop)
2906               fprintf(sql_file, "/*!50003 DROP %s IF EXISTS %s */;\n",
2907                       routine_type[i], routine_name);
2908 
2909             if (mysql_num_fields(routine_res) >= 6)
2910             {
2911               if (switch_db_collation(sql_file, db_name_buff, ";",
2912                                       db_cl_name, row[5], &db_cl_altered))
2913               {
2914                 DBUG_RETURN(1);
2915               }
2916 
2917               switch_cs_variables(sql_file, ";",
2918                                   row[3],   /* character_set_client */
2919                                   row[3],   /* character_set_results */
2920                                   row[4]);  /* collation_connection */
2921             }
2922             else
2923             {
2924               /*
2925                 mysqldump is being run against the server, that does not
2926                 provide character set information in SHOW CREATE
2927                 statements.
2928 
2929                 NOTE: the dump may be incorrect, since character set
2930                 information is required in order to restore stored
2931                 procedure/function properly.
2932               */
2933 
2934               fprintf(sql_file,
2935                       "--\n"
2936                       "-- WARNING: old server version. "
2937                         "The following dump may be incomplete.\n"
2938                       "--\n");
2939             }
2940 
2941             remove_sql_mode(row[1], C_STRING_WITH_LEN("NO_AUTO_CREATE_USER"));
2942             switch_sql_mode(sql_file, ";", row[1]);
2943 
2944             fprintf(sql_file,
2945                     "DELIMITER ;;\n"
2946                     "%s ;;\n"
2947                     "DELIMITER ;\n",
2948                     (const char *) row[2]);
2949 
2950             restore_sql_mode(sql_file, ";");
2951 
2952             if (mysql_num_fields(routine_res) >= 6)
2953             {
2954               restore_cs_variables(sql_file, ";");
2955 
2956               if (db_cl_altered)
2957               {
2958                 if (restore_db_collation(sql_file, db_name_buff, ";", db_cl_name))
2959                   DBUG_RETURN(1);
2960               }
2961             }
2962 
2963           }
2964         } /* end of routine printing */
2965         mysql_free_result(routine_res);
2966 
2967       } /* end of list of routines */
2968     }
2969     mysql_free_result(routine_list_res);
2970   } /* end of for i (0 .. 1)  */
2971 
2972   if (opt_xml)
2973   {
2974     fputs("\t</routines>\n", sql_file);
2975     check_io(sql_file);
2976   }
2977 
2978   if (switch_character_set_results(mysql, default_charset))
2979     DBUG_RETURN(1);
2980 
2981   if (lock_tables)
2982     (void) mysql_query_with_error_report(mysql, 0, "UNLOCK TABLES");
2983   DBUG_RETURN(0);
2984 }
2985 
2986 /* general_log or slow_log tables under mysql database */
general_log_or_slow_log_tables(const char * db,const char * table)2987 static inline my_bool general_log_or_slow_log_tables(const char *db,
2988                                                      const char *table)
2989 {
2990   return (!my_strcasecmp(charset_info, db, "mysql")) &&
2991           (!my_strcasecmp(charset_info, table, "general_log") ||
2992            !my_strcasecmp(charset_info, table, "slow_log"));
2993 }
2994 
2995 /*
2996  slave_master_info,slave_relay_log_info and gtid_executed tables under
2997  mysql database
2998 */
replication_metadata_tables(const char * db,const char * table)2999 static inline my_bool replication_metadata_tables(const char *db,
3000                                                   const char *table)
3001 {
3002   return (!my_strcasecmp(charset_info, db, "mysql")) &&
3003           (!my_strcasecmp(charset_info, table, "slave_master_info") ||
3004            !my_strcasecmp(charset_info, table, "slave_relay_log_info") ||
3005            !my_strcasecmp(charset_info, table, "gtid_executed"));
3006 }
3007 
3008 /*
3009   Find the first occurrence of a quoted identifier in a given string. Returns
3010   the pointer to the opening quote, and stores the pointer to the closing quote
3011   to the memory location pointed to by the 'end' argument,
3012 
3013   If no quoted identifiers are found, returns NULL (and the value pointed to by
3014   'end' is undefined in this case).
3015 */
3016 
parse_quoted_identifier(const char * str,const char ** end)3017 static const char *parse_quoted_identifier(const char *str,
3018                                             const char **end)
3019 {
3020   const char *from;
3021   const char *to;
3022 
3023   if (!(from= strchr(str, '`')))
3024     return NULL;
3025 
3026   to= from;
3027 
3028   while ((to= strchr(to + 1, '`'))) {
3029     /*
3030       Double backticks represent a backtick in identifier, rather than a quote
3031       character.
3032     */
3033     if (to[1] == '`')
3034     {
3035       to++;
3036       continue;
3037     }
3038 
3039     break;
3040   }
3041 
3042   if (to <= from + 1)
3043     return NULL;                                /* Empty identifier */
3044 
3045   *end= to;
3046 
3047   return from;
3048 }
3049 
3050 /*
3051   Parse the specified key definition string and check if the key contains an
3052   AUTO_INCREMENT column as the first key part. We only check for the first key
3053   part, because unlike MyISAM, InnoDB does not allow the AUTO_INCREMENT column
3054   as a secondary key column, i.e. the AUTO_INCREMENT column would not be
3055   considered indexed for such key specification.
3056 */
contains_autoinc_column(const char * autoinc_column,ssize_t autoinc_column_len,const char * keydef,key_type_t type)3057 static my_bool contains_autoinc_column(const char *autoinc_column,
3058                                        ssize_t autoinc_column_len,
3059                                        const char *keydef,
3060                                        key_type_t type)
3061 {
3062   const char *from, *to;
3063   uint idnum;
3064 
3065   assert(type != KEY_TYPE_NONE);
3066 
3067   if (autoinc_column == NULL)
3068     return FALSE;
3069 
3070   idnum= 0;
3071 
3072   /*
3073     There is only 1 iteration of the following loop for type == KEY_TYPE_PRIMARY
3074     and 2 iterations for type == KEY_TYPE_UNIQUE / KEY_TYPE_NON_UNIQUE.
3075   */
3076   while ((from= parse_quoted_identifier(keydef, &to)))
3077   {
3078     idnum++;
3079 
3080     /*
3081       Skip the check if it's the first identifier and we are processing a
3082       secondary key.
3083     */
3084     if ((type == KEY_TYPE_PRIMARY || idnum != 1) &&
3085         to - from - 1 == autoinc_column_len &&
3086         !strncmp(autoinc_column, from + 1, autoinc_column_len))
3087       return TRUE;
3088 
3089     /*
3090       Check only the first (for PRIMARY KEY) or the second (for secondary keys)
3091       quoted identifier.
3092     */
3093     if (idnum == 1 + MY_TEST(type != KEY_TYPE_PRIMARY))
3094       break;
3095 
3096     keydef= to + 1;
3097   }
3098 
3099   return FALSE;
3100 }
3101 
3102 
3103 /*
3104   Remove secondary/foreign key definitions from a given SHOW CREATE TABLE string
3105   and store them into a temporary list to be used later.
3106 
3107   SYNOPSIS
3108     skip_secondary_keys()
3109     create_str                SHOW CREATE TABLE output
3110     has_pk                    TRUE, if the table has PRIMARY KEY
3111                               (or UNIQUE key on non-nullable columns)
3112 
3113 
3114   DESCRIPTION
3115 
3116     Stores all lines starting with "KEY" or "UNIQUE KEY"
3117     into skipped_keys_list and removes them from the input string.
3118     Stores all CONSTRAINT/FOREIGN KEYS declarations into
3119     alter_constraints_list and removes them from the input string.
3120 */
3121 
skip_secondary_keys(char * create_str,my_bool has_pk)3122 static void skip_secondary_keys(char *create_str, my_bool has_pk)
3123 {
3124   char *ptr, *strend;
3125   char *last_comma= NULL;
3126   my_bool pk_processed= FALSE;
3127   char *autoinc_column= NULL;
3128   ssize_t autoinc_column_len= 0;
3129   my_bool has_autoinc= FALSE;
3130   key_type_t type;
3131   my_bool keys_processed= FALSE;
3132 
3133   strend= create_str + strlen(create_str);
3134 
3135   ptr= create_str;
3136   while (*ptr && !keys_processed)
3137   {
3138     char *tmp, *orig_ptr, c;
3139 
3140     orig_ptr= ptr;
3141     /* Skip leading whitespace */
3142     while (*ptr && my_isspace(charset_info, *ptr))
3143       ptr++;
3144 
3145     /* Read the next line */
3146     for (tmp= ptr; *tmp != '\n' && *tmp != '\0'; tmp++);
3147 
3148     c= *tmp;
3149     *tmp= '\0'; /* so strstr() only processes the current line */
3150 
3151     if (!strncmp(ptr, "CONSTRAINT ", sizeof("CONSTRAINT ") - 1))
3152       type= KEY_TYPE_CONSTRAINT;
3153     else if (!strncmp(ptr, "UNIQUE KEY ", sizeof("UNIQUE KEY ") - 1))
3154       type= KEY_TYPE_UNIQUE;
3155     else if (!strncmp(ptr, "KEY ", sizeof("KEY ") - 1))
3156       type= KEY_TYPE_NON_UNIQUE;
3157     else if (!strncmp(ptr, "PRIMARY KEY ", sizeof("PRIMARY KEY ") - 1))
3158       type= KEY_TYPE_PRIMARY;
3159     else
3160       type= KEY_TYPE_NONE;
3161 
3162     has_autoinc= (type != KEY_TYPE_NONE)
3163       ? contains_autoinc_column(autoinc_column, autoinc_column_len, ptr, type)
3164       : FALSE;
3165 
3166     /* Is it a secondary index definition? */
3167     if (c == '\n' && !has_autoinc &&
3168         ((type == KEY_TYPE_UNIQUE && (pk_processed || !has_pk)) ||
3169          type == KEY_TYPE_NON_UNIQUE || type == KEY_TYPE_CONSTRAINT))
3170     {
3171       char *data, *end= tmp - 1;
3172 
3173       /* Remove the trailing comma */
3174       if (*end == ',')
3175         end--;
3176       data= my_strndup(PSI_NOT_INSTRUMENTED, ptr, end - ptr + 1, MYF(MY_FAE));
3177 
3178       if (type == KEY_TYPE_CONSTRAINT)
3179         alter_constraints_list= list_cons(data, alter_constraints_list);
3180       else
3181         skipped_keys_list= list_cons(data, skipped_keys_list);
3182 
3183       memmove(orig_ptr, tmp + 1, strend - tmp);
3184       ptr= orig_ptr;
3185       strend-= tmp + 1 - ptr;
3186 
3187       /* Remove the comma on the previos line */
3188       if (last_comma != NULL)
3189       {
3190         *last_comma= ' ';
3191       }
3192     }
3193     else
3194     {
3195       char *end;
3196 
3197       if (last_comma != NULL && *ptr == ')')
3198       {
3199         keys_processed= TRUE;
3200       }
3201       else if (last_comma != NULL && !keys_processed)
3202       {
3203         /*
3204           It's not the last line of CREATE TABLE, so we have skipped a key
3205           definition. We have to restore the last removed comma.
3206         */
3207         *last_comma= ',';
3208       }
3209 
3210       /*
3211         If we are skipping a key which indexes an AUTO_INCREMENT column, it is
3212         safe to optimize all subsequent keys, i.e. we should not be checking for
3213         that column anymore.
3214       */
3215       if (type != KEY_TYPE_NONE && has_autoinc)
3216       {
3217           assert(autoinc_column != NULL);
3218 
3219           my_free(autoinc_column);
3220           autoinc_column= NULL;
3221       }
3222 
3223       if ((has_pk && type == KEY_TYPE_UNIQUE && !pk_processed) ||
3224           type == KEY_TYPE_PRIMARY)
3225         pk_processed= TRUE;
3226 
3227       if (strstr(ptr, "AUTO_INCREMENT") && *ptr == '`')
3228       {
3229         /*
3230           The first secondary key defined on this column later cannot be
3231           skipped, as CREATE TABLE would fail on import. Unless there is a
3232           PRIMARY KEY and it indexes that column.
3233         */
3234         for (end= ptr + 1;
3235              /* Skip double backticks as they are a part of identifier */
3236              *end != '\0' && (*end != '`' || end[1] == '`');
3237              end++)
3238           /* empty */;
3239 
3240         if (*end == '`' && end > ptr + 1)
3241         {
3242           assert(autoinc_column == NULL);
3243 
3244           autoinc_column_len= end - ptr - 1;
3245           autoinc_column= my_strndup(PSI_NOT_INSTRUMENTED, ptr + 1,
3246                                      autoinc_column_len, MYF(MY_FAE));
3247         }
3248       }
3249 
3250       *tmp= c;
3251 
3252       if (tmp[-1] == ',')
3253         last_comma= tmp - 1;
3254       ptr= (*tmp == '\0') ? tmp : tmp + 1;
3255     }
3256   }
3257 
3258   my_free(autoinc_column);
3259 }
3260 
3261 /**
3262   Removes some compressed columns extensions from the create table
3263   definition (a string produced by SHOW CREATE TABLE) depending on
3264   opt_compressed_columns and opt_compressed_columns_with_dictionaries flags.
3265   If opt_compressed_columns_with_dictionaries flags is true, in addition
3266   dictionaries list will be filled with referenced compression
3267   dictionaries.
3268 
3269   @param create_str     SHOW CREATE TABLE output
3270   @param dictionaries   the list of dictionary names found in the
3271                         create table definition
3272 */
skip_compressed_columns(char * create_str,LIST ** dictionaries)3273 static void skip_compressed_columns(char *create_str, LIST **dictionaries)
3274 {
3275   static const char prefix[]=
3276     " /*!"
3277     STRINGIFY_ARG(FIRST_SUPPORTED_COMPRESSED_COLUMNS_VERSION)
3278     " COLUMN_FORMAT COMPRESSED";
3279   static const size_t prefix_length= sizeof(prefix) - 1;
3280   static const char suffix[]= " */";
3281   static const size_t suffix_length= sizeof(suffix) - 1;
3282   static const char dictionary_keyword[]=" WITH COMPRESSION_DICTIONARY ";
3283   static const size_t dictionary_keyword_length=
3284     sizeof(dictionary_keyword) - 1;
3285 
3286   char *ptr, *end_ptr, *prefix_ptr, *suffix_ptr, *dictionary_keyword_ptr;
3287 
3288   DBUG_ENTER("skip_compressed_columns");
3289 
3290   ptr= create_str;
3291   end_ptr= ptr + strlen(create_str);
3292   if (opt_compressed_columns_with_dictionaries && dictionaries != 0)
3293     *dictionaries= 0;
3294 
3295   while ((prefix_ptr= strstr(ptr, prefix)) != 0)
3296   {
3297     suffix_ptr= strstr(prefix_ptr + prefix_length, suffix);
3298     assert(suffix_ptr != 0);
3299     if (!opt_compressed_columns_with_dictionaries)
3300     {
3301       if (!opt_compressed_columns)
3302       {
3303         /* Strip out all compressed columns extensions. */
3304         memmove(prefix_ptr, suffix_ptr + suffix_length,
3305                 end_ptr - (suffix_ptr + suffix_length) + 1);
3306         end_ptr-= suffix_ptr + suffix_length - prefix_ptr;
3307         ptr= prefix_ptr;
3308       }
3309       else
3310       {
3311         /* Strip out only compression dictionary references. */
3312         memmove(prefix_ptr + prefix_length, suffix_ptr,
3313           end_ptr - suffix_ptr + 1);
3314         end_ptr-= suffix_ptr - (prefix_ptr + prefix_length);
3315         ptr= prefix_ptr + prefix_length + suffix_length;
3316       }
3317     }
3318     else
3319     {
3320       /* Do not strip out anything. Leave full column definition as is. */
3321       if (dictionaries !=0 && prefix_ptr + prefix_length != suffix_ptr)
3322       {
3323         char *dictionary_name_ptr;
3324         size_t dictionary_name_length;
3325         char opt_quoted_buff[NAME_LEN * 2 + 3],
3326              unquoted_buff[NAME_LEN * 2 + 3];
3327 
3328         dictionary_keyword_ptr= strstr(prefix_ptr + prefix_length,
3329                                        dictionary_keyword);
3330         assert(dictionary_keyword_ptr < suffix_ptr);
3331         dictionary_name_length= suffix_ptr -
3332           (dictionary_keyword_ptr + dictionary_keyword_length);
3333 
3334         strncpy(opt_quoted_buff,
3335           dictionary_keyword_ptr + dictionary_keyword_length,
3336           dictionary_name_length);
3337         opt_quoted_buff[dictionary_name_length]= '\0';
3338 
3339         dictionary_name_ptr=
3340           my_strdup(PSI_NOT_INSTRUMENTED,
3341                     unquote_name(opt_quoted_buff, unquoted_buff), MYF(0));
3342         if (dictionary_name_ptr == 0)
3343           die(EX_MYSQLERR, "Couldn't allocate memory");
3344 
3345         list_push(*dictionaries, dictionary_name_ptr);
3346       }
3347       ptr= suffix_ptr + suffix_length;
3348     }
3349   }
3350   DBUG_VOID_RETURN;
3351 }
3352 
3353 
3354 /*
3355   Check if the table has a primary key defined either explicitly or
3356   implicitly (i.e. a unique key on non-nullable columns).
3357 
3358   SYNOPSIS
3359     my_bool has_primary_key(const char *table_name)
3360 
3361     table_name  quoted table name
3362 
3363   RETURNS     TRUE if the table has a primary key
3364 
3365   DESCRIPTION
3366 */
3367 
has_primary_key(const char * table_name)3368 static my_bool has_primary_key(const char *table_name)
3369 {
3370   MYSQL_RES  *res= NULL;
3371   MYSQL_ROW  row;
3372   char query_buff[QUERY_LENGTH];
3373   my_bool has_pk= TRUE;
3374 
3375   my_snprintf(query_buff, sizeof(query_buff),
3376               "SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE "
3377               "TABLE_SCHEMA=DATABASE() AND TABLE_NAME='%s' AND "
3378               "COLUMN_KEY='PRI'", table_name);
3379   if (mysql_query(mysql, query_buff) || !(res= mysql_store_result(mysql)) ||
3380       !(row= mysql_fetch_row(res)))
3381   {
3382     fprintf(stderr, "%s: Warning: Couldn't determine if table %s has a "
3383             "primary key (%s). "
3384             "--innodb-optimize-keys may work inefficiently.\n",
3385             my_progname, table_name, mysql_error(mysql));
3386     goto cleanup;
3387   }
3388 
3389   has_pk= atoi(row[0]) > 0;
3390 
3391 cleanup:
3392   if (res)
3393     mysql_free_result(res);
3394 
3395   return has_pk;
3396 }
3397 
3398 /**
3399   Prints "CREATE COMPRESSION_DICTIONARY ..." statement for the specified
3400   dictionary name if this is the first time this dictionary is referenced.
3401 
3402   @param sql_file          output file
3403   @param dictionary_name   dictionary name
3404 */
print_optional_create_compression_dictionary(FILE * sql_file,const char * dictionary_name)3405 static void print_optional_create_compression_dictionary(FILE* sql_file,
3406   const char *dictionary_name)
3407 {
3408   DBUG_ENTER("print_optional_create_compression_dictionary");
3409   DBUG_PRINT("enter", ("dictionary: %s", dictionary_name));
3410 
3411   /*
3412     We skip this compression dictionary if it has already been processed
3413   */
3414   if (my_hash_search(&processed_compression_dictionaries,
3415     (const uchar *)dictionary_name, strlen(dictionary_name)) == 0)
3416   {
3417     static const char get_zip_dict_data_stmt[] =
3418       "SELECT `ZIP_DICT` "
3419       "FROM `INFORMATION_SCHEMA`.`XTRADB_ZIP_DICT` "
3420       "WHERE `NAME` = '%s'";
3421 
3422     char quoted_buff[NAME_LEN * 2 + 3];
3423     char *quoted_dictionary_name;
3424     char query_buff[QUERY_LENGTH];
3425     MYSQL_RES *result= 0;
3426     MYSQL_ROW row;
3427     ulong *lengths;
3428 
3429     if (my_hash_insert(&processed_compression_dictionaries,
3430       (uchar*)my_strdup(PSI_NOT_INSTRUMENTED, dictionary_name, MYF(0))))
3431       die(EX_MYSQLERR, "Couldn't allocate memory");
3432 
3433     my_snprintf(query_buff, sizeof(query_buff), get_zip_dict_data_stmt,
3434                 dictionary_name);
3435 
3436     if (mysql_query_with_error_report(mysql, &result, query_buff))
3437     {
3438       DBUG_VOID_RETURN;
3439     }
3440 
3441     row= mysql_fetch_row(result);
3442     if (row == 0)
3443     {
3444       mysql_free_result(result);
3445       maybe_die(EX_MYSQLERR,
3446                 "Couldn't read data for compresion dictionary %s (%s)\n",
3447                 dictionary_name, mysql_error(mysql));
3448       DBUG_VOID_RETURN;
3449     }
3450     lengths= mysql_fetch_lengths(result);
3451     assert(lengths != 0);
3452 
3453     quoted_dictionary_name= quote_name(dictionary_name, quoted_buff, 0);
3454 
3455     /*
3456       We print DROP COMPRESSION_DICTIONARY only if no --tab
3457       (file per table option) and no --skip-add-drop-compression-dictionary
3458       were specified
3459     */
3460     if (path == 0 && opt_drop_compression_dictionary)
3461     {
3462       fprintf(sql_file,
3463         "/*!"  STRINGIFY_ARG(FIRST_SUPPORTED_COMPRESSED_COLUMNS_VERSION)
3464         " DROP COMPRESSION_DICTIONARY IF EXISTS %s */;\n",
3465         quoted_dictionary_name);
3466       check_io(sql_file);
3467     }
3468 
3469     /*
3470       Whether IF NOT EXISTS is added to CREATE COMPRESSION_DICTIONARY
3471       depends on the --add-drop-compression-dictionary /
3472       --skip-add-drop-compression-dictionary options.
3473     */
3474     fprintf(sql_file,
3475       "/*!"  STRINGIFY_ARG(FIRST_SUPPORTED_COMPRESSED_COLUMNS_VERSION)
3476       " CREATE COMPRESSION_DICTIONARY %s%s (",
3477       path != 0 ? "IF NOT EXISTS " : "",
3478       quoted_dictionary_name);
3479     check_io(sql_file);
3480 
3481     unescape(sql_file, row[0], lengths[0]);
3482     fputs(") */;\n", sql_file);
3483     check_io(sql_file);
3484 
3485     mysql_free_result(result);
3486   }
3487   DBUG_VOID_RETURN;
3488 }
3489 
3490 /**
3491   Checks if --add-drop-table option is enabled and prints
3492   "DROP TABLE IF EXISTS ..." if the specified table is not a log table.
3493 
3494   @param sq_file            output file
3495   @param db                 db name
3496   @param table              table name
3497   @param opt_quoted_table   optionally quoted table name
3498 */
print_optional_drop_table(FILE * sql_file,const char * db,const char * table,const char * opt_quoted_table)3499 static void print_optional_drop_table(FILE *sql_file, const char* db,
3500                                       const char *table,
3501                                       const char *opt_quoted_table)
3502 {
3503   DBUG_ENTER("print_optional_drop_table");
3504   DBUG_PRINT("enter", ("db: %s  table: %s", db, table));
3505   if (opt_drop)
3506   {
3507     if (!(general_log_or_slow_log_tables(db, table) ||
3508           replication_metadata_tables(db, table)))
3509     {
3510       fprintf(sql_file, "DROP TABLE IF EXISTS %s;\n", opt_quoted_table);
3511       check_io(sql_file);
3512     }
3513   }
3514   DBUG_VOID_RETURN;
3515 }
3516 /*
3517   get_table_structure -- retrievs database structure, prints out corresponding
3518   CREATE statement and fills out insert_pat if the table is the type we will
3519   be dumping.
3520 
3521   ARGS
3522     table       - table name
3523     db          - db name
3524     table_type  - table type, e.g. "MyISAM" or "InnoDB", but also "VIEW"
3525     ignore_flag - what we must particularly ignore - see IGNORE_ defines above
3526     real_columns- Contains one byte per column, 0 means unused, 1 is used
3527                   Generated columns are marked as unused
3528   RETURN
3529     number of fields in table, 0 if error
3530 */
3531 
get_table_structure(char * table,char * db,char * table_type,char * ignore_flag,my_bool real_columns[])3532 static uint get_table_structure(char *table, char *db, char *table_type,
3533                                 char *ignore_flag, my_bool real_columns[])
3534 {
3535   my_bool    init=0, write_data, complete_insert;
3536   my_ulonglong num_fields;
3537   char       *result_table, *opt_quoted_table;
3538   const char *insert_option;
3539   char	     name_buff[NAME_LEN+3],table_buff[NAME_LEN*2+3];
3540   char       table_buff2[NAME_LEN*2+3], query_buff[QUERY_LENGTH];
3541   const char *show_fields_stmt= "SELECT `COLUMN_NAME` AS `Field`, "
3542                                 "`COLUMN_TYPE` AS `Type`, "
3543                                 "`IS_NULLABLE` AS `Null`, "
3544                                 "`COLUMN_KEY` AS `Key`, "
3545                                 "`COLUMN_DEFAULT` AS `Default`, "
3546                                 "`EXTRA` AS `Extra`, "
3547                                 "`COLUMN_COMMENT` AS `Comment` "
3548                                 "FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE "
3549                                 "TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s'";
3550   FILE       *sql_file= md_result_file;
3551   size_t     len;
3552   my_bool    is_log_table;
3553   my_bool    is_replication_metadata_table;
3554   unsigned int colno;
3555   my_bool    is_innodb_table;
3556   MYSQL_RES  *result;
3557   MYSQL_ROW  row;
3558   my_bool    has_pk= FALSE;
3559   DBUG_ENTER("get_table_structure");
3560   DBUG_PRINT("enter", ("db: %s  table: %s", db, table));
3561 
3562   *ignore_flag= check_if_ignore_table(table, table_type);
3563 
3564   complete_insert= 0;
3565   if ((write_data= !(*ignore_flag & IGNORE_DATA)))
3566   {
3567     complete_insert= opt_complete_insert;
3568     if (!insert_pat_inited)
3569     {
3570       insert_pat_inited= 1;
3571       init_dynamic_string_checked(&insert_pat, "", 1024, 1024);
3572     }
3573     else
3574       dynstr_set_checked(&insert_pat, "");
3575   }
3576 
3577   insert_option= (opt_ignore ? " IGNORE " : "");
3578 
3579   verbose_msg("-- Retrieving table structure for table %s...\n", table);
3580 
3581   len= my_snprintf(query_buff, sizeof(query_buff),
3582                    "SET SQL_QUOTE_SHOW_CREATE=%d",
3583                    (opt_quoted || opt_keywords));
3584   if (!create_options)
3585     my_stpcpy(query_buff+len,
3586            "/*!40102 ,SQL_MODE=concat(@@sql_mode, _utf8 ',NO_KEY_OPTIONS,NO_TABLE_OPTIONS,NO_FIELD_OPTIONS') */");
3587 
3588   result_table=     quote_name(table, table_buff, 1);
3589   opt_quoted_table= quote_name(table, table_buff2, 0);
3590 
3591   if (opt_innodb_optimize_keys && !strcmp(table_type, "InnoDB"))
3592     has_pk= has_primary_key(table);
3593 
3594   if (opt_order_by_primary || opt_order_by_primary_desc)
3595     order_by= primary_key_fields(result_table,
3596                                  opt_order_by_primary_desc ? TRUE : FALSE);
3597 
3598   if (!opt_xml && !mysql_query_with_error_report(mysql, 0, query_buff))
3599   {
3600     /* using SHOW CREATE statement */
3601     if (!opt_no_create_info)
3602     {
3603       /* Make an sql-file, if path was given iow. option -T was given */
3604       char buff[20+FN_REFLEN];
3605       MYSQL_FIELD *field;
3606       my_bool freemem= FALSE;
3607       char const *text;
3608 
3609       my_snprintf(buff, sizeof(buff), "show create table %s", result_table);
3610 
3611       if (switch_character_set_results(mysql, "binary") ||
3612           mysql_query_with_error_report(mysql, &result, buff) ||
3613           switch_character_set_results(mysql, default_charset))
3614         DBUG_RETURN(0);
3615 
3616       if (path)
3617       {
3618         if (!(sql_file= open_sql_file_for_table(table, O_WRONLY)))
3619           DBUG_RETURN(0);
3620 
3621         write_header(sql_file, db);
3622       }
3623 
3624       text= fix_identifier_with_newline(result_table, &freemem);
3625       if (strcmp (table_type, "VIEW") == 0)         /* view */
3626         print_comment(sql_file, 0,
3627                       "\n--\n-- Temporary table structure for view %s\n--\n\n",
3628                       text);
3629       else
3630         print_comment(sql_file, 0,
3631                       "\n--\n-- Table structure for table %s\n--\n\n", text);
3632 
3633       if (freemem)
3634         my_free((void*)text);
3635 
3636       field= mysql_fetch_field_direct(result, 0);
3637       if (strcmp(field->name, "View") == 0)
3638       {
3639         char *scv_buff= NULL;
3640         my_ulonglong n_cols;
3641 
3642         /*
3643           Even if the "table" is a view, we do a DROP TABLE here.  The
3644           view-specific code below fills in the DROP VIEW.
3645           We will skip the DROP TABLE for general_log and slow_log, since
3646           those stmts will fail, in case we apply dump by enabling logging.
3647           We will skip this for replication metadata tables as well.
3648         */
3649         print_optional_drop_table(sql_file, db, table, opt_quoted_table);
3650 
3651         verbose_msg("-- It's a view, create dummy view\n");
3652 
3653         /* save "show create" statement for later */
3654         if ((row= mysql_fetch_row(result)) && (scv_buff=row[1]))
3655           scv_buff= my_strdup(PSI_NOT_INSTRUMENTED,
3656                               scv_buff, MYF(0));
3657 
3658         mysql_free_result(result);
3659 
3660         /*
3661           Create a table with the same name as the view and with columns of
3662           the same name in order to satisfy views that depend on this view.
3663           The table will be removed when the actual view is created.
3664 
3665           The properties of each column, are not preserved in this temporary
3666           table, because they are not necessary.
3667 
3668           This will not be necessary once we can determine dependencies
3669           between views and can simply dump them in the appropriate order.
3670         */
3671         my_snprintf(query_buff, sizeof(query_buff),
3672                     "SHOW FIELDS FROM %s", result_table);
3673         if (switch_character_set_results(mysql, "binary") ||
3674             mysql_query_with_error_report(mysql, &result, query_buff) ||
3675             switch_character_set_results(mysql, default_charset))
3676         {
3677           /*
3678             View references invalid or privileged table/col/fun (err 1356),
3679             so we cannot create a stand-in table.  Be defensive and dump
3680             a comment with the view's 'show create' statement. (Bug #17371)
3681           */
3682 
3683           if (mysql_errno(mysql) == ER_VIEW_INVALID)
3684             fprintf(sql_file, "\n-- failed on view %s: %s\n\n", result_table, scv_buff ? scv_buff : "");
3685 
3686           my_free(scv_buff);
3687 
3688           DBUG_RETURN(0);
3689         }
3690         else
3691           my_free(scv_buff);
3692 
3693         n_cols= mysql_num_rows(result);
3694         if (0 != n_cols)
3695         {
3696 
3697           /*
3698             The actual formula is based on the column names and how the .FRM
3699             files are stored and is too volatile to be repeated here.
3700             Thus we simply warn the user if the columns exceed a limit we
3701             know works most of the time.
3702           */
3703           if (n_cols >= 1000)
3704             fprintf(stderr,
3705                     "-- Warning: Creating a stand-in table for view %s may"
3706                     " fail when replaying the dump file produced because "
3707                     "of the number of columns exceeding 1000. Exercise "
3708                     "caution when replaying the produced dump file.\n",
3709                     table);
3710           if (opt_drop)
3711           {
3712             /*
3713               We have already dropped any table of the same name above, so
3714               here we just drop the view.
3715             */
3716 
3717             fprintf(sql_file, "/*!50001 DROP VIEW IF EXISTS %s*/;\n",
3718                     opt_quoted_table);
3719             check_io(sql_file);
3720           }
3721 
3722           fprintf(sql_file,
3723                   "SET @saved_cs_client     = @@character_set_client;\n"
3724                   "SET character_set_client = utf8;\n"
3725                   "/*!50001 CREATE VIEW %s AS SELECT \n",
3726                   result_table);
3727 
3728           /*
3729             Get first row, following loop will prepend comma - keeps from
3730             having to know if the row being printed is last to determine if
3731             there should be a _trailing_ comma.
3732           */
3733 
3734 
3735           row= mysql_fetch_row(result);
3736 
3737           /*
3738             A temporary view is created to resolve the view interdependencies.
3739             This temporary view is dropped when the actual view is created.
3740           */
3741 
3742           fprintf(sql_file, " 1 AS %s",
3743                   quote_name(row[0], name_buff, 0));
3744 
3745           while((row= mysql_fetch_row(result)))
3746           {
3747             fprintf(sql_file, ",\n 1 AS %s",
3748                     quote_name(row[0], name_buff, 0));
3749           }
3750 
3751           fprintf(sql_file,"*/;\n"
3752                   "SET character_set_client = @saved_cs_client;\n");
3753 
3754           check_io(sql_file);
3755         }
3756 
3757         mysql_free_result(result);
3758 
3759         if (path)
3760           my_fclose(sql_file, MYF(MY_WME));
3761 
3762         seen_views= 1;
3763         DBUG_RETURN(0);
3764       }
3765 
3766       row= mysql_fetch_row(result);
3767 
3768       is_innodb_table= (strcmp(table_type, "InnoDB") == 0);
3769       if (opt_innodb_optimize_keys && is_innodb_table)
3770         skip_secondary_keys(row[1], has_pk);
3771       if (is_innodb_table)
3772       {
3773         /*
3774           Search for compressed columns attributes and remove them if
3775           necessary.
3776         */
3777 
3778         LIST *referenced_dictionaries= 0, *current_dictionary;
3779 
3780         skip_compressed_columns(row[1], &referenced_dictionaries);
3781         referenced_dictionaries= list_reverse(referenced_dictionaries);
3782         for (current_dictionary= referenced_dictionaries;
3783              current_dictionary != 0;
3784              current_dictionary= list_rest(current_dictionary))
3785         {
3786           print_optional_create_compression_dictionary(
3787             sql_file, (const char*)current_dictionary->data);
3788         }
3789         list_free(referenced_dictionaries, TRUE);
3790       }
3791 
3792       print_optional_drop_table(sql_file, db, table, opt_quoted_table);
3793 
3794       is_log_table= general_log_or_slow_log_tables(db, table);
3795       is_replication_metadata_table= replication_metadata_tables(db, table);
3796       if (is_log_table || is_replication_metadata_table)
3797         row[1]+= 13; /* strlen("CREATE TABLE ")= 13 */
3798       if (opt_compatible_mode & 3)
3799       {
3800         fprintf(sql_file,
3801                 (is_log_table || is_replication_metadata_table) ?
3802                 "CREATE TABLE IF NOT EXISTS %s;\n" : "%s;\n", row[1]);
3803       }
3804       else
3805       {
3806         fprintf(sql_file,
3807                 "/*!40101 SET @saved_cs_client     = @@character_set_client */;\n"
3808                 "/*!40101 SET character_set_client = utf8 */;\n"
3809                 "%s%s;\n"
3810                 "/*!40101 SET character_set_client = @saved_cs_client */;\n",
3811                 (is_log_table || is_replication_metadata_table) ?
3812                 "CREATE TABLE IF NOT EXISTS " : "", row[1]);
3813       }
3814 
3815       check_io(sql_file);
3816       mysql_free_result(result);
3817     }
3818     my_snprintf(query_buff, sizeof(query_buff), "show fields from %s",
3819                 result_table);
3820     if (mysql_query_with_error_report(mysql, &result, query_buff))
3821     {
3822       if (path)
3823         my_fclose(sql_file, MYF(MY_WME));
3824       DBUG_RETURN(0);
3825     }
3826 
3827     if (write_data && !complete_insert)
3828     {
3829       /*
3830         If data contents of table are to be written and complete_insert
3831         is false (column list not required in INSERT statement), scan the
3832         column list for generated columns, as presence of any generated column
3833         will require that an explicit list of columns is printed.
3834       */
3835       while ((row= mysql_fetch_row(result)))
3836       {
3837         complete_insert|=
3838           strcmp(row[SHOW_EXTRA], "STORED GENERATED") == 0 ||
3839           strcmp(row[SHOW_EXTRA], "VIRTUAL GENERATED") == 0;
3840       }
3841       mysql_free_result(result);
3842 
3843       if (mysql_query_with_error_report(mysql, &result, query_buff))
3844       {
3845         if (path)
3846           my_fclose(sql_file, MYF(MY_WME));
3847         DBUG_RETURN(0);
3848       }
3849     }
3850     /*
3851       If write_data is true, then we build up insert statements for
3852       the table's data. Note: in subsequent lines of code, this test
3853       will have to be performed each time we are appending to
3854       insert_pat.
3855     */
3856     if (write_data)
3857     {
3858       if (opt_replace_into)
3859         dynstr_append_checked(&insert_pat, "REPLACE ");
3860       else
3861         dynstr_append_checked(&insert_pat, "INSERT ");
3862       dynstr_append_checked(&insert_pat, insert_option);
3863       dynstr_append_checked(&insert_pat, "INTO ");
3864       dynstr_append_checked(&insert_pat, opt_quoted_table);
3865       if (complete_insert)
3866       {
3867         dynstr_append_checked(&insert_pat, " (");
3868       }
3869       else
3870       {
3871         dynstr_append_checked(&insert_pat, " VALUES ");
3872         if (!extended_insert)
3873           dynstr_append_checked(&insert_pat, "(");
3874       }
3875     }
3876 
3877     colno= 0;
3878     while ((row= mysql_fetch_row(result)))
3879     {
3880       real_columns[colno]=
3881         strcmp(row[SHOW_EXTRA], "STORED GENERATED") != 0 &&
3882         strcmp(row[SHOW_EXTRA], "VIRTUAL GENERATED") != 0;
3883 
3884       if (real_columns[colno++] && complete_insert)
3885       {
3886         if (init)
3887         {
3888           dynstr_append_checked(&insert_pat, ", ");
3889         }
3890         init=1;
3891         dynstr_append_checked(&insert_pat,
3892                       quote_name(row[SHOW_FIELDNAME], name_buff, 0));
3893       }
3894     }
3895     num_fields= mysql_num_rows(result);
3896     mysql_free_result(result);
3897   }
3898   else
3899   {
3900     verbose_msg("%s: Warning: Can't set SQL_QUOTE_SHOW_CREATE option (%s)\n",
3901                 my_progname, mysql_error(mysql));
3902 
3903     my_snprintf(query_buff, sizeof(query_buff), show_fields_stmt, db, table);
3904 
3905     if (mysql_query_with_error_report(mysql, &result, query_buff))
3906       DBUG_RETURN(0);
3907 
3908     if (write_data && !complete_insert)
3909     {
3910       /*
3911         If data contents of table are to be written and complete_insert
3912         is false (column list not required in INSERT statement), scan the
3913         column list for generated columns, as presence of any generated column
3914         will require that an explicit list of columns is printed.
3915       */
3916       while ((row= mysql_fetch_row(result)))
3917       {
3918         complete_insert|=
3919           strcmp(row[SHOW_EXTRA], "STORED GENERATED") == 0 ||
3920           strcmp(row[SHOW_EXTRA], "VIRTUAL GENERATED") == 0;
3921       }
3922       mysql_free_result(result);
3923 
3924       if (mysql_query_with_error_report(mysql, &result, query_buff))
3925       {
3926         if (path)
3927           my_fclose(sql_file, MYF(MY_WME));
3928         DBUG_RETURN(0);
3929       }
3930     }
3931     /* Make an sql-file, if path was given iow. option -T was given */
3932     if (!opt_no_create_info)
3933     {
3934       my_bool freemem= FALSE;
3935       char const *text;
3936 
3937       if (path)
3938       {
3939         if (!(sql_file= open_sql_file_for_table(table, O_WRONLY)))
3940           DBUG_RETURN(0);
3941         write_header(sql_file, db);
3942       }
3943 
3944       text= fix_identifier_with_newline(result_table, &freemem);
3945       print_comment(sql_file, 0,
3946                     "\n--\n-- Table structure for table %s\n--\n\n",
3947                     text);
3948       if (freemem)
3949         my_free((void*)text);
3950 
3951       if (opt_drop)
3952         fprintf(sql_file, "DROP TABLE IF EXISTS %s;\n", result_table);
3953       if (!opt_xml)
3954         fprintf(sql_file, "CREATE TABLE %s (\n", result_table);
3955       else
3956         print_xml_tag(sql_file, "\t", "\n", "table_structure", "name=", table,
3957                 NullS);
3958       check_io(sql_file);
3959     }
3960 
3961     if (write_data)
3962     {
3963       if (opt_replace_into)
3964         dynstr_append_checked(&insert_pat, "REPLACE ");
3965       else
3966         dynstr_append_checked(&insert_pat, "INSERT ");
3967       dynstr_append_checked(&insert_pat, insert_option);
3968       dynstr_append_checked(&insert_pat, "INTO ");
3969       dynstr_append_checked(&insert_pat, result_table);
3970       if (complete_insert)
3971         dynstr_append_checked(&insert_pat, " (");
3972       else
3973       {
3974         dynstr_append_checked(&insert_pat, " VALUES ");
3975         if (!extended_insert)
3976           dynstr_append_checked(&insert_pat, "(");
3977       }
3978     }
3979 
3980     colno= 0;
3981     while ((row= mysql_fetch_row(result)))
3982     {
3983       ulong *lengths= mysql_fetch_lengths(result);
3984 
3985       real_columns[colno]=
3986         strcmp(row[SHOW_EXTRA], "STORED GENERATED") != 0 &&
3987         strcmp(row[SHOW_EXTRA], "VIRTUAL GENERATED") != 0;
3988 
3989       if (!real_columns[colno++])
3990         continue;
3991 
3992       if (init)
3993       {
3994         if (!opt_xml && !opt_no_create_info)
3995         {
3996           fputs(",\n",sql_file);
3997           check_io(sql_file);
3998         }
3999         if (complete_insert)
4000           dynstr_append_checked(&insert_pat, ", ");
4001       }
4002       init=1;
4003       if (complete_insert)
4004         dynstr_append_checked(&insert_pat,
4005                       quote_name(row[SHOW_FIELDNAME], name_buff, 0));
4006       if (!opt_no_create_info)
4007       {
4008         if (opt_xml)
4009         {
4010           print_xml_row(sql_file, "field", result, &row, NullS);
4011           continue;
4012         }
4013 
4014         if (opt_keywords)
4015           fprintf(sql_file, "  %s.%s %s", result_table,
4016                   quote_name(row[SHOW_FIELDNAME],name_buff, 0),
4017                   row[SHOW_TYPE]);
4018         else
4019           fprintf(sql_file, "  %s %s", quote_name(row[SHOW_FIELDNAME],
4020                                                   name_buff, 0),
4021                   row[SHOW_TYPE]);
4022         if (row[SHOW_DEFAULT])
4023         {
4024           fputs(" DEFAULT ", sql_file);
4025           unescape(sql_file, row[SHOW_DEFAULT], lengths[SHOW_DEFAULT]);
4026         }
4027         if (!row[SHOW_NULL][0])
4028           fputs(" NOT NULL", sql_file);
4029         if (row[SHOW_EXTRA][0])
4030           fprintf(sql_file, " %s",row[SHOW_EXTRA]);
4031         check_io(sql_file);
4032       }
4033     }
4034     num_fields= mysql_num_rows(result);
4035     mysql_free_result(result);
4036     if (!opt_no_create_info)
4037     {
4038       /* Make an sql-file, if path was given iow. option -T was given */
4039       char buff[20+FN_REFLEN];
4040       uint keynr,primary_key;
4041       my_snprintf(buff, sizeof(buff), "show keys from %s", result_table);
4042       if (mysql_query_with_error_report(mysql, &result, buff))
4043       {
4044         if (mysql_errno(mysql) == ER_WRONG_OBJECT)
4045         {
4046           /* it is VIEW */
4047           fputs("\t\t<options Comment=\"view\" />\n", sql_file);
4048           goto continue_xml;
4049         }
4050         fprintf(stderr, "%s: Can't get keys for table %s (%s)\n",
4051                 my_progname, result_table, mysql_error(mysql));
4052         if (path)
4053           my_fclose(sql_file, MYF(MY_WME));
4054         DBUG_RETURN(0);
4055       }
4056 
4057       /* Find first which key is primary key */
4058       keynr=0;
4059       primary_key=INT_MAX;
4060       while ((row= mysql_fetch_row(result)))
4061       {
4062         if (atoi(row[3]) == 1)
4063         {
4064           keynr++;
4065           if (!strcmp(row[2],"PRIMARY"))
4066           {
4067             primary_key=keynr;
4068             break;
4069           }
4070         }
4071       }
4072       mysql_data_seek(result,0);
4073       keynr=0;
4074       while ((row= mysql_fetch_row(result)))
4075       {
4076         if (opt_xml)
4077         {
4078           print_xml_row(sql_file, "key", result, &row, NullS);
4079           continue;
4080         }
4081 
4082         if (atoi(row[3]) == 1)
4083         {
4084           if (keynr++)
4085             putc(')', sql_file);
4086           if (atoi(row[1]))       /* Test if duplicate key */
4087             /* Duplicate allowed */
4088             fprintf(sql_file, ",\n  KEY %s (",quote_name(row[2],name_buff,0));
4089           else if (keynr == primary_key)
4090             fputs(",\n  PRIMARY KEY (",sql_file); /* First UNIQUE is primary */
4091           else
4092             fprintf(sql_file, ",\n  UNIQUE %s (",quote_name(row[2],name_buff,
4093                                                             0));
4094         }
4095         else
4096           putc(',', sql_file);
4097         fputs(quote_name(row[4], name_buff, 0), sql_file);
4098         if (row[7])
4099           fprintf(sql_file, " (%s)",row[7]);      /* Sub key */
4100         check_io(sql_file);
4101       }
4102       mysql_free_result(result);
4103       if (!opt_xml)
4104       {
4105         if (keynr)
4106           putc(')', sql_file);
4107         fputs("\n)",sql_file);
4108         check_io(sql_file);
4109       }
4110 
4111       /* Get MySQL specific create options */
4112       if (create_options)
4113       {
4114         char show_name_buff[NAME_LEN*2+2+24];
4115 
4116         /* Check memory for quote_for_like() */
4117         my_snprintf(buff, sizeof(buff), "show table status like %s",
4118                     quote_for_like(table, show_name_buff));
4119 
4120         if (mysql_query_with_error_report(mysql, &result, buff))
4121         {
4122           if (mysql_errno(mysql) != ER_PARSE_ERROR)
4123           {                                     /* If old MySQL version */
4124             verbose_msg("-- Warning: Couldn't get status information for " \
4125                         "table %s (%s)\n", result_table,mysql_error(mysql));
4126           }
4127         }
4128         else if (!(row= mysql_fetch_row(result)))
4129         {
4130           fprintf(stderr,
4131                   "Error: Couldn't read status information for table %s (%s)\n",
4132                   result_table,mysql_error(mysql));
4133         }
4134         else
4135         {
4136           if (opt_xml)
4137             print_xml_row(sql_file, "options", result, &row, NullS);
4138           else
4139           {
4140             fputs("/*!",sql_file);
4141             print_value(sql_file,result,row,"engine=","Engine",0);
4142             print_value(sql_file,result,row,"","Create_options",0);
4143             print_value(sql_file,result,row,"comment=","Comment",1);
4144             fputs(" */",sql_file);
4145             check_io(sql_file);
4146           }
4147         }
4148         mysql_free_result(result);              /* Is always safe to free */
4149       }
4150 continue_xml:
4151       if (!opt_xml)
4152         fputs(";\n", sql_file);
4153       else
4154         fputs("\t</table_structure>\n", sql_file);
4155       check_io(sql_file);
4156     }
4157   }
4158   if (complete_insert)
4159   {
4160     dynstr_append_checked(&insert_pat, ") VALUES ");
4161     if (!extended_insert)
4162       dynstr_append_checked(&insert_pat, "(");
4163   }
4164   if (sql_file != md_result_file)
4165   {
4166     fputs("\n", sql_file);
4167     write_footer(sql_file);
4168     my_fclose(sql_file, MYF(MY_WME));
4169   }
4170   DBUG_RETURN((uint) num_fields);
4171 } /* get_table_structure */
4172 
dump_trigger_old(FILE * sql_file,MYSQL_RES * show_triggers_rs,MYSQL_ROW * show_trigger_row,const char * table_name)4173 static void dump_trigger_old(FILE *sql_file, MYSQL_RES *show_triggers_rs,
4174                              MYSQL_ROW *show_trigger_row,
4175                              const char *table_name)
4176 {
4177   char quoted_table_name_buf[NAME_LEN * 2 + 3];
4178   char *quoted_table_name= quote_name(table_name, quoted_table_name_buf, 1);
4179 
4180   char name_buff[NAME_LEN * 4 + 3];
4181   const char *xml_msg= "\nWarning! mysqldump being run against old server "
4182                        "that does not\nsupport 'SHOW CREATE TRIGGERS' "
4183                        "statement. Skipping..\n";
4184 
4185   DBUG_ENTER("dump_trigger_old");
4186 
4187   if (opt_xml)
4188   {
4189     print_xml_comment(sql_file, strlen(xml_msg), xml_msg);
4190     check_io(sql_file);
4191     DBUG_VOID_RETURN;
4192   }
4193 
4194   fprintf(sql_file,
4195           "--\n"
4196           "-- WARNING: old server version. "
4197             "The following dump may be incomplete.\n"
4198           "--\n");
4199 
4200   if (opt_compact)
4201     fprintf(sql_file, "/*!50003 SET @OLD_SQL_MODE=@@SQL_MODE*/;\n");
4202 
4203   if (opt_drop_trigger)
4204     fprintf(sql_file, "/*!50032 DROP TRIGGER IF EXISTS %s */;\n", (*show_trigger_row)[0]);
4205 
4206   fprintf(sql_file,
4207           "DELIMITER ;;\n"
4208           "/*!50003 SET SESSION SQL_MODE=\"%s\" */;;\n"
4209           "/*!50003 CREATE */ ",
4210           (*show_trigger_row)[6]);
4211 
4212   if (mysql_num_fields(show_triggers_rs) > 7)
4213   {
4214     /*
4215       mysqldump can be run against the server, that does not support
4216       definer in triggers (there is no DEFINER column in SHOW TRIGGERS
4217       output). So, we should check if we have this column before
4218       accessing it.
4219     */
4220 
4221     size_t user_name_len;
4222     char user_name_str[USERNAME_LENGTH + 1];
4223     char quoted_user_name_str[USERNAME_LENGTH * 2 + 3];
4224     size_t host_name_len;
4225     char host_name_str[HOSTNAME_LENGTH + 1];
4226     char quoted_host_name_str[HOSTNAME_LENGTH * 2 + 3];
4227 
4228     parse_user((*show_trigger_row)[7],
4229                strlen((*show_trigger_row)[7]),
4230                user_name_str, &user_name_len,
4231                host_name_str, &host_name_len);
4232 
4233     fprintf(sql_file,
4234             "/*!50017 DEFINER=%s@%s */ ",
4235             quote_name(user_name_str, quoted_user_name_str, FALSE),
4236             quote_name(host_name_str, quoted_host_name_str, FALSE));
4237   }
4238 
4239   fprintf(sql_file,
4240           "/*!50003 TRIGGER %s %s %s ON %s FOR EACH ROW%s%s */;;\n"
4241           "DELIMITER ;\n",
4242           quote_name((*show_trigger_row)[0], name_buff, 0), /* Trigger */
4243           (*show_trigger_row)[4], /* Timing */
4244           (*show_trigger_row)[1], /* Event */
4245           quoted_table_name,
4246           (strchr(" \t\n\r", *((*show_trigger_row)[3]))) ? "" : " ",
4247           (*show_trigger_row)[3] /* Statement */);
4248 
4249   if (opt_compact)
4250     fprintf(sql_file, "/*!50003 SET SESSION SQL_MODE=@OLD_SQL_MODE */;\n");
4251 
4252   DBUG_VOID_RETURN;
4253 }
4254 
dump_trigger(FILE * sql_file,MYSQL_RES * show_create_trigger_rs,const char * db_name,const char * db_cl_name)4255 static int dump_trigger(FILE *sql_file, MYSQL_RES *show_create_trigger_rs,
4256                         const char *db_name,
4257                         const char *db_cl_name)
4258 {
4259   MYSQL_ROW row;
4260   char *query_str;
4261   int db_cl_altered= FALSE;
4262 
4263   DBUG_ENTER("dump_trigger");
4264 
4265   while ((row= mysql_fetch_row(show_create_trigger_rs)))
4266   {
4267     if (opt_xml)
4268     {
4269       print_xml_row(sql_file, "trigger", show_create_trigger_rs, &row,
4270                     "SQL Original Statement");
4271       check_io(sql_file);
4272       continue;
4273     }
4274 
4275     query_str= cover_definer_clause(row[2], strlen(row[2]),
4276                                     C_STRING_WITH_LEN("50017"),
4277                                     C_STRING_WITH_LEN("50003"),
4278                                     C_STRING_WITH_LEN(" TRIGGER"));
4279     if (switch_db_collation(sql_file, db_name, ";",
4280                             db_cl_name, row[5], &db_cl_altered))
4281       DBUG_RETURN(TRUE);
4282 
4283     switch_cs_variables(sql_file, ";",
4284                         row[3],   /* character_set_client */
4285                         row[3],   /* character_set_results */
4286                         row[4]);  /* collation_connection */
4287 
4288     remove_sql_mode(row[1], C_STRING_WITH_LEN("NO_AUTO_CREATE_USER"));
4289     switch_sql_mode(sql_file, ";", row[1]);
4290 
4291     if (opt_drop_trigger)
4292       fprintf(sql_file, "/*!50032 DROP TRIGGER IF EXISTS %s */;\n", row[0]);
4293 
4294     fprintf(sql_file,
4295             "DELIMITER ;;\n"
4296             "/*!50003 %s */;;\n"
4297             "DELIMITER ;\n",
4298             (const char *) (query_str != NULL ? query_str : row[2]));
4299 
4300     restore_sql_mode(sql_file, ";");
4301     restore_cs_variables(sql_file, ";");
4302 
4303     if (db_cl_altered)
4304     {
4305       if (restore_db_collation(sql_file, db_name, ";", db_cl_name))
4306         DBUG_RETURN(TRUE);
4307     }
4308 
4309     my_free(query_str);
4310   }
4311 
4312   DBUG_RETURN(FALSE);
4313 }
4314 
4315 /**
4316   Dump the triggers for a given table.
4317 
4318   This should be called after the tables have been dumped in case a trigger
4319   depends on the existence of a table.
4320 
4321   @param[in] table_name
4322   @param[in] db_name
4323 
4324   @return Error status.
4325     @retval TRUE error has occurred.
4326     @retval FALSE operation succeed.
4327 */
4328 
dump_triggers_for_table(char * table_name,char * db_name)4329 static int dump_triggers_for_table(char *table_name, char *db_name)
4330 {
4331   char       name_buff[NAME_LEN*4+3];
4332   char       query_buff[QUERY_LENGTH];
4333   uint       old_opt_compatible_mode= opt_compatible_mode;
4334   MYSQL_RES  *show_triggers_rs;
4335   MYSQL_ROW  row;
4336   FILE      *sql_file= md_result_file;
4337 
4338   char       db_cl_name[MY_CS_NAME_SIZE];
4339   int        ret= TRUE;
4340 
4341   DBUG_ENTER("dump_triggers_for_table");
4342   DBUG_PRINT("enter", ("db: %s, table_name: %s", db_name, table_name));
4343 
4344   if (path && !(sql_file= open_sql_file_for_table(table_name,
4345                                                   O_WRONLY | O_APPEND)))
4346     DBUG_RETURN(1);
4347 
4348   /* Do not use ANSI_QUOTES on triggers in dump */
4349   opt_compatible_mode&= ~MASK_ANSI_QUOTES;
4350 
4351   /* Get database collation. */
4352 
4353   if (switch_character_set_results(mysql, "binary"))
4354     goto done;
4355 
4356   if (fetch_db_collation(db_name, db_cl_name, sizeof (db_cl_name)))
4357     goto done;
4358 
4359   /* Get list of triggers. */
4360 
4361   my_snprintf(query_buff, sizeof(query_buff),
4362               "SHOW TRIGGERS LIKE %s",
4363               quote_for_like(table_name, name_buff));
4364 
4365   if (mysql_query_with_error_report(mysql, &show_triggers_rs, query_buff))
4366     goto done;
4367 
4368   /* Dump triggers. */
4369 
4370   if (! mysql_num_rows(show_triggers_rs))
4371     goto skip;
4372 
4373   if (opt_xml)
4374     print_xml_tag(sql_file, "\t", "\n", "triggers", "name=",
4375                   table_name, NullS);
4376 
4377   while ((row= mysql_fetch_row(show_triggers_rs)))
4378   {
4379 
4380     my_snprintf(query_buff, sizeof (query_buff),
4381                 "SHOW CREATE TRIGGER %s",
4382                 quote_name(row[0], name_buff, TRUE));
4383 
4384     if (mysql_query(mysql, query_buff))
4385     {
4386       /*
4387         mysqldump is being run against old server, that does not support
4388         SHOW CREATE TRIGGER statement. We should use SHOW TRIGGERS output.
4389 
4390         NOTE: the dump may be incorrect, as old SHOW TRIGGERS does not
4391         provide all the necessary information to restore trigger properly.
4392       */
4393 
4394       dump_trigger_old(sql_file, show_triggers_rs, &row, table_name);
4395     }
4396     else
4397     {
4398       MYSQL_RES *show_create_trigger_rs= mysql_store_result(mysql);
4399 
4400       if (!show_create_trigger_rs ||
4401           dump_trigger(sql_file, show_create_trigger_rs, db_name, db_cl_name))
4402         goto done;
4403 
4404       mysql_free_result(show_create_trigger_rs);
4405     }
4406 
4407   }
4408 
4409   if (opt_xml)
4410   {
4411     fputs("\t</triggers>\n", sql_file);
4412     check_io(sql_file);
4413   }
4414 
4415 skip:
4416   mysql_free_result(show_triggers_rs);
4417 
4418   if (switch_character_set_results(mysql, default_charset))
4419     goto done;
4420 
4421   /*
4422     make sure to set back opt_compatible mode to
4423     original value
4424   */
4425   opt_compatible_mode=old_opt_compatible_mode;
4426 
4427   ret= FALSE;
4428 
4429 done:
4430   if (path)
4431     my_fclose(sql_file, MYF(0));
4432 
4433   DBUG_RETURN(ret);
4434 }
4435 
add_load_option(DYNAMIC_STRING * str,const char * option,const char * option_value)4436 static void add_load_option(DYNAMIC_STRING *str, const char *option,
4437                              const char *option_value)
4438 {
4439   if (!option_value)
4440   {
4441     /* Null value means we don't add this option. */
4442     return;
4443   }
4444 
4445   dynstr_append_checked(str, option);
4446 
4447   if (strncmp(option_value, "0x", sizeof("0x")-1) == 0)
4448   {
4449     /* It's a hex constant, don't escape */
4450     dynstr_append_checked(str, option_value);
4451   }
4452   else
4453   {
4454     /* char constant; escape */
4455     field_escape(str, option_value);
4456   }
4457 }
4458 
4459 
4460 /*
4461   Allow the user to specify field terminator strings like:
4462   "'", "\", "\\" (escaped backslash), "\t" (tab), "\n" (newline)
4463   This is done by doubling ' and add a end -\ if needed to avoid
4464   syntax errors from the SQL parser.
4465 */
4466 
field_escape(DYNAMIC_STRING * in,const char * from)4467 static void field_escape(DYNAMIC_STRING* in, const char *from)
4468 {
4469   uint end_backslashes= 0;
4470 
4471   dynstr_append_checked(in, "'");
4472 
4473   while (*from)
4474   {
4475     dynstr_append_mem_checked(in, from, 1);
4476 
4477     if (*from == '\\')
4478       end_backslashes^=1;    /* find odd number of backslashes */
4479     else
4480     {
4481       if (*from == '\'' && !end_backslashes)
4482       {
4483         /* We want a duplicate of "'" for MySQL */
4484         dynstr_append_checked(in, "\'");
4485       }
4486       end_backslashes=0;
4487     }
4488     from++;
4489   }
4490   /* Add missing backslashes if user has specified odd number of backs.*/
4491   if (end_backslashes)
4492     dynstr_append_checked(in, "\\");
4493 
4494   dynstr_append_checked(in, "'");
4495 }
4496 
4497 
4498 
alloc_query_str(size_t size)4499 static char *alloc_query_str(size_t size)
4500 {
4501   char *query;
4502 
4503   if (!(query= (char*) my_malloc(PSI_NOT_INSTRUMENTED,
4504                                  size, MYF(MY_WME))))
4505     die(EX_MYSQLERR, "Couldn't allocate a query string.");
4506 
4507   return query;
4508 }
4509 
4510 
4511 
4512 /*
4513   Dump delayed secondary index definitions when --innodb-optimize-keys is used.
4514 */
4515 
dump_skipped_keys(const char * table)4516 static void dump_skipped_keys(const char *table)
4517 {
4518   uint keys;
4519 
4520   if (!skipped_keys_list && !alter_constraints_list)
4521     return;
4522 
4523   verbose_msg("-- Dumping delayed secondary index definitions for table %s\n",
4524               table);
4525 
4526   if (skipped_keys_list)
4527   {
4528     uint sk_list_len= list_length(skipped_keys_list);
4529     skipped_keys_list= list_reverse(skipped_keys_list);
4530     fprintf(md_result_file, "ALTER TABLE %s%s", table,
4531             (sk_list_len > 1) ? "\n" : " ");
4532 
4533     for (keys= sk_list_len; keys > 0; keys--)
4534     {
4535       LIST *node= skipped_keys_list;
4536       char *def= node->data;
4537 
4538       fprintf(md_result_file, "%sADD %s%s", (sk_list_len > 1) ? "  " : "",
4539               def, (keys > 1) ? ",\n" : ";\n");
4540 
4541       skipped_keys_list= list_delete(skipped_keys_list, node);
4542       my_free(def);
4543       my_free(node);
4544     }
4545   }
4546 
4547   if (alter_constraints_list)
4548   {
4549     uint ac_list_len= list_length(alter_constraints_list);
4550     alter_constraints_list= list_reverse(alter_constraints_list);
4551     fprintf(md_result_file, "ALTER TABLE %s%s", table,
4552             (ac_list_len > 1) ? "\n" : " ");
4553 
4554     for (keys= ac_list_len; keys > 0; keys--)
4555     {
4556       LIST *node= alter_constraints_list;
4557       char *def= node->data;
4558 
4559       fprintf(md_result_file, "%sADD %s%s", (ac_list_len > 1) ? "  " : "",
4560               def, (keys > 1) ? ",\n" : ";\n");
4561 
4562       alter_constraints_list= list_delete(alter_constraints_list, node);
4563       my_free(def);
4564       my_free(node);
4565     }
4566   }
4567 
4568   assert(skipped_keys_list == NULL);
4569   assert(alter_constraints_list == NULL);
4570 }
4571 
4572 
4573 /*
4574 
4575  SYNOPSIS
4576   dump_table()
4577 
4578   dump_table saves database contents as a series of INSERT statements.
4579 
4580   ARGS
4581    table - table name
4582    db    - db name
4583 
4584    RETURNS
4585     void
4586 */
4587 
4588 
dump_table(char * table,char * db)4589 static void dump_table(char *table, char *db)
4590 {
4591   char ignore_flag;
4592   char buf[200], table_buff[NAME_LEN+3];
4593   DYNAMIC_STRING query_string;
4594   DYNAMIC_STRING extended_row;
4595   char table_type[NAME_LEN];
4596   char *result_table, table_buff2[NAME_LEN*2+3], *opt_quoted_table;
4597   int error= 0;
4598   ulong         rownr, row_break;
4599   size_t        total_length, init_length;
4600   uint num_fields;
4601   MYSQL_RES     *res;
4602   MYSQL_FIELD   *field;
4603   MYSQL_ROW     row;
4604   my_bool real_columns[MAX_FIELDS];
4605   DBUG_ENTER("dump_table");
4606 
4607   /*
4608     Make sure you get the create table info before the following check for
4609     --no-data flag below. Otherwise, the create table info won't be printed.
4610   */
4611   num_fields= get_table_structure(table, db, table_type, &ignore_flag,
4612                                   real_columns);
4613 
4614   /*
4615     The "table" could be a view.  If so, we don't do anything here.
4616   */
4617   if (strcmp(table_type, "VIEW") == 0)
4618     DBUG_VOID_RETURN;
4619 
4620   /*
4621     We don't dump data fo`r replication metadata tables.
4622   */
4623   if (replication_metadata_tables(db, table))
4624     DBUG_VOID_RETURN;
4625 
4626   result_table= quote_name(table,table_buff, 1);
4627   opt_quoted_table= quote_name(table, table_buff2, 0);
4628 
4629   /* Check --no-data flag */
4630   if (opt_no_data)
4631   {
4632     dump_skipped_keys(opt_quoted_table);
4633 
4634     verbose_msg("-- Skipping dump data for table '%s', --no-data was used\n",
4635                 table);
4636     DBUG_VOID_RETURN;
4637   }
4638 
4639   DBUG_PRINT("info",
4640              ("ignore_flag: %x  num_fields: %d", (int) ignore_flag,
4641               num_fields));
4642   /*
4643     If the table type is a merge table or any type that has to be
4644      _completely_ ignored and no data dumped
4645   */
4646   if (ignore_flag & IGNORE_DATA)
4647   {
4648     verbose_msg("-- Warning: Skipping data for table '%s' because " \
4649                 "it's of type %s\n", table, table_type);
4650     DBUG_VOID_RETURN;
4651   }
4652   /* Check that there are any fields in the table */
4653   if (num_fields == 0)
4654   {
4655     verbose_msg("-- Skipping dump data for table '%s', it has no fields\n",
4656                 table);
4657     DBUG_VOID_RETURN;
4658   }
4659 
4660   verbose_msg("-- Sending SELECT query...\n");
4661 
4662   init_dynamic_string_checked(&query_string, "", 1024, 1024);
4663   if (extended_insert)
4664     init_dynamic_string_checked(&extended_row, "", 1024, 1024);
4665 
4666   if (path)
4667   {
4668     char filename[FN_REFLEN], tmp_path[FN_REFLEN];
4669 
4670     /*
4671       Convert the path to native os format
4672       and resolve to the full filepath.
4673     */
4674     convert_dirname(tmp_path,path,NullS);
4675     my_load_path(tmp_path, tmp_path, NULL);
4676     fn_format(filename, table, tmp_path, ".txt", MYF(MY_UNPACK_FILENAME));
4677 
4678     /* Must delete the file that 'INTO OUTFILE' will write to */
4679     my_delete(filename, MYF(0));
4680 
4681     /* convert to a unix path name to stick into the query */
4682     to_unix_path(filename);
4683 
4684     /* now build the query string */
4685 
4686     dynstr_append_checked(&query_string, "SELECT /*!40001 SQL_NO_CACHE */ * INTO OUTFILE '");
4687     dynstr_append_checked(&query_string, filename);
4688     dynstr_append_checked(&query_string, "'");
4689 
4690     dynstr_append_checked(&query_string, " /*!50138 CHARACTER SET ");
4691     dynstr_append_checked(&query_string, default_charset == mysql_universal_client_charset ?
4692                                          my_charset_bin.name : /* backward compatibility */
4693                                          default_charset);
4694     dynstr_append_checked(&query_string, " */");
4695 
4696     if (fields_terminated || enclosed || opt_enclosed || escaped)
4697       dynstr_append_checked(&query_string, " FIELDS");
4698 
4699     add_load_option(&query_string, " TERMINATED BY ", fields_terminated);
4700     add_load_option(&query_string, " ENCLOSED BY ", enclosed);
4701     add_load_option(&query_string, " OPTIONALLY ENCLOSED BY ", opt_enclosed);
4702     add_load_option(&query_string, " ESCAPED BY ", escaped);
4703     add_load_option(&query_string, " LINES TERMINATED BY ", lines_terminated);
4704 
4705     dynstr_append_checked(&query_string, " FROM ");
4706     dynstr_append_checked(&query_string, result_table);
4707 
4708     if (where)
4709     {
4710       dynstr_append_checked(&query_string, " WHERE ");
4711       dynstr_append_checked(&query_string, where);
4712     }
4713     else if ((!my_strcasecmp(charset_info, db, "mysql")) &&
4714              (!my_strcasecmp(charset_info, table, "proc")) &&
4715              opt_alldbs)
4716     {
4717       dynstr_append_checked(&query_string, " WHERE db != 'sys'");
4718     }
4719 
4720     if (order_by)
4721     {
4722       dynstr_append_checked(&query_string, " ORDER BY ");
4723       dynstr_append_checked(&query_string, order_by);
4724     }
4725 
4726     if (mysql_real_query(mysql, query_string.str, (ulong)query_string.length))
4727     {
4728       DB_error(mysql, "when executing 'SELECT INTO OUTFILE'");
4729       dynstr_free(&query_string);
4730       DBUG_VOID_RETURN;
4731     }
4732   }
4733   else
4734   {
4735     my_bool freemem= FALSE;
4736     char const* text= fix_identifier_with_newline(result_table, &freemem);
4737     print_comment(md_result_file, 0, "\n--\n-- Dumping data for table %s\n--\n",
4738                   text);
4739     if (freemem)
4740       my_free((void*)text);
4741 
4742     dynstr_append_checked(&query_string, "SELECT /*!40001 SQL_NO_CACHE */ * FROM ");
4743     dynstr_append_checked(&query_string, result_table);
4744 
4745     if (where)
4746     {
4747       freemem= FALSE;
4748       text= fix_identifier_with_newline(where, &freemem);
4749       print_comment(md_result_file, 0, "-- WHERE:  %s\n", text);
4750       if (freemem)
4751         my_free((void*)text);
4752 
4753       dynstr_append_checked(&query_string, " WHERE ");
4754       dynstr_append_checked(&query_string, where);
4755     }
4756     /*
4757       If table is mysql.proc then do not dump routines which belong
4758       to sys schema
4759     */
4760     else if ((!my_strcasecmp(charset_info, db, "mysql")) &&
4761              (!my_strcasecmp(charset_info, table, "proc")) &&
4762              opt_alldbs)
4763     {
4764       dynstr_append_checked(&query_string, " WHERE db != 'sys'");
4765     }
4766     if (order_by)
4767     {
4768       freemem= FALSE;
4769       text= fix_identifier_with_newline(order_by, &freemem);
4770       print_comment(md_result_file, 0, "-- ORDER BY:  %s\n", text);
4771       if (freemem)
4772         my_free((void*)text);
4773 
4774       dynstr_append_checked(&query_string, " ORDER BY ");
4775       dynstr_append_checked(&query_string, order_by);
4776     }
4777 
4778     if (!opt_xml && !opt_compact)
4779     {
4780       fputs("\n", md_result_file);
4781       check_io(md_result_file);
4782     }
4783     if (mysql_query_with_error_report(mysql, 0, query_string.str))
4784     {
4785       DB_error(mysql, "when retrieving data from server");
4786       goto err;
4787     }
4788     if (quick)
4789       res=mysql_use_result(mysql);
4790     else
4791       res=mysql_store_result(mysql);
4792     if (!res)
4793     {
4794       DB_error(mysql, "when retrieving data from server");
4795       goto err;
4796     }
4797 
4798     verbose_msg("-- Retrieving rows...\n");
4799     if (mysql_num_fields(res) != num_fields)
4800     {
4801       fprintf(stderr,"%s: Error in field count for table: %s !  Aborting.\n",
4802               my_progname, result_table);
4803       error= EX_CONSCHECK;
4804       goto err;
4805     }
4806 
4807     if (opt_lock)
4808     {
4809       fprintf(md_result_file,"LOCK TABLES %s WRITE;\n", opt_quoted_table);
4810       check_io(md_result_file);
4811     }
4812     /* Moved disable keys to after lock per bug 15977 */
4813     if (opt_disable_keys)
4814     {
4815       fprintf(md_result_file, "/*!40000 ALTER TABLE %s DISABLE KEYS */;\n",
4816 	      opt_quoted_table);
4817       check_io(md_result_file);
4818     }
4819 
4820     total_length= opt_net_buffer_length;                /* Force row break */
4821     row_break=0;
4822     rownr=0;
4823     init_length=(uint) insert_pat.length+4;
4824     if (opt_xml)
4825       print_xml_tag(md_result_file, "\t", "\n", "table_data", "name=", table,
4826               NullS);
4827     if (opt_autocommit)
4828     {
4829       fprintf(md_result_file, "set autocommit=0;\n");
4830       check_io(md_result_file);
4831     }
4832 
4833     while ((row= mysql_fetch_row(res)))
4834     {
4835       uint i;
4836       ulong *lengths= mysql_fetch_lengths(res);
4837       rownr++;
4838       if (!extended_insert && !opt_xml)
4839       {
4840         fputs(insert_pat.str,md_result_file);
4841         check_io(md_result_file);
4842       }
4843       mysql_field_seek(res,0);
4844 
4845       if (opt_xml)
4846       {
4847         fputs("\t<row>\n", md_result_file);
4848         check_io(md_result_file);
4849       }
4850 
4851       for (i= 0; i < mysql_num_fields(res); i++)
4852       {
4853         int is_blob;
4854         ulong length= lengths[i];
4855 
4856         if (!(field= mysql_fetch_field(res)))
4857           die(EX_CONSCHECK,
4858                       "Not enough fields from table %s! Aborting.\n",
4859                       result_table);
4860 
4861         if (!real_columns[i])
4862           continue;
4863         /*
4864            63 is my_charset_bin. If charsetnr is not 63,
4865            we have not a BLOB but a TEXT column.
4866         */
4867         is_blob= (field->charsetnr == 63 &&
4868                   (field->type == MYSQL_TYPE_BIT ||
4869                    field->type == MYSQL_TYPE_STRING ||
4870                    field->type == MYSQL_TYPE_VAR_STRING ||
4871                    field->type == MYSQL_TYPE_VARCHAR ||
4872                    field->type == MYSQL_TYPE_BLOB ||
4873                    field->type == MYSQL_TYPE_LONG_BLOB ||
4874                    field->type == MYSQL_TYPE_MEDIUM_BLOB ||
4875                    field->type == MYSQL_TYPE_TINY_BLOB ||
4876                    field->type == MYSQL_TYPE_GEOMETRY)) ? 1 : 0;
4877         if (extended_insert && !opt_xml)
4878         {
4879           if (i == 0)
4880             dynstr_set_checked(&extended_row,"(");
4881           else
4882             dynstr_append_checked(&extended_row,",");
4883 
4884           if (row[i])
4885           {
4886             if (length)
4887             {
4888               if (!(field->flags & NUM_FLAG))
4889               {
4890                 /*
4891                   "length * 2 + 2" is OK for HEX mode:
4892                   - In HEX mode we need exactly 2 bytes per character
4893                   plus 2 bytes for '0x' prefix.
4894                   - In non-HEX mode we need up to 2 bytes per character,
4895                   plus 2 bytes for leading and trailing '\'' characters
4896                   and reserve 1 byte for terminating '\0'.
4897                   In addition to this, for the blob type, we need to
4898                   reserve for the "_binary " string that gets added in
4899                   front of the string in the dump.
4900                 */
4901                 if (opt_hex_blob && is_blob)
4902                 {
4903                   dynstr_realloc_checked(&extended_row,length * 2 + 2 + 1);
4904                   dynstr_append_checked(&extended_row, "0x");
4905                   extended_row.length+= mysql_hex_string(extended_row.str +
4906                                                          extended_row.length,
4907                                                          row[i], length);
4908                   assert(extended_row.length+1 <= extended_row.max_length);
4909                   /* mysql_hex_string() already terminated string by '\0' */
4910                   assert(extended_row.str[extended_row.length] == '\0');
4911                 }
4912                 else
4913                 {
4914                   dynstr_realloc_checked(&extended_row,length * 2 + 2 + 1 +
4915                                          (is_blob? strlen("_binary ") : 0));
4916                   if (is_blob)
4917                   {
4918                     /*
4919                       inform SQL parser that this string isn't in
4920                       character_set_connection, so it doesn't emit a warning.
4921                     */
4922                     dynstr_append_checked(&extended_row, "_binary ");
4923                   }
4924                   dynstr_append_checked(&extended_row,"'");
4925                   extended_row.length +=
4926                   mysql_real_escape_string_quote(&mysql_connection,
4927                                          &extended_row.str[extended_row.length],
4928                                          row[i],length,
4929                                          '\'');
4930                   extended_row.str[extended_row.length]='\0';
4931                   dynstr_append_checked(&extended_row,"'");
4932                 }
4933               }
4934               else
4935               {
4936                 /* change any strings ("inf", "-inf", "nan") into NULL */
4937                 char *ptr= row[i];
4938                 if (my_isalpha(charset_info, *ptr) || (*ptr == '-' &&
4939                     my_isalpha(charset_info, ptr[1])))
4940                   dynstr_append_checked(&extended_row, "NULL");
4941                 else
4942                 {
4943                   if (field->type == MYSQL_TYPE_DECIMAL)
4944                   {
4945                     /* add " signs around */
4946                     dynstr_append_checked(&extended_row, "'");
4947                     dynstr_append_checked(&extended_row, ptr);
4948                     dynstr_append_checked(&extended_row, "'");
4949                   }
4950                   else
4951                     dynstr_append_checked(&extended_row, ptr);
4952                 }
4953               }
4954             }
4955             else
4956               dynstr_append_checked(&extended_row,"''");
4957           }
4958           else
4959             dynstr_append_checked(&extended_row,"NULL");
4960         }
4961         else
4962         {
4963           if (i && !opt_xml)
4964           {
4965             fputc(',', md_result_file);
4966             check_io(md_result_file);
4967           }
4968           if (row[i])
4969           {
4970             if (!(field->flags & NUM_FLAG))
4971             {
4972               if (opt_xml)
4973               {
4974                 if (opt_hex_blob && is_blob && length)
4975                 {
4976                   /* Define xsi:type="xs:hexBinary" for hex encoded data */
4977                   print_xml_tag(md_result_file, "\t\t", "", "field", "name=",
4978                                 field->name, "xsi:type=", "xs:hexBinary", NullS);
4979                   print_blob_as_hex(md_result_file, row[i], length);
4980                 }
4981                 else
4982                 {
4983                   print_xml_tag(md_result_file, "\t\t", "", "field", "name=",
4984                                 field->name, NullS);
4985                   print_quoted_xml(md_result_file, row[i], length, 0);
4986                 }
4987                 fputs("</field>\n", md_result_file);
4988               }
4989               else if (opt_hex_blob && is_blob && length)
4990               {
4991                 fputs("0x", md_result_file);
4992                 print_blob_as_hex(md_result_file, row[i], length);
4993               }
4994               else
4995               {
4996                 if (is_blob)
4997                 {
4998                   fputs("_binary ", md_result_file);
4999                   check_io(md_result_file);
5000                 }
5001                 unescape(md_result_file, row[i], length);
5002               }
5003             }
5004             else
5005             {
5006               /* change any strings ("inf", "-inf", "nan") into NULL */
5007               char *ptr= row[i];
5008               if (opt_xml)
5009               {
5010                 print_xml_tag(md_result_file, "\t\t", "", "field", "name=",
5011                         field->name, NullS);
5012                 fputs(!my_isalpha(charset_info, *ptr) ? ptr: "NULL",
5013                       md_result_file);
5014                 fputs("</field>\n", md_result_file);
5015               }
5016               else if (my_isalpha(charset_info, *ptr) ||
5017                        (*ptr == '-' && my_isalpha(charset_info, ptr[1])))
5018                 fputs("NULL", md_result_file);
5019               else if (field->type == MYSQL_TYPE_DECIMAL)
5020               {
5021                 /* add " signs around */
5022                 fputc('\'', md_result_file);
5023                 fputs(ptr, md_result_file);
5024                 fputc('\'', md_result_file);
5025               }
5026               else
5027                 fputs(ptr, md_result_file);
5028             }
5029           }
5030           else
5031           {
5032             /* The field value is NULL */
5033             if (!opt_xml)
5034               fputs("NULL", md_result_file);
5035             else
5036               print_xml_null_tag(md_result_file, "\t\t", "field name=",
5037                                  field->name, "\n");
5038           }
5039           check_io(md_result_file);
5040         }
5041       }
5042 
5043       if (opt_xml)
5044       {
5045         fputs("\t</row>\n", md_result_file);
5046         check_io(md_result_file);
5047       }
5048 
5049       if (extended_insert)
5050       {
5051         size_t row_length;
5052         dynstr_append_checked(&extended_row,")");
5053         row_length= 2 + extended_row.length;
5054         if (total_length + row_length < opt_net_buffer_length)
5055         {
5056           total_length+= row_length;
5057           fputc(',',md_result_file);            /* Always row break */
5058           fputs(extended_row.str,md_result_file);
5059         }
5060         else
5061         {
5062           if (row_break)
5063             fputs(";\n", md_result_file);
5064           row_break=1;                          /* This is first row */
5065 
5066           fputs(insert_pat.str,md_result_file);
5067           fputs(extended_row.str,md_result_file);
5068           total_length= row_length+init_length;
5069         }
5070         check_io(md_result_file);
5071       }
5072       else if (!opt_xml)
5073       {
5074         fputs(");\n", md_result_file);
5075         check_io(md_result_file);
5076       }
5077     }
5078 
5079     /* XML - close table tag and supress regular output */
5080     if (opt_xml)
5081         fputs("\t</table_data>\n", md_result_file);
5082     else if (extended_insert && row_break)
5083       fputs(";\n", md_result_file);             /* If not empty table */
5084     fflush(md_result_file);
5085     check_io(md_result_file);
5086     if (mysql_errno(mysql))
5087     {
5088       my_snprintf(buf, sizeof(buf),
5089                   "%s: Error %d: %s when dumping table %s at row: %ld\n",
5090                   my_progname,
5091                   mysql_errno(mysql),
5092                   mysql_error(mysql),
5093                   result_table,
5094                   rownr);
5095       fputs(buf,stderr);
5096       error= EX_CONSCHECK;
5097       goto err;
5098     }
5099 
5100     dump_skipped_keys(opt_quoted_table);
5101 
5102     /* Moved enable keys to before unlock per bug 15977 */
5103     if (opt_disable_keys)
5104     {
5105       fprintf(md_result_file,"/*!40000 ALTER TABLE %s ENABLE KEYS */;\n",
5106               opt_quoted_table);
5107       check_io(md_result_file);
5108     }
5109     if (opt_lock)
5110     {
5111       fputs("UNLOCK TABLES;\n", md_result_file);
5112       check_io(md_result_file);
5113     }
5114     if (opt_autocommit)
5115     {
5116       fprintf(md_result_file, "commit;\n");
5117       check_io(md_result_file);
5118     }
5119     mysql_free_result(res);
5120   }
5121   dynstr_free(&query_string);
5122   if (extended_insert)
5123     dynstr_free(&extended_row);
5124   DBUG_VOID_RETURN;
5125 
5126 err:
5127   dynstr_free(&query_string);
5128   if (extended_insert)
5129     dynstr_free(&extended_row);
5130   maybe_exit(error);
5131   DBUG_VOID_RETURN;
5132 } /* dump_table */
5133 
5134 
getTableName(int reset)5135 static char *getTableName(int reset)
5136 {
5137   static MYSQL_RES *res= NULL;
5138   MYSQL_ROW    row;
5139 
5140   if (!res)
5141   {
5142     if (!(res= mysql_list_tables(mysql,NullS)))
5143       return(NULL);
5144   }
5145   if ((row= mysql_fetch_row(res)))
5146     return((char*) row[0]);
5147 
5148   if (reset)
5149     mysql_data_seek(res,0);      /* We want to read again */
5150   else
5151   {
5152     mysql_free_result(res);
5153     res= NULL;
5154   }
5155   return(NULL);
5156 } /* getTableName */
5157 
5158 
5159 /*
5160   dump all logfile groups and tablespaces
5161 */
5162 
dump_all_tablespaces()5163 static int dump_all_tablespaces()
5164 {
5165   return dump_tablespaces(NULL);
5166 }
5167 
dump_tablespaces_for_tables(char * db,char ** table_names,int tables)5168 static int dump_tablespaces_for_tables(char *db, char **table_names, int tables)
5169 {
5170   DYNAMIC_STRING where;
5171   int r;
5172   int i;
5173   char name_buff[NAME_LEN*2+3];
5174 
5175   mysql_real_escape_string_quote(mysql, name_buff, db, (ulong)strlen(db), '\'');
5176 
5177   init_dynamic_string_checked(&where, " AND TABLESPACE_NAME IN ("
5178                       "SELECT DISTINCT TABLESPACE_NAME FROM"
5179                       " INFORMATION_SCHEMA.PARTITIONS"
5180                       " WHERE"
5181                       " TABLE_SCHEMA='", 256, 1024);
5182   dynstr_append_checked(&where, name_buff);
5183   dynstr_append_checked(&where, "' AND TABLE_NAME IN (");
5184 
5185   for (i=0 ; i<tables ; i++)
5186   {
5187     mysql_real_escape_string_quote(mysql, name_buff,
5188                            table_names[i], (ulong)strlen(table_names[i]), '\'');
5189 
5190     dynstr_append_checked(&where, "'");
5191     dynstr_append_checked(&where, name_buff);
5192     dynstr_append_checked(&where, "',");
5193   }
5194   dynstr_trunc(&where, 1);
5195   dynstr_append_checked(&where,"))");
5196 
5197   DBUG_PRINT("info",("Dump TS for Tables where: %s",where.str));
5198   r= dump_tablespaces(where.str);
5199   dynstr_free(&where);
5200   return r;
5201 }
5202 
dump_tablespaces_for_databases(char ** databases)5203 static int dump_tablespaces_for_databases(char** databases)
5204 {
5205   DYNAMIC_STRING where;
5206   int r;
5207   int i;
5208 
5209   init_dynamic_string_checked(&where, " AND TABLESPACE_NAME IN ("
5210                       "SELECT DISTINCT TABLESPACE_NAME FROM"
5211                       " INFORMATION_SCHEMA.PARTITIONS"
5212                       " WHERE"
5213                       " TABLE_SCHEMA IN (", 256, 1024);
5214 
5215   for (i=0 ; databases[i]!=NULL ; i++)
5216   {
5217     char db_name_buff[NAME_LEN*2+3];
5218     mysql_real_escape_string_quote(mysql, db_name_buff,
5219                                databases[i], (ulong)strlen(databases[i]), '\'');
5220     dynstr_append_checked(&where, "'");
5221     dynstr_append_checked(&where, db_name_buff);
5222     dynstr_append_checked(&where, "',");
5223   }
5224   dynstr_trunc(&where, 1);
5225   dynstr_append_checked(&where,"))");
5226 
5227   DBUG_PRINT("info",("Dump TS for DBs where: %s",where.str));
5228   r= dump_tablespaces(where.str);
5229   dynstr_free(&where);
5230   return r;
5231 }
5232 
dump_tablespaces(char * ts_where)5233 static int dump_tablespaces(char* ts_where)
5234 {
5235   MYSQL_ROW row;
5236   MYSQL_RES *tableres;
5237   char buf[FN_REFLEN];
5238   DYNAMIC_STRING sqlbuf;
5239   int first= 0;
5240   /*
5241     The following are used for parsing the EXTRA field
5242   */
5243   char extra_format[]= "UNDO_BUFFER_SIZE=";
5244   char *ubs;
5245   char *endsemi;
5246   DBUG_ENTER("dump_tablespaces");
5247 
5248   init_dynamic_string_checked(&sqlbuf,
5249                       "SELECT LOGFILE_GROUP_NAME,"
5250                       " FILE_NAME,"
5251                       " TOTAL_EXTENTS,"
5252                       " INITIAL_SIZE,"
5253                       " ENGINE,"
5254                       " EXTRA"
5255                       " FROM INFORMATION_SCHEMA.FILES"
5256                       " WHERE FILE_TYPE = 'UNDO LOG'"
5257                       " AND FILE_NAME IS NOT NULL"
5258                       " AND LOGFILE_GROUP_NAME IS NOT NULL",
5259                       256, 1024);
5260   if(ts_where)
5261   {
5262     dynstr_append_checked(&sqlbuf,
5263                   " AND LOGFILE_GROUP_NAME IN ("
5264                   "SELECT DISTINCT LOGFILE_GROUP_NAME"
5265                   " FROM INFORMATION_SCHEMA.FILES"
5266                   " WHERE FILE_TYPE = 'DATAFILE'"
5267                   );
5268     dynstr_append_checked(&sqlbuf, ts_where);
5269     dynstr_append_checked(&sqlbuf, ")");
5270   }
5271   dynstr_append_checked(&sqlbuf,
5272                 " GROUP BY LOGFILE_GROUP_NAME, FILE_NAME"
5273                 ", ENGINE, TOTAL_EXTENTS, INITIAL_SIZE"
5274                 " ORDER BY LOGFILE_GROUP_NAME");
5275 
5276   if (mysql_query(mysql, sqlbuf.str) ||
5277       !(tableres = mysql_store_result(mysql)))
5278   {
5279     dynstr_free(&sqlbuf);
5280     if (mysql_errno(mysql) == ER_BAD_TABLE_ERROR ||
5281         mysql_errno(mysql) == ER_BAD_DB_ERROR ||
5282         mysql_errno(mysql) == ER_UNKNOWN_TABLE)
5283     {
5284       fprintf(md_result_file,
5285               "\n--\n-- Not dumping tablespaces as no INFORMATION_SCHEMA.FILES"
5286               " table on this server\n--\n");
5287       check_io(md_result_file);
5288       DBUG_RETURN(0);
5289     }
5290 
5291     my_printf_error(0, "Error: '%s' when trying to dump tablespaces",
5292                     MYF(0), mysql_error(mysql));
5293     DBUG_RETURN(1);
5294   }
5295 
5296   buf[0]= 0;
5297   while ((row= mysql_fetch_row(tableres)))
5298   {
5299     if (strcmp(buf, row[0]) != 0)
5300       first= 1;
5301     if (first)
5302     {
5303       print_comment(md_result_file, 0, "\n--\n-- Logfile group: %s\n--\n",
5304                     row[0]);
5305 
5306       fprintf(md_result_file, "\nCREATE");
5307     }
5308     else
5309     {
5310       fprintf(md_result_file, "\nALTER");
5311     }
5312     fprintf(md_result_file,
5313             " LOGFILE GROUP %s\n"
5314             "  ADD UNDOFILE '%s'\n",
5315             row[0],
5316             row[1]);
5317     if (first)
5318     {
5319       ubs= strstr(row[5],extra_format);
5320       if(!ubs)
5321         break;
5322       ubs+= strlen(extra_format);
5323       endsemi= strstr(ubs,";");
5324       if(endsemi)
5325         endsemi[0]= '\0';
5326       fprintf(md_result_file,
5327               "  UNDO_BUFFER_SIZE %s\n",
5328               ubs);
5329     }
5330     fprintf(md_result_file,
5331             "  INITIAL_SIZE %s\n"
5332             "  ENGINE=%s;\n",
5333             row[3],
5334             row[4]);
5335     check_io(md_result_file);
5336     if (first)
5337     {
5338       first= 0;
5339       strxmov(buf, row[0], NullS);
5340     }
5341   }
5342   dynstr_free(&sqlbuf);
5343   mysql_free_result(tableres);
5344   init_dynamic_string_checked(&sqlbuf,
5345                       "SELECT DISTINCT TABLESPACE_NAME,"
5346                       " FILE_NAME,"
5347                       " LOGFILE_GROUP_NAME,"
5348                       " EXTENT_SIZE,"
5349                       " INITIAL_SIZE,"
5350                       " ENGINE"
5351                       " FROM INFORMATION_SCHEMA.FILES"
5352                       " WHERE FILE_TYPE = 'DATAFILE'",
5353                       256, 1024);
5354 
5355   if(ts_where)
5356     dynstr_append_checked(&sqlbuf, ts_where);
5357 
5358   dynstr_append_checked(&sqlbuf, " ORDER BY TABLESPACE_NAME, LOGFILE_GROUP_NAME");
5359 
5360   if (mysql_query_with_error_report(mysql, &tableres, sqlbuf.str))
5361   {
5362     dynstr_free(&sqlbuf);
5363     DBUG_RETURN(1);
5364   }
5365 
5366   buf[0]= 0;
5367   while ((row= mysql_fetch_row(tableres)))
5368   {
5369     if (strcmp(buf, row[0]) != 0)
5370       first= 1;
5371     if (first)
5372     {
5373       print_comment(md_result_file, 0, "\n--\n-- Tablespace: %s\n--\n", row[0]);
5374       fprintf(md_result_file, "\nCREATE");
5375     }
5376     else
5377     {
5378       fprintf(md_result_file, "\nALTER");
5379     }
5380     fprintf(md_result_file,
5381             " TABLESPACE %s\n"
5382             "  ADD DATAFILE '%s'\n",
5383             row[0],
5384             row[1]);
5385     if (first)
5386     {
5387       fprintf(md_result_file,
5388               "  USE LOGFILE GROUP %s\n"
5389               "  EXTENT_SIZE %s\n",
5390               row[2],
5391               row[3]);
5392     }
5393     fprintf(md_result_file,
5394             "  INITIAL_SIZE %s\n"
5395             "  ENGINE=%s;\n",
5396             row[4],
5397             row[5]);
5398     check_io(md_result_file);
5399     if (first)
5400     {
5401       first= 0;
5402       strxmov(buf, row[0], NullS);
5403     }
5404   }
5405 
5406   mysql_free_result(tableres);
5407   dynstr_free(&sqlbuf);
5408   DBUG_RETURN(0);
5409 }
5410 
5411 
5412 static int
is_ndbinfo(MYSQL * mysql,const char * dbname)5413 is_ndbinfo(MYSQL* mysql, const char* dbname)
5414 {
5415   static int checked_ndbinfo= 0;
5416   static int have_ndbinfo= 0;
5417 
5418   if (!checked_ndbinfo)
5419   {
5420     MYSQL_RES *res;
5421     MYSQL_ROW row;
5422     char buf[32], query[64];
5423 
5424     my_snprintf(query, sizeof(query),
5425                 "SHOW VARIABLES LIKE %s",
5426                 quote_for_like("ndbinfo_version", buf));
5427 
5428     checked_ndbinfo= 1;
5429 
5430     if (mysql_query_with_error_report(mysql, &res, query))
5431       return 0;
5432 
5433     if (!(row= mysql_fetch_row(res)))
5434     {
5435       mysql_free_result(res);
5436       return 0;
5437     }
5438 
5439     have_ndbinfo= 1;
5440     mysql_free_result(res);
5441   }
5442 
5443   if (!have_ndbinfo)
5444     return 0;
5445 
5446   if (my_strcasecmp(&my_charset_latin1, dbname, "ndbinfo") == 0)
5447     return 1;
5448 
5449   return 0;
5450 }
5451 
5452 
dump_all_databases()5453 static int dump_all_databases()
5454 {
5455   MYSQL_ROW row;
5456   MYSQL_RES *tableres;
5457   int result=0;
5458 
5459   my_ulonglong total_databases= 0;
5460   char **database_list;
5461   uint db_cnt = 0, cnt = 0;
5462   uint mysql_db_found = 0;
5463 
5464   if (mysql_query_with_error_report(mysql, &tableres, "SHOW DATABASES"))
5465     return 1;
5466 
5467   total_databases= mysql_num_rows(tableres);
5468   database_list = (char **) my_malloc(PSI_NOT_INSTRUMENTED,
5469     (sizeof(char *) * total_databases), MYF(MY_WME));
5470 
5471   while ((row= mysql_fetch_row(tableres)))
5472   {
5473     if (mysql_get_server_version(mysql) >= FIRST_INFORMATION_SCHEMA_VERSION &&
5474         !my_strcasecmp(&my_charset_latin1, row[0], INFORMATION_SCHEMA_DB_NAME))
5475       continue;
5476 
5477     if (mysql_get_server_version(mysql) >= FIRST_PERFORMANCE_SCHEMA_VERSION &&
5478         !my_strcasecmp(&my_charset_latin1, row[0], PERFORMANCE_SCHEMA_DB_NAME))
5479       continue;
5480 
5481     if (mysql_get_server_version(mysql) >= FIRST_SYS_SCHEMA_VERSION &&
5482         !my_strcasecmp(&my_charset_latin1, row[0], SYS_SCHEMA_DB_NAME))
5483       continue;
5484 
5485     if (is_ndbinfo(mysql, row[0]))
5486       continue;
5487 
5488     if (mysql_db_found || (!my_strcasecmp(charset_info, row[0], "mysql")))
5489     {
5490       if (dump_all_tables_in_db(row[0]))
5491         result=1;
5492       mysql_db_found = 1;
5493       /*
5494         once mysql database is found dump all dbs saved as part
5495         of database_list
5496       */
5497       for (; cnt < db_cnt; cnt++)
5498       {
5499         if (dump_all_tables_in_db(database_list[cnt]))
5500           result=1;
5501         my_free(database_list[cnt]);
5502       }
5503     }
5504     else
5505     {
5506       /*
5507         till mysql database is not found save database names to
5508         database_list
5509       */
5510       database_list[db_cnt] = my_strdup(PSI_NOT_INSTRUMENTED, row[0],
5511                                         MYF(MY_WME | MY_ZEROFILL));
5512       db_cnt++;
5513     }
5514   }
5515   assert(mysql_db_found);
5516   memset(database_list, 0, sizeof(*database_list));
5517   my_free(database_list);
5518 
5519   mysql_free_result(tableres);
5520   if (seen_views)
5521   {
5522     if (mysql_query(mysql, "SHOW DATABASES") ||
5523         !(tableres= mysql_store_result(mysql)))
5524     {
5525       my_printf_error(0, "Error: Couldn't execute 'SHOW DATABASES': %s",
5526                       MYF(0), mysql_error(mysql));
5527       return 1;
5528     }
5529     while ((row= mysql_fetch_row(tableres)))
5530     {
5531       if (mysql_get_server_version(mysql) >= FIRST_INFORMATION_SCHEMA_VERSION &&
5532           !my_strcasecmp(&my_charset_latin1, row[0], INFORMATION_SCHEMA_DB_NAME))
5533         continue;
5534 
5535       if (mysql_get_server_version(mysql) >= FIRST_PERFORMANCE_SCHEMA_VERSION &&
5536           !my_strcasecmp(&my_charset_latin1, row[0], PERFORMANCE_SCHEMA_DB_NAME))
5537         continue;
5538 
5539       if (mysql_get_server_version(mysql) >= FIRST_SYS_SCHEMA_VERSION &&
5540           !my_strcasecmp(&my_charset_latin1, row[0], SYS_SCHEMA_DB_NAME))
5541         continue;
5542 
5543       if (is_ndbinfo(mysql, row[0]))
5544         continue;
5545 
5546       if (dump_all_views_in_db(row[0]))
5547         result=1;
5548     }
5549     mysql_free_result(tableres);
5550   }
5551   return result;
5552 }
5553 /* dump_all_databases */
5554 
5555 
dump_databases(char ** db_names)5556 static int dump_databases(char **db_names)
5557 {
5558   int result=0;
5559   char **db;
5560   DBUG_ENTER("dump_databases");
5561 
5562   for (db= db_names ; *db ; db++)
5563   {
5564     if (dump_all_tables_in_db(*db))
5565       result=1;
5566   }
5567   if (!result && seen_views)
5568   {
5569     for (db= db_names ; *db ; db++)
5570     {
5571       if (dump_all_views_in_db(*db))
5572         result=1;
5573     }
5574   }
5575   DBUG_RETURN(result);
5576 } /* dump_databases */
5577 
5578 
5579 /*
5580 View Specific database initalization.
5581 
5582 SYNOPSIS
5583   init_dumping_views
5584   qdatabase      quoted name of the database
5585   is_mysql_db    TRUE if the db is mysql, else FALSE
5586 
5587 RETURN VALUES
5588   0        Success.
5589   1        Failure.
5590 */
init_dumping_views(char * qdatabase MY_ATTRIBUTE ((unused)),my_bool is_mysql_db MY_ATTRIBUTE ((unused)))5591 int init_dumping_views(char *qdatabase MY_ATTRIBUTE((unused)),
5592                        my_bool is_mysql_db MY_ATTRIBUTE((unused)))
5593 {
5594     return 0;
5595 } /* init_dumping_views */
5596 
5597 
5598 /*
5599 Table Specific database initalization.
5600 
5601 SYNOPSIS
5602   init_dumping_tables
5603   qdatabase      quoted name of the database
5604   is_mysql_db    TRUE if the db is mysql, else FALSE
5605 
5606 RETURN VALUES
5607   0        Success.
5608   1        Failure.
5609 */
5610 
init_dumping_tables(char * qdatabase,my_bool is_mysql_db)5611 int init_dumping_tables(char *qdatabase, my_bool is_mysql_db)
5612 {
5613   DBUG_ENTER("init_dumping_tables");
5614 
5615   if (!opt_create_db)
5616   {
5617     char qbuf[256];
5618     MYSQL_ROW row;
5619     MYSQL_RES *dbinfo;
5620 
5621     my_snprintf(qbuf, sizeof(qbuf),
5622                 "SHOW CREATE DATABASE IF NOT EXISTS %s",
5623                 qdatabase);
5624 
5625     if (mysql_query(mysql, qbuf) || !(dbinfo = mysql_store_result(mysql)))
5626     {
5627       /* Old server version, dump generic CREATE DATABASE */
5628       if (opt_drop_database && (!opt_skip_mysql_schema || !is_mysql_db))
5629         fprintf(md_result_file,
5630                 "\n/*!40000 DROP DATABASE IF EXISTS %s*/;\n",
5631                 qdatabase);
5632       fprintf(md_result_file,
5633               "\nCREATE DATABASE /*!32312 IF NOT EXISTS*/ %s;\n",
5634               qdatabase);
5635     }
5636     else
5637     {
5638       if (opt_drop_database && (!opt_skip_mysql_schema || !is_mysql_db))
5639         fprintf(md_result_file,
5640                 "\n/*!40000 DROP DATABASE IF EXISTS %s*/;\n",
5641                 qdatabase);
5642       row = mysql_fetch_row(dbinfo);
5643       if (row[1])
5644       {
5645         fprintf(md_result_file,"\n%s;\n",row[1]);
5646       }
5647       mysql_free_result(dbinfo);
5648     }
5649   }
5650   DBUG_RETURN(0);
5651 } /* init_dumping_tables */
5652 
5653 
init_dumping(char * database,int init_func (char *,my_bool))5654 static int init_dumping(char *database, int init_func(char*, my_bool))
5655 {
5656   if (is_ndbinfo(mysql, database))
5657   {
5658     verbose_msg("-- Skipping dump of ndbinfo database\n");
5659     return 0;
5660   }
5661 
5662   if (mysql_select_db(mysql, database))
5663   {
5664     DB_error(mysql, "when selecting the database");
5665     return 1;                   /* If --force */
5666   }
5667   if (!path && !opt_xml)
5668   {
5669     if (opt_databases || opt_alldbs)
5670     {
5671       /*
5672         length of table name * 2 (if name contains quotes), 2 quotes and 0
5673       */
5674       char quoted_database_buf[NAME_LEN*2+3];
5675       char *qdatabase= quote_name(database,quoted_database_buf,opt_quoted);
5676       my_bool freemem= FALSE;
5677       char const* text= fix_identifier_with_newline(qdatabase, &freemem);
5678       my_bool is_mysql_db= !my_strcasecmp(charset_info, database, "mysql");
5679 
5680       print_comment(md_result_file, 0, "\n--\n-- Current Database: %s\n--\n",
5681                     text);
5682       if (freemem)
5683         my_free((void*)text);
5684 
5685       /* Call the view or table specific function */
5686       init_func(qdatabase, is_mysql_db);
5687 
5688       fprintf(md_result_file,"\nUSE %s;\n", qdatabase);
5689       check_io(md_result_file);
5690     }
5691   }
5692   return 0;
5693 } /* init_dumping */
5694 
5695 
5696 /* Return 1 if we should copy the table */
5697 
include_table(const uchar * hash_key,size_t len)5698 my_bool include_table(const uchar *hash_key, size_t len)
5699 {
5700   return ! my_hash_search(&ignore_table, hash_key, len);
5701 }
5702 
5703 
dump_all_tables_in_db(char * database)5704 static int dump_all_tables_in_db(char *database)
5705 {
5706   char *table;
5707   uint numrows;
5708   char table_buff[NAME_LEN*2+3];
5709   char hash_key[2*NAME_LEN+2];  /* "db.tablename" */
5710   char *afterdot;
5711   my_bool general_log_table_exists= 0, slow_log_table_exists=0;
5712   int using_mysql_db= !my_strcasecmp(charset_info, database, "mysql");
5713   my_bool real_columns[MAX_FIELDS];
5714 
5715   DBUG_ENTER("dump_all_tables_in_db");
5716 
5717   afterdot= my_stpcpy(hash_key, database);
5718   *afterdot++= '.';
5719 
5720   if (init_dumping(database, init_dumping_tables))
5721     DBUG_RETURN(1);
5722   if (opt_xml)
5723     print_xml_tag(md_result_file, "", "\n", "database", "name=", database, NullS);
5724 
5725   if (lock_tables)
5726   {
5727     DYNAMIC_STRING query;
5728     init_dynamic_string_checked(&query, "LOCK TABLES ", 256, 1024);
5729     for (numrows= 0 ; (table= getTableName(1)) ; )
5730     {
5731       char *end= my_stpcpy(afterdot, table);
5732       if (include_table((uchar*) hash_key,end - hash_key))
5733       {
5734         numrows++;
5735         dynstr_append_checked(&query, quote_name(table, table_buff, 1));
5736         dynstr_append_checked(&query, " READ /*!32311 LOCAL */,");
5737       }
5738     }
5739     if (numrows && mysql_real_query(mysql, query.str, (ulong)(query.length-1)))
5740       DB_error(mysql, "when using LOCK TABLES");
5741             /* We shall continue here, if --force was given */
5742     dynstr_free(&query);
5743   }
5744   if (flush_logs)
5745   {
5746     if (mysql_refresh(mysql, REFRESH_LOG))
5747       DB_error(mysql, "when doing refresh");
5748            /* We shall continue here, if --force was given */
5749     else
5750       verbose_msg("-- dump_all_tables_in_db : logs flushed successfully!\n");
5751   }
5752   if (opt_single_transaction && mysql_get_server_version(mysql) >= 50500)
5753   {
5754     verbose_msg("-- Setting savepoint...\n");
5755     if (mysql_query_with_error_report(mysql, 0, "SAVEPOINT sp"))
5756       DBUG_RETURN(1);
5757   }
5758   while ((table= getTableName(0)))
5759   {
5760     char *end= my_stpcpy(afterdot, table);
5761     if (include_table((uchar*) hash_key, end - hash_key))
5762     {
5763       dump_table(table,database);
5764       my_free(order_by);
5765       order_by= 0;
5766       if (opt_dump_triggers && mysql_get_server_version(mysql) >= 50009)
5767       {
5768         if (dump_triggers_for_table(table, database))
5769         {
5770           if (path)
5771             my_fclose(md_result_file, MYF(MY_WME));
5772           maybe_exit(EX_MYSQLERR);
5773         }
5774       }
5775 
5776       /**
5777         ROLLBACK TO SAVEPOINT in --single-transaction mode to release metadata
5778         lock on table which was already dumped. This allows to avoid blocking
5779         concurrent DDL on this table without sacrificing correctness, as we
5780         won't access table second time and dumps created by --single-transaction
5781         mode have validity point at the start of transaction anyway.
5782         Note that this doesn't make --single-transaction mode with concurrent
5783         DDL safe in general case. It just improves situation for people for whom
5784         it might be working.
5785       */
5786       if (opt_single_transaction && mysql_get_server_version(mysql) >= 50500)
5787       {
5788         verbose_msg("-- Rolling back to savepoint sp...\n");
5789         if (mysql_query_with_error_report(mysql, 0, "ROLLBACK TO SAVEPOINT sp"))
5790           maybe_exit(EX_MYSQLERR);
5791       }
5792     }
5793     else
5794     {
5795       /*
5796         If general_log and slow_log exists in the 'mysql' database,
5797          we should dump the table structure. But we cannot
5798          call get_table_structure() here as 'LOCK TABLES' query got executed
5799          above on the session and that 'LOCK TABLES' query does not contain
5800          'general_log' and 'slow_log' tables. (you cannot acquire lock
5801          on log tables). Hence mark the existence of these log tables here and
5802          after 'UNLOCK TABLES' query is executed on the session, get the table
5803          structure from server and dump it in the file.
5804       */
5805       if (using_mysql_db)
5806       {
5807         if (!my_strcasecmp(charset_info, table, "general_log"))
5808           general_log_table_exists= 1;
5809         else if (!my_strcasecmp(charset_info, table, "slow_log"))
5810           slow_log_table_exists= 1;
5811       }
5812     }
5813   }
5814 
5815   if (opt_single_transaction && mysql_get_server_version(mysql) >= 50500)
5816   {
5817     verbose_msg("-- Releasing savepoint...\n");
5818     if (mysql_query_with_error_report(mysql, 0, "RELEASE SAVEPOINT sp"))
5819       DBUG_RETURN(1);
5820   }
5821 
5822   if (opt_events && mysql_get_server_version(mysql) >= 50106)
5823   {
5824     DBUG_PRINT("info", ("Dumping events for database %s", database));
5825     dump_events_for_db(database);
5826   }
5827   if (opt_routines && mysql_get_server_version(mysql) >= 50009)
5828   {
5829     DBUG_PRINT("info", ("Dumping routines for database %s", database));
5830     dump_routines_for_db(database);
5831   }
5832   if (opt_xml)
5833   {
5834     fputs("</database>\n", md_result_file);
5835     check_io(md_result_file);
5836   }
5837   if (lock_tables)
5838     (void) mysql_query_with_error_report(mysql, 0, "UNLOCK TABLES");
5839   if (using_mysql_db)
5840   {
5841     char table_type[NAME_LEN];
5842     char ignore_flag;
5843     if (general_log_table_exists)
5844     {
5845       if (!get_table_structure((char *) "general_log",
5846                                database, table_type, &ignore_flag,
5847                                real_columns))
5848         verbose_msg("-- Warning: get_table_structure() failed with some internal "
5849                     "error for 'general_log' table\n");
5850     }
5851     if (slow_log_table_exists)
5852     {
5853       if (!get_table_structure((char *) "slow_log",
5854                                database, table_type, &ignore_flag,
5855                                real_columns))
5856         verbose_msg("-- Warning: get_table_structure() failed with some internal "
5857                     "error for 'slow_log' table\n");
5858     }
5859   }
5860   if (flush_privileges && using_mysql_db)
5861   {
5862     fprintf(md_result_file,"\n--\n-- Flush Grant Tables \n--\n");
5863     fprintf(md_result_file,"\n/*! FLUSH PRIVILEGES */;\n");
5864   }
5865   DBUG_RETURN(0);
5866 } /* dump_all_tables_in_db */
5867 
5868 
5869 /*
5870    dump structure of views of database
5871 
5872    SYNOPSIS
5873      dump_all_views_in_db()
5874      database  database name
5875 
5876   RETURN
5877     0 OK
5878     1 ERROR
5879 */
5880 
dump_all_views_in_db(char * database)5881 static my_bool dump_all_views_in_db(char *database)
5882 {
5883   char *table;
5884   uint numrows;
5885   char table_buff[NAME_LEN*2+3];
5886   char hash_key[2*NAME_LEN+2];  /* "db.tablename" */
5887   char *afterdot;
5888 
5889   afterdot= my_stpcpy(hash_key, database);
5890   *afterdot++= '.';
5891 
5892   if (init_dumping(database, init_dumping_views))
5893     return 1;
5894   if (opt_xml)
5895     print_xml_tag(md_result_file, "", "\n", "database", "name=", database, NullS);
5896   if (lock_tables)
5897   {
5898     DYNAMIC_STRING query;
5899     init_dynamic_string_checked(&query, "LOCK TABLES ", 256, 1024);
5900     for (numrows= 0 ; (table= getTableName(1)); )
5901     {
5902       char *end= my_stpcpy(afterdot, table);
5903       if (include_table((uchar*) hash_key,end - hash_key))
5904       {
5905         numrows++;
5906         dynstr_append_checked(&query, quote_name(table, table_buff, 1));
5907         dynstr_append_checked(&query, " READ /*!32311 LOCAL */,");
5908       }
5909     }
5910     if (numrows && mysql_real_query(mysql, query.str, (ulong)(query.length-1)))
5911       DB_error(mysql, "when using LOCK TABLES");
5912             /* We shall continue here, if --force was given */
5913     dynstr_free(&query);
5914   }
5915   if (flush_logs)
5916   {
5917     if (mysql_refresh(mysql, REFRESH_LOG))
5918       DB_error(mysql, "when doing refresh");
5919            /* We shall continue here, if --force was given */
5920     else
5921       verbose_msg("-- dump_all_views_in_db : logs flushed successfully!\n");
5922   }
5923   while ((table= getTableName(0)))
5924   {
5925     char *end= my_stpcpy(afterdot, table);
5926     if (include_table((uchar*) hash_key, end - hash_key))
5927       get_view_structure(table, database);
5928   }
5929   if (opt_xml)
5930   {
5931     fputs("</database>\n", md_result_file);
5932     check_io(md_result_file);
5933   }
5934   if (lock_tables)
5935     (void) mysql_query_with_error_report(mysql, 0, "UNLOCK TABLES");
5936   return 0;
5937 } /* dump_all_tables_in_db */
5938 
5939 
5940 /*
5941   get_actual_table_name -- executes a SHOW TABLES LIKE '%s' to get the actual
5942   table name from the server for the table name given on the command line.
5943   we do this because the table name given on the command line may be a
5944   different case (e.g.  T1 vs t1)
5945 
5946   RETURN
5947     pointer to the table name
5948     0 if error
5949 */
5950 
get_actual_table_name(const char * old_table_name,MEM_ROOT * root)5951 static char *get_actual_table_name(const char *old_table_name, MEM_ROOT *root)
5952 {
5953   char *name= 0;
5954   MYSQL_RES  *table_res;
5955   MYSQL_ROW  row;
5956   char query[50 + 2*NAME_LEN];
5957   char show_name_buff[FN_REFLEN];
5958   DBUG_ENTER("get_actual_table_name");
5959 
5960   /* Check memory for quote_for_like() */
5961   assert(2*sizeof(old_table_name) < sizeof(show_name_buff));
5962   my_snprintf(query, sizeof(query), "SHOW TABLES LIKE %s",
5963               quote_for_like(old_table_name, show_name_buff));
5964 
5965   if (mysql_query_with_error_report(mysql, 0, query))
5966     DBUG_RETURN(NullS);
5967 
5968   if ((table_res= mysql_store_result(mysql)))
5969   {
5970     my_ulonglong num_rows= mysql_num_rows(table_res);
5971     if (num_rows > 0)
5972     {
5973       ulong *lengths;
5974       /*
5975         Return first row
5976         TODO: Return all matching rows
5977       */
5978       row= mysql_fetch_row(table_res);
5979       lengths= mysql_fetch_lengths(table_res);
5980       name= strmake_root(root, row[0], lengths[0]);
5981     }
5982     mysql_free_result(table_res);
5983   }
5984   DBUG_PRINT("exit", ("new_table_name: %s", name));
5985   DBUG_RETURN(name);
5986 }
5987 
5988 
dump_selected_tables(char * db,char ** table_names,int tables)5989 static int dump_selected_tables(char *db, char **table_names, int tables)
5990 {
5991   char table_buff[NAME_LEN*2+3];
5992   DYNAMIC_STRING lock_tables_query;
5993   MEM_ROOT root;
5994   char **dump_tables, **pos, **end;
5995   DBUG_ENTER("dump_selected_tables");
5996 
5997   if (init_dumping(db, init_dumping_tables))
5998     DBUG_RETURN(1);
5999 
6000   init_alloc_root(PSI_NOT_INSTRUMENTED, &root, 8192, 0);
6001   if (!(dump_tables= pos= (char**) alloc_root(&root, tables * sizeof(char *))))
6002      die(EX_EOM, "alloc_root failure.");
6003 
6004   init_dynamic_string_checked(&lock_tables_query, "LOCK TABLES ", 256, 1024);
6005   for (; tables > 0 ; tables-- , table_names++)
6006   {
6007     /* the table name passed on commandline may be wrong case */
6008     if ((*pos= get_actual_table_name(*table_names, &root)))
6009     {
6010       /* Add found table name to lock_tables_query */
6011       if (lock_tables)
6012       {
6013         dynstr_append_checked(&lock_tables_query, quote_name(*pos, table_buff, 1));
6014         dynstr_append_checked(&lock_tables_query, " READ /*!32311 LOCAL */,");
6015       }
6016       pos++;
6017     }
6018     else
6019     {
6020       if (!opt_force)
6021       {
6022         dynstr_free(&lock_tables_query);
6023         free_root(&root, MYF(0));
6024       }
6025       maybe_die(EX_ILLEGAL_TABLE, "Couldn't find table: \"%s\"", *table_names);
6026       /* We shall countinue here, if --force was given */
6027     }
6028   }
6029   end= pos;
6030 
6031   /* Can't LOCK TABLES in I_S / P_S, so don't try. */
6032   if (lock_tables &&
6033       !(mysql_get_server_version(mysql) >= FIRST_INFORMATION_SCHEMA_VERSION &&
6034         !my_strcasecmp(&my_charset_latin1, db, INFORMATION_SCHEMA_DB_NAME)) &&
6035       !(mysql_get_server_version(mysql) >= FIRST_PERFORMANCE_SCHEMA_VERSION &&
6036         !my_strcasecmp(&my_charset_latin1, db, PERFORMANCE_SCHEMA_DB_NAME)))
6037   {
6038     if (mysql_real_query(mysql, lock_tables_query.str,
6039                          (ulong)(lock_tables_query.length-1)))
6040     {
6041       if (!opt_force)
6042       {
6043         dynstr_free(&lock_tables_query);
6044         free_root(&root, MYF(0));
6045       }
6046       DB_error(mysql, "when doing LOCK TABLES");
6047        /* We shall countinue here, if --force was given */
6048     }
6049   }
6050   dynstr_free(&lock_tables_query);
6051   if (flush_logs)
6052   {
6053     if (mysql_refresh(mysql, REFRESH_LOG))
6054     {
6055       if (!opt_force)
6056         free_root(&root, MYF(0));
6057       DB_error(mysql, "when doing refresh");
6058     }
6059      /* We shall countinue here, if --force was given */
6060     else
6061       verbose_msg("-- dump_selected_tables : logs flushed successfully!\n");
6062   }
6063   if (opt_xml)
6064     print_xml_tag(md_result_file, "", "\n", "database", "name=", db, NullS);
6065 
6066   if (opt_single_transaction && mysql_get_server_version(mysql) >= 50500)
6067   {
6068     verbose_msg("-- Setting savepoint...\n");
6069     if (mysql_query_with_error_report(mysql, 0, "SAVEPOINT sp"))
6070       DBUG_RETURN(1);
6071   }
6072 
6073   /* Dump each selected table */
6074   for (pos= dump_tables; pos < end; pos++)
6075   {
6076     DBUG_PRINT("info",("Dumping table %s", *pos));
6077     dump_table(*pos, db);
6078     if (opt_dump_triggers &&
6079         mysql_get_server_version(mysql) >= 50009)
6080     {
6081       if (dump_triggers_for_table(*pos, db))
6082       {
6083         if (path)
6084           my_fclose(md_result_file, MYF(MY_WME));
6085         maybe_exit(EX_MYSQLERR);
6086       }
6087     }
6088 
6089     /**
6090       ROLLBACK TO SAVEPOINT in --single-transaction mode to release metadata
6091       lock on table which was already dumped. This allows to avoid blocking
6092       concurrent DDL on this table without sacrificing correctness, as we
6093       won't access table second time and dumps created by --single-transaction
6094       mode have validity point at the start of transaction anyway.
6095       Note that this doesn't make --single-transaction mode with concurrent
6096       DDL safe in general case. It just improves situation for people for whom
6097       it might be working.
6098     */
6099     if (opt_single_transaction && mysql_get_server_version(mysql) >= 50500)
6100     {
6101       verbose_msg("-- Rolling back to savepoint sp...\n");
6102       if (mysql_query_with_error_report(mysql, 0, "ROLLBACK TO SAVEPOINT sp"))
6103         maybe_exit(EX_MYSQLERR);
6104     }
6105   }
6106 
6107   if (opt_single_transaction && mysql_get_server_version(mysql) >= 50500)
6108   {
6109     verbose_msg("-- Releasing savepoint...\n");
6110     if (mysql_query_with_error_report(mysql, 0, "RELEASE SAVEPOINT sp"))
6111       DBUG_RETURN(1);
6112 
6113   }
6114 
6115   /* Dump each selected view */
6116   if (seen_views)
6117   {
6118     for (pos= dump_tables; pos < end; pos++)
6119       get_view_structure(*pos, db);
6120   }
6121   if (opt_events && mysql_get_server_version(mysql) >= 50106)
6122   {
6123     DBUG_PRINT("info", ("Dumping events for database %s", db));
6124     dump_events_for_db(db);
6125   }
6126   /* obtain dump of routines (procs/functions) */
6127   if (opt_routines && mysql_get_server_version(mysql) >= 50009)
6128   {
6129     DBUG_PRINT("info", ("Dumping routines for database %s", db));
6130     dump_routines_for_db(db);
6131   }
6132   free_root(&root, MYF(0));
6133   my_free(order_by);
6134   order_by= 0;
6135   if (opt_xml)
6136   {
6137     fputs("</database>\n", md_result_file);
6138     check_io(md_result_file);
6139   }
6140   if (lock_tables)
6141     (void) mysql_query_with_error_report(mysql, 0, "UNLOCK TABLES");
6142   DBUG_RETURN(0);
6143 } /* dump_selected_tables */
6144 
6145 
do_show_master_status(MYSQL * mysql_con,int consistent_binlog_pos)6146 static int do_show_master_status(MYSQL *mysql_con, int consistent_binlog_pos)
6147 {
6148   MYSQL_ROW row;
6149   MYSQL_RES *master;
6150   char binlog_pos_file[FN_REFLEN];
6151   char binlog_pos_offset[LONGLONG_LEN+1];
6152   char *file, *offset;
6153   const char *comment_prefix=
6154     (opt_master_data == MYSQL_OPT_MASTER_DATA_COMMENTED_SQL) ? "-- " : "";
6155 
6156   if (consistent_binlog_pos)
6157   {
6158     if (!check_consistent_binlog_pos(binlog_pos_file, binlog_pos_offset))
6159       return 1;
6160 
6161     file= binlog_pos_file;
6162     offset= binlog_pos_offset;
6163   }
6164   else
6165   {
6166     if (mysql_query_with_error_report(mysql_con, &master, "SHOW MASTER STATUS"))
6167       return 1;
6168 
6169     row= mysql_fetch_row(master);
6170     if (row && row[0] && row[1])
6171     {
6172       file= row[0];
6173       offset= row[1];
6174     }
6175     else
6176     {
6177       mysql_free_result(master);
6178       if (!opt_force)
6179       {
6180         /* SHOW MASTER STATUS reports nothing and --force is not enabled */
6181         my_printf_error(0, "Error: Binlogging on server not active", MYF(0));
6182         maybe_exit(EX_MYSQLERR);
6183         return 1;
6184       }
6185       else
6186       {
6187         return 0;
6188       }
6189     }
6190   }
6191 
6192   /* SHOW MASTER STATUS reports file and position */
6193   print_comment(md_result_file, 0,
6194                 "\n--\n-- Position to start replication or point-in-time "
6195                 "recovery from\n--\n\n");
6196   fprintf(md_result_file,
6197           "%sCHANGE MASTER TO MASTER_LOG_FILE='%s', MASTER_LOG_POS=%s;\n",
6198           comment_prefix, file, offset);
6199   check_io(md_result_file);
6200 
6201   if (!consistent_binlog_pos)
6202     mysql_free_result(master);
6203 
6204   return 0;
6205 }
6206 
do_stop_slave_sql(MYSQL * mysql_con)6207 static int do_stop_slave_sql(MYSQL *mysql_con)
6208 {
6209   MYSQL_RES *slave;
6210   /* We need to check if the slave sql is running in the first place */
6211   if (mysql_query_with_error_report(mysql_con, &slave, "SHOW SLAVE STATUS"))
6212     return(1);
6213   else
6214   {
6215     MYSQL_ROW row= mysql_fetch_row(slave);
6216     if (row && row[11])
6217     {
6218       /* if SLAVE SQL is not running, we don't stop it */
6219       if (!strcmp(row[11],"No"))
6220       {
6221         mysql_free_result(slave);
6222         /* Silently assume that they don't have the slave running */
6223         return(0);
6224       }
6225     }
6226   }
6227   mysql_free_result(slave);
6228 
6229   /* now, stop slave if running */
6230   if (mysql_query_with_error_report(mysql_con, 0, "STOP SLAVE SQL_THREAD"))
6231     return(1);
6232 
6233   return(0);
6234 }
6235 
add_stop_slave(void)6236 static int add_stop_slave(void)
6237 {
6238   if (opt_comments)
6239     fprintf(md_result_file,
6240             "\n--\n-- stop slave statement to make a recovery dump)\n--\n\n");
6241   fprintf(md_result_file, "STOP SLAVE;\n");
6242   return(0);
6243 }
6244 
add_slave_statements(void)6245 static int add_slave_statements(void)
6246 {
6247   if (opt_comments)
6248     fprintf(md_result_file,
6249             "\n--\n-- start slave statement to make a recovery dump)\n--\n\n");
6250   fprintf(md_result_file, "START SLAVE;\n");
6251   return(0);
6252 }
6253 
do_show_slave_status(MYSQL * mysql_con)6254 static int do_show_slave_status(MYSQL *mysql_con)
6255 {
6256   MYSQL_RES *slave= NULL;
6257   const char *comment_prefix=
6258     (opt_slave_data == MYSQL_OPT_SLAVE_DATA_COMMENTED_SQL) ? "-- " : "";
6259   if (mysql_query_with_error_report(mysql_con, &slave, "SHOW SLAVE STATUS"))
6260   {
6261     if (!opt_force)
6262     {
6263       /* SHOW SLAVE STATUS reports nothing and --force is not enabled */
6264       my_printf_error(0, "Error: Slave not set up", MYF(0));
6265     }
6266     mysql_free_result(slave);
6267     return 1;
6268   }
6269   else
6270   {
6271     const int n_master_host= 1;
6272     const int n_master_port= 3;
6273     const int n_master_log_file= 9;
6274     const int n_master_log_pos= 21;
6275     const int n_channel_name= 55;
6276     MYSQL_ROW row= mysql_fetch_row(slave);
6277     /* Since 5.7 is is possible that SSS returns multiple channels */
6278     while (row)
6279     {
6280       if (row[n_master_log_file] && row[n_master_log_pos])
6281       {
6282         /* SHOW MASTER STATUS reports file and position */
6283         if (opt_comments)
6284           fprintf(md_result_file,
6285                   "\n--\n-- Position to start replication or point-in-time "
6286                   "recovery from (the master of this slave)\n--\n\n");
6287 
6288         fprintf(md_result_file, "%sCHANGE MASTER TO ", comment_prefix);
6289 
6290         if (opt_include_master_host_port)
6291         {
6292           if (row[n_master_host])
6293             fprintf(md_result_file, "MASTER_HOST='%s', ", row[n_master_host]);
6294           if (row[n_master_port])
6295             fprintf(md_result_file, "MASTER_PORT=%s, ", row[n_master_port]);
6296         }
6297         fprintf(md_result_file,
6298                 "MASTER_LOG_FILE='%s', MASTER_LOG_POS=%s",
6299                 row[n_master_log_file], row[n_master_log_pos]);
6300 
6301         /* Only print the FOR CHANNEL if there is more than one channel */
6302         if (slave->row_count > 1)
6303           fprintf(md_result_file, " FOR CHANNEL '%s'", row[n_channel_name]);
6304 
6305         fprintf(md_result_file, ";\n");
6306       }
6307       row= mysql_fetch_row(slave);
6308     }
6309     check_io(md_result_file);
6310     mysql_free_result(slave);
6311   }
6312   return 0;
6313 }
6314 
do_start_slave_sql(MYSQL * mysql_con)6315 static int do_start_slave_sql(MYSQL *mysql_con)
6316 {
6317   MYSQL_RES *slave;
6318   /* We need to check if the slave sql is stopped in the first place */
6319   if (mysql_query_with_error_report(mysql_con, &slave, "SHOW SLAVE STATUS"))
6320     return(1);
6321   else
6322   {
6323     MYSQL_ROW row= mysql_fetch_row(slave);
6324     if (row && row[11])
6325     {
6326       /* if SLAVE SQL is not running, we don't start it */
6327       if (!strcmp(row[11],"Yes"))
6328       {
6329         mysql_free_result(slave);
6330         /* Silently assume that they don't have the slave running */
6331         return(0);
6332       }
6333     }
6334   }
6335   mysql_free_result(slave);
6336 
6337   /* now, start slave if stopped */
6338   if (mysql_query_with_error_report(mysql_con, 0, "START SLAVE"))
6339   {
6340     my_printf_error(0, "Error: Unable to start slave", MYF(0));
6341     return 1;
6342   }
6343   return(0);
6344 }
6345 
6346 
6347 
do_flush_tables_read_lock(MYSQL * mysql_con)6348 static int do_flush_tables_read_lock(MYSQL *mysql_con)
6349 {
6350   /*
6351     We do first a FLUSH TABLES. If a long update is running, the FLUSH TABLES
6352     will wait but will not stall the whole mysqld, and when the long update is
6353     done the FLUSH TABLES WITH READ LOCK will start and succeed quickly. So,
6354     FLUSH TABLES is to lower the probability of a stage where both mysqldump
6355     and most client connections are stalled. Of course, if a second long
6356     update starts between the two FLUSHes, we have that bad stall.
6357   */
6358   return
6359     ( mysql_query_with_error_report(mysql_con, 0,
6360                                     ((opt_master_data != 0) ?
6361                                         "FLUSH /*!40101 LOCAL */ TABLES" :
6362                                         "FLUSH TABLES")) ||
6363       mysql_query_with_error_report(mysql_con, 0,
6364                                     "FLUSH TABLES WITH READ LOCK") );
6365 }
6366 
6367 /**
6368    Execute LOCK TABLES FOR BACKUP if supported by the server.
6369 
6370    @note If LOCK TABLES FOR BACKUP is not supported by the server, then nothing
6371          is done and no error condition is returned.
6372 
6373    @returns  whether there was an error or not
6374 */
6375 
do_lock_tables_for_backup(MYSQL * mysql_con)6376 static int do_lock_tables_for_backup(MYSQL *mysql_con)
6377 {
6378   return mysql_query_with_error_report(mysql_con, 0,
6379                                        "LOCK TABLES FOR BACKUP");
6380 }
6381 
do_unlock_tables(MYSQL * mysql_con)6382 static int do_unlock_tables(MYSQL *mysql_con)
6383 {
6384   return mysql_query_with_error_report(mysql_con, 0, "UNLOCK TABLES");
6385 }
6386 
get_bin_log_name(MYSQL * mysql_con,char * buff_log_name,uint buff_len)6387 static int get_bin_log_name(MYSQL *mysql_con,
6388                             char* buff_log_name, uint buff_len)
6389 {
6390   MYSQL_RES *res;
6391   MYSQL_ROW row;
6392 
6393   if (mysql_query(mysql_con, "SHOW MASTER STATUS") ||
6394       !(res= mysql_store_result(mysql)))
6395     return 1;
6396 
6397   if (!(row= mysql_fetch_row(res)))
6398   {
6399     mysql_free_result(res);
6400     return 1;
6401   }
6402   /*
6403     Only one row is returned, and the first column is the name of the
6404     active log.
6405   */
6406   strmake(buff_log_name, row[0], buff_len - 1);
6407 
6408   mysql_free_result(res);
6409   return 0;
6410 }
6411 
purge_bin_logs_to(MYSQL * mysql_con,char * log_name)6412 static int purge_bin_logs_to(MYSQL *mysql_con, char* log_name)
6413 {
6414   DYNAMIC_STRING str;
6415   int err;
6416   init_dynamic_string_checked(&str, "PURGE BINARY LOGS TO '", 1024, 1024);
6417   dynstr_append_checked(&str, log_name);
6418   dynstr_append_checked(&str, "'");
6419   err = mysql_query_with_error_report(mysql_con, 0, str.str);
6420   dynstr_free(&str);
6421   return err;
6422 }
6423 
6424 
start_transaction(MYSQL * mysql_con)6425 static int start_transaction(MYSQL *mysql_con)
6426 {
6427   verbose_msg("-- Starting transaction...\n");
6428   /*
6429     We use BEGIN for old servers. --single-transaction --master-data will fail
6430     on old servers, but that's ok as it was already silently broken (it didn't
6431     do a consistent read, so better tell people frankly, with the error).
6432 
6433     We want the first consistent read to be used for all tables to dump so we
6434     need the REPEATABLE READ level (not anything lower, for example READ
6435     COMMITTED would give one new consistent read per dumped table).
6436   */
6437   if ((mysql_get_server_version(mysql_con) < 40100) && opt_master_data)
6438   {
6439     fprintf(stderr, "-- %s: the combination of --single-transaction and "
6440             "--master-data requires a MySQL server version of at least 4.1 "
6441             "(current server's version is %s). %s\n",
6442             opt_force ? "Warning" : "Error",
6443             mysql_con->server_version ? mysql_con->server_version : "unknown",
6444             opt_force ? "Continuing due to --force, backup may not be "
6445             "consistent across all tables!" : "Aborting.");
6446     if (!opt_force)
6447       exit(EX_MYSQLERR);
6448   }
6449 
6450   return (mysql_query_with_error_report(mysql_con, 0,
6451                                         "SET SESSION TRANSACTION ISOLATION "
6452                                         "LEVEL REPEATABLE READ") ||
6453           mysql_query_with_error_report(mysql_con, 0,
6454                                         "START TRANSACTION "
6455                                         "/*!40100 WITH CONSISTENT SNAPSHOT */"));
6456 }
6457 
6458 
find_set(TYPELIB * lib,const char * x,size_t length,char ** err_pos,uint * err_len)6459 static ulong find_set(TYPELIB *lib, const char *x, size_t length,
6460                       char **err_pos, uint *err_len)
6461 {
6462   const char *end= x + length;
6463   ulong found= 0;
6464   uint find;
6465   char buff[255];
6466 
6467   *err_pos= 0;                  /* No error yet */
6468   while (end > x && my_isspace(charset_info, end[-1]))
6469     end--;
6470 
6471   *err_len= 0;
6472   if (x != end)
6473   {
6474     const char *start= x;
6475     for (;;)
6476     {
6477       const char *pos= start;
6478       uint var_len;
6479 
6480       for (; pos != end && *pos != ','; pos++) ;
6481       var_len= (uint) (pos - start);
6482       strmake(buff, start, MY_MIN(sizeof(buff) - 1, var_len));
6483       find= find_type(buff, lib, FIND_TYPE_BASIC);
6484       if (!find)
6485       {
6486         *err_pos= (char*) start;
6487         *err_len= var_len;
6488       }
6489       else
6490         found|= ((longlong) 1 << (find - 1));
6491       if (pos == end)
6492         break;
6493       start= pos + 1;
6494     }
6495   }
6496   return found;
6497 }
6498 
6499 
6500 /* Print a value with a prefix on file */
print_value(FILE * file,MYSQL_RES * result,MYSQL_ROW row,const char * prefix,const char * name,int string_value)6501 static void print_value(FILE *file, MYSQL_RES  *result, MYSQL_ROW row,
6502                         const char *prefix, const char *name,
6503                         int string_value)
6504 {
6505   MYSQL_FIELD   *field;
6506   mysql_field_seek(result, 0);
6507 
6508   for ( ; (field= mysql_fetch_field(result)) ; row++)
6509   {
6510     if (!strcmp(field->name,name))
6511     {
6512       if (row[0] && row[0][0] && strcmp(row[0],"0")) /* Skip default */
6513       {
6514         fputc(' ',file);
6515         fputs(prefix, file);
6516         if (string_value)
6517           unescape(file,row[0], strlen(row[0]));
6518         else
6519           fputs(row[0], file);
6520         check_io(file);
6521         return;
6522       }
6523     }
6524   }
6525   return;                                       /* This shouldn't happen */
6526 } /* print_value */
6527 
6528 
6529 /*
6530   SYNOPSIS
6531 
6532   Check if the table is one of the table types that should be ignored:
6533   MRG_ISAM, MRG_MYISAM.
6534 
6535   If the table should be altogether ignored, it returns a TRUE, FALSE if it
6536   should not be ignored.
6537 
6538   ARGS
6539 
6540     check_if_ignore_table()
6541     table_name                  Table name to check
6542     table_type                  Type of table
6543 
6544   GLOBAL VARIABLES
6545     mysql                       MySQL connection
6546     verbose                     Write warning messages
6547 
6548   RETURN
6549     char (bit value)            See IGNORE_ values at top
6550 */
6551 
check_if_ignore_table(const char * table_name,char * table_type)6552 char check_if_ignore_table(const char *table_name, char *table_type)
6553 {
6554   char result= IGNORE_NONE;
6555   char buff[FN_REFLEN+80], show_name_buff[FN_REFLEN];
6556   MYSQL_RES *res= NULL;
6557   MYSQL_ROW row;
6558   DBUG_ENTER("check_if_ignore_table");
6559 
6560   /* Check memory for quote_for_like() */
6561   assert(2*sizeof(table_name) < sizeof(show_name_buff));
6562   my_snprintf(buff, sizeof(buff), "show table status like %s",
6563               quote_for_like(table_name, show_name_buff));
6564   if (mysql_query_with_error_report(mysql, &res, buff))
6565   {
6566     if (mysql_errno(mysql) != ER_PARSE_ERROR)
6567     {                                   /* If old MySQL version */
6568       verbose_msg("-- Warning: Couldn't get status information for "
6569                   "table %s (%s)\n", table_name, mysql_error(mysql));
6570       DBUG_RETURN(result);                       /* assume table is ok */
6571     }
6572   }
6573   if (!(row= mysql_fetch_row(res)))
6574   {
6575     fprintf(stderr,
6576             "Error: Couldn't read status information for table %s (%s)\n",
6577             table_name, mysql_error(mysql));
6578     mysql_free_result(res);
6579     DBUG_RETURN(result);                         /* assume table is ok */
6580   }
6581   if (!(row[1]))
6582     strmake(table_type, "VIEW", NAME_LEN-1);
6583   else
6584   {
6585     strmake(table_type, row[1], NAME_LEN-1);
6586 
6587     /*  If these two types, we want to skip dumping the table. */
6588     if (!opt_no_data &&
6589         (!my_strcasecmp(&my_charset_latin1, table_type, "MRG_MyISAM") ||
6590          !strcmp(table_type,"MRG_ISAM") ||
6591          !strcmp(table_type,"FEDERATED")))
6592       result= IGNORE_DATA;
6593   }
6594   mysql_free_result(res);
6595   DBUG_RETURN(result);
6596 }
6597 
6598 
6599 /*
6600   Get string of comma-separated primary key field names
6601 
6602   SYNOPSIS
6603     char *primary_key_fields(const char *table_name)
6604     RETURNS     pointer to allocated buffer (must be freed by caller)
6605     table_name  quoted table name
6606 
6607   DESCRIPTION
6608     Use SHOW KEYS FROM table_name, allocate a buffer to hold the
6609     field names, and then build that string and return the pointer
6610     to that buffer.
6611 
6612     Returns NULL if there is no PRIMARY or UNIQUE key on the table,
6613     or if there is some failure.  It is better to continue to dump
6614     the table unsorted, rather than exit without dumping the data.
6615 */
6616 
primary_key_fields(const char * table_name,const my_bool desc)6617 static char *primary_key_fields(const char *table_name, const my_bool desc)
6618 {
6619   MYSQL_RES  *res= NULL;
6620   MYSQL_ROW  row;
6621   /* SHOW KEYS FROM + table name * 2 (escaped) + 2 quotes + \0 */
6622   char show_keys_buff[15 + NAME_LEN * 2 + 3];
6623   size_t result_length= 0;
6624   char *result= 0;
6625   char buff[NAME_LEN * 2 + 3];
6626   char *quoted_field;
6627   static const char *desc_index= " DESC";
6628 
6629   my_snprintf(show_keys_buff, sizeof(show_keys_buff),
6630               "SHOW KEYS FROM %s", table_name);
6631   if (mysql_query(mysql, show_keys_buff) ||
6632       !(res= mysql_store_result(mysql)))
6633   {
6634     fprintf(stderr, "Warning: Couldn't read keys from table %s;"
6635             " records are NOT sorted (%s)\n",
6636             table_name, mysql_error(mysql));
6637     /* Don't exit, because it's better to print out unsorted records */
6638     goto cleanup;
6639   }
6640 
6641   /*
6642    * Figure out the length of the ORDER BY clause result.
6643    * Note that SHOW KEYS is ordered:  a PRIMARY key is always the first
6644    * row, and UNIQUE keys come before others.  So we only need to check
6645    * the first key, not all keys.
6646    */
6647   if ((row= mysql_fetch_row(res)) && atoi(row[1]) == 0)
6648   {
6649     /* Key is unique */
6650     do
6651     {
6652       quoted_field= quote_name(row[4], buff, 0);
6653       result_length+= strlen(quoted_field) + 1; /* + 1 for ',' or \0 */
6654       if (desc)
6655       {
6656         result_length+= strlen(desc_index);
6657       }
6658     } while ((row= mysql_fetch_row(res)) && atoi(row[3]) > 1);
6659   }
6660 
6661   /* Build the ORDER BY clause result */
6662   if (result_length)
6663   {
6664     char *end;
6665     /* result (terminating \0 is already in result_length) */
6666     result= my_malloc(PSI_NOT_INSTRUMENTED,
6667                       result_length + 10, MYF(MY_WME));
6668     if (!result)
6669     {
6670       fprintf(stderr, "Error: Not enough memory to store ORDER BY clause\n");
6671       goto cleanup;
6672     }
6673     mysql_data_seek(res, 0);
6674     row= mysql_fetch_row(res);
6675     quoted_field= quote_name(row[4], buff, 0);
6676     end= my_stpcpy(result, quoted_field);
6677     while ((row= mysql_fetch_row(res)) && atoi(row[3]) > 1)
6678     {
6679       quoted_field= quote_name(row[4], buff, 0);
6680       end= strxmov(end, desc ? " DESC," : ",", quoted_field, NullS);
6681     }
6682     if (desc)
6683     {
6684       end= my_stpmov(end, " DESC");
6685     }
6686   }
6687 
6688 cleanup:
6689   if (res)
6690     mysql_free_result(res);
6691 
6692   return result;
6693 }
6694 
6695 
6696 /*
6697   Replace a substring
6698 
6699   SYNOPSIS
6700     replace
6701     ds_str      The string to search and perform the replace in
6702     search_str  The string to search for
6703     search_len  Length of the string to search for
6704     replace_str The string to replace with
6705     replace_len Length of the string to replace with
6706 
6707   RETURN
6708     0 String replaced
6709     1 Could not find search_str in str
6710 */
6711 
replace(DYNAMIC_STRING * ds_str,const char * search_str,size_t search_len,const char * replace_str,size_t replace_len)6712 static int replace(DYNAMIC_STRING *ds_str,
6713                    const char *search_str, size_t search_len,
6714                    const char *replace_str, size_t replace_len)
6715 {
6716   DYNAMIC_STRING ds_tmp;
6717   const char *start= strstr(ds_str->str, search_str);
6718   if (!start)
6719     return 1;
6720   init_dynamic_string_checked(&ds_tmp, "",
6721                       ds_str->length + replace_len, 256);
6722   dynstr_append_mem_checked(&ds_tmp, ds_str->str, start - ds_str->str);
6723   dynstr_append_mem_checked(&ds_tmp, replace_str, replace_len);
6724   dynstr_append_checked(&ds_tmp, start + search_len);
6725   dynstr_set_checked(ds_str, ds_tmp.str);
6726   dynstr_free(&ds_tmp);
6727   return 0;
6728 }
6729 
6730 
6731 /**
6732   This function sets the session binlog in the dump file.
6733   When --set-gtid-purged is used, this function is called to
6734   disable the session binlog and at the end of the dump, to restore
6735   the session binlog.
6736 
6737   @note: md_result_file should have been opened, before
6738          this function is called.
6739 */
6740 
set_session_binlog()6741 static void set_session_binlog()
6742 {
6743   static my_bool is_binlog_disabled= FALSE;
6744 
6745   if (!is_binlog_disabled)
6746   {
6747     fprintf(md_result_file,
6748             "SET @MYSQLDUMP_TEMP_LOG_BIN = @@SESSION.SQL_LOG_BIN;\n");
6749     fprintf(md_result_file, "SET @@SESSION.SQL_LOG_BIN= 0;\n");
6750     is_binlog_disabled= 1;
6751   }
6752   else
6753   {
6754     fprintf(md_result_file,
6755             "SET @@SESSION.SQL_LOG_BIN = @MYSQLDUMP_TEMP_LOG_BIN;\n");
6756     is_binlog_disabled= 0;
6757   }
6758 }
6759 
6760 
6761 /**
6762   This function gets the GTID_EXECUTED sets from the
6763   server and assigns those sets to GTID_PURGED in the
6764   dump file.
6765 
6766   @param[in]  mysql_con     connection to the server
6767   @param[in]  ftwrl_done    FLUSH TABLES WITH READ LOCK query was issued
6768 
6769   @retval     FALSE         succesfully printed GTID_PURGED sets
6770                              in the dump file.
6771   @retval     TRUE          failed.
6772 
6773 */
6774 
add_set_gtid_purged(MYSQL * mysql_con,my_bool ftwrl_done)6775 static my_bool add_set_gtid_purged(MYSQL *mysql_con, my_bool ftwrl_done)
6776 {
6777   MYSQL_RES  *gtid_purged_res;
6778   MYSQL_ROW  gtid_set;
6779   ulonglong  num_sets, idx;
6780   int        value_idx= 1;
6781   int        capture_raw_gtid_executed= TRUE;
6782   char       gtid_buffer[128];
6783   /*
6784    Check if we didn't already acquire FTWRL
6785    and we are in transaction with consistent snapshot.
6786    If we are, and snapshot of gtid_executed is supported by server - use it.
6787   */
6788   if (!ftwrl_done && opt_single_transaction)
6789   {
6790     if (!consistent_gtid_executed_supported(mysql_con) ||
6791         mysql_query_with_error_report(
6792             mysql_con, &gtid_purged_res,
6793             "SHOW STATUS LIKE 'Binlog_snapshot_gtid_executed'"))
6794     {
6795       fprintf(stderr,
6796               "\nWARNING: Server does not support consistent snapshot of "
6797               "GTID_EXECUTED."
6798               "\nThe value provided for GTID_PURGED may be inaccurate!"
6799               "\nTo overcome this issue, start mysqldump with "
6800               "--lock-all-tables.\n");
6801 
6802       fprintf(md_result_file,
6803               "\n\n--"
6804               "\n-- WARNING: Server does not support consistent snapshot of "
6805               "GTID_EXECUTED."
6806               "\n-- The value provided for GTID_PURGED may be inaccurate!"
6807               "\n-- To overcome this issue, start mysqldump with "
6808               "--lock-all-tables."
6809               "\n--\n");
6810     }
6811     else
6812     {
6813       capture_raw_gtid_executed= FALSE;
6814     }
6815   }
6816 
6817   if (capture_raw_gtid_executed)
6818   {
6819     /* query to get the GTID_EXECUTED */
6820     value_idx= 0;
6821     if (mysql_query_with_error_report(mysql_con, &gtid_purged_res,
6822                                       "SELECT @@GLOBAL.GTID_EXECUTED"))
6823     {
6824       return TRUE;
6825     }
6826   }
6827 
6828   /* Proceed only if gtid_purged_res is non empty */
6829   if ((num_sets= mysql_num_rows(gtid_purged_res)) > 0)
6830   {
6831     gtid_executed_buffer_inited= 1;
6832     init_dynamic_string_checked(&gtid_executed_buffer, "", 1024, 1024);
6833     if (opt_comments)
6834     {
6835       my_snprintf(gtid_buffer, 128,
6836               "\n--\n-- GTID state at the end of the backup"
6837               "\n-- (origin: %s)"
6838               "\n--\n\n",
6839               capture_raw_gtid_executed ? "@@global.gtid_executed"
6840                                         : "Binlog_snapshot_gtid_executed");
6841       dynstr_append_checked(&gtid_executed_buffer, gtid_buffer);
6842     }
6843 
6844     dynstr_append_checked(&gtid_executed_buffer, "SET @@GLOBAL.GTID_PURGED='");
6845 
6846     /* formatting is not required, even for multiple gtid sets */
6847     for (idx= 0; idx< num_sets; idx++)
6848     {
6849       gtid_set= mysql_fetch_row(gtid_purged_res);
6850       dynstr_append_checked(&gtid_executed_buffer, (char *)gtid_set[value_idx]);
6851     }
6852     /* for the last set close the SET expression */
6853     dynstr_append_checked(&gtid_executed_buffer, "';\n");
6854   }
6855   mysql_free_result(gtid_purged_res);
6856 
6857   return FALSE;  /*success */
6858 }
6859 
6860 
6861 /**
6862   This function processes the opt_set_gtid_purged option.
6863   This function when called with the flag as FALSE, just
6864   disables the binlog by calling set_session_binlog().
6865   Later when this function is called with the flag as TRUE,
6866   SET @@GLOBAL.GTID_PURGED is written in the output and the
6867   session binlog is restored if disabled previously.
6868 
6869   @param[in]          mysql_con     the connection to the server
6870   @param[in]		  ftwrl_done    FLUSH TABLES WITH READ LOCK query was issued
6871   @param[in]          flag          If FALSE, just disable binlog and not
6872                                     set the gtid purged as it will be set
6873                                     at a later point of time.
6874                                     If TRUE, set the gtid purged and
6875                                     restore the session binlog if disabled
6876                                     previously.
6877 
6878   @retval             FALSE         successful according to the value
6879                                     of opt_set_gtid_purged.
6880   @retval             TRUE          fail.
6881 */
6882 
process_set_gtid_purged(MYSQL * mysql_con,my_bool ftwrl_done,my_bool flag)6883 static my_bool process_set_gtid_purged(MYSQL* mysql_con, my_bool ftwrl_done,
6884                                        my_bool flag)
6885 {
6886   MYSQL_RES   *gtid_mode_res;
6887   MYSQL_ROW   gtid_mode_row;
6888   char *gtid_mode_val= 0;
6889   static int  gtid_mode= -1;
6890   char buf[32], query[64];
6891 
6892   if (opt_set_gtid_purged_mode == SET_GTID_PURGED_OFF)
6893     return FALSE;  /* nothing to be done */
6894 
6895   /*
6896      Set gtid_mode, by fetching gtid_mode from server, if its not
6897      yet populated. gtid_mode is set to -1 if gtid_mode is not yet
6898      fetched from the server.
6899   */
6900   if (gtid_mode < 0)
6901   {
6902     /*
6903       Check if the server has the knowledge of GTIDs(pre mysql-5.6)
6904       or if the gtid_mode is ON or OFF.
6905     */
6906     my_snprintf(query, sizeof(query), "SHOW VARIABLES LIKE %s",
6907                 quote_for_like("gtid_mode", buf));
6908 
6909     if (mysql_query_with_error_report(mysql_con, &gtid_mode_res, query))
6910       return TRUE;
6911 
6912     gtid_mode_row = mysql_fetch_row(gtid_mode_res);
6913 
6914     /*
6915        gtid_mode_row is NULL for pre 5.6 versions. For versions >= 5.6,
6916        get the gtid_mode value from the second column.
6917     */
6918     gtid_mode_val = gtid_mode_row ? (char*)gtid_mode_row[1] : NULL;
6919     gtid_mode= (gtid_mode_val && strcmp(gtid_mode_val, "OFF")) ? 1 : 0;
6920     mysql_free_result(gtid_mode_res);
6921   }
6922 
6923   if (gtid_mode)
6924   {
6925     /*
6926        For any gtid_mode !=OFF and irrespective of --set-gtid-purged
6927        being AUTO or ON,  add GTID_PURGED in the output.
6928     */
6929     if (!flag)
6930     {
6931       set_session_binlog();
6932       if (add_set_gtid_purged(mysql_con, ftwrl_done))
6933         return TRUE;
6934     }
6935 
6936     else
6937     {
6938       if (flag && (opt_databases || !opt_alldbs || !opt_dump_triggers
6939           || !opt_routines || !opt_events))
6940       {
6941         fprintf(stderr,"Warning: A partial dump from a server that has GTIDs will "
6942                        "by default include the GTIDs of all transactions, even "
6943                        "those that changed suppressed parts of the database. If "
6944                        "you don't want to restore GTIDs, pass "
6945                        "--set-gtid-purged=OFF. To make a complete dump, pass "
6946                        "--all-databases --triggers --routines --events. \n");
6947       }
6948 
6949       fputs(gtid_executed_buffer.str, md_result_file);
6950     }
6951   }
6952   else /* gtid_mode is off */
6953   {
6954     if (flag && opt_set_gtid_purged_mode == SET_GTID_PURGED_ON)
6955     {
6956       fprintf(stderr, "Error: Server has GTIDs disabled.\n");
6957       return TRUE;
6958     }
6959   }
6960 
6961   return FALSE;
6962 }
6963 
6964 
6965 /*
6966   Getting VIEW structure
6967 
6968   SYNOPSIS
6969     get_view_structure()
6970     table   view name
6971     db      db name
6972 
6973   RETURN
6974     0 OK
6975     1 ERROR
6976 */
6977 
get_view_structure(char * table,char * db)6978 static my_bool get_view_structure(char *table, char* db)
6979 {
6980   MYSQL_RES  *table_res;
6981   MYSQL_ROW  row;
6982   MYSQL_FIELD *field;
6983   char       *result_table, *opt_quoted_table;
6984   char       table_buff[NAME_LEN*2+3];
6985   char       table_buff2[NAME_LEN*2+3];
6986   char       query[QUERY_LENGTH];
6987   FILE       *sql_file= md_result_file;
6988   my_bool    freemem= FALSE;
6989   char const *text;
6990   DBUG_ENTER("get_view_structure");
6991 
6992   if (opt_no_create_info) /* Don't write table creation info */
6993     DBUG_RETURN(0);
6994 
6995   verbose_msg("-- Retrieving view structure for table %s...\n", table);
6996 
6997   result_table=     quote_name(table, table_buff, 1);
6998   opt_quoted_table= quote_name(table, table_buff2, 0);
6999 
7000   if (switch_character_set_results(mysql, "binary"))
7001     DBUG_RETURN(1);
7002 
7003   my_snprintf(query, sizeof(query), "SHOW CREATE TABLE %s", result_table);
7004 
7005   if (mysql_query_with_error_report(mysql, &table_res, query))
7006   {
7007     switch_character_set_results(mysql, default_charset);
7008     DBUG_RETURN(0);
7009   }
7010 
7011   /* Check if this is a view */
7012   field= mysql_fetch_field_direct(table_res, 0);
7013   if (strcmp(field->name, "View") != 0)
7014   {
7015     switch_character_set_results(mysql, default_charset);
7016     verbose_msg("-- It's base table, skipped\n");
7017     mysql_free_result(table_res);
7018     DBUG_RETURN(0);
7019   }
7020 
7021   /* If requested, open separate .sql file for this view */
7022   if (path)
7023   {
7024     if (!(sql_file= open_sql_file_for_table(table, O_WRONLY)))
7025     {
7026       mysql_free_result(table_res);
7027       DBUG_RETURN(1);
7028     }
7029 
7030     write_header(sql_file, db);
7031   }
7032 
7033   text= fix_identifier_with_newline(result_table, &freemem);
7034   print_comment(sql_file, 0,
7035                 "\n--\n-- Final view structure for view %s\n--\n\n", text);
7036   if (freemem)
7037     my_free((void*)text);
7038 
7039   verbose_msg("-- Dropping the temporary view structure created\n");
7040   fprintf(sql_file, "/*!50001 DROP VIEW IF EXISTS %s*/;\n", opt_quoted_table);
7041 
7042   my_snprintf(query, sizeof(query),
7043               "SELECT CHECK_OPTION, DEFINER, SECURITY_TYPE, "
7044               "       CHARACTER_SET_CLIENT, COLLATION_CONNECTION "
7045               "FROM information_schema.views "
7046               "WHERE table_name=\"%s\" AND table_schema=\"%s\"", table, db);
7047 
7048   if (mysql_query(mysql, query))
7049   {
7050     /*
7051       Use the raw output from SHOW CREATE TABLE if
7052        information_schema query fails.
7053      */
7054     row= mysql_fetch_row(table_res);
7055     fprintf(sql_file, "/*!50001 %s */;\n", row[1]);
7056     check_io(sql_file);
7057     mysql_free_result(table_res);
7058   }
7059   else
7060   {
7061     char *ptr;
7062     ulong *lengths;
7063     char search_buf[256], replace_buf[256];
7064     ulong search_len, replace_len;
7065     DYNAMIC_STRING ds_view;
7066 
7067     /* Save the result of SHOW CREATE TABLE in ds_view */
7068     row= mysql_fetch_row(table_res);
7069     lengths= mysql_fetch_lengths(table_res);
7070     init_dynamic_string_checked(&ds_view, row[1], lengths[1] + 1, 1024);
7071     mysql_free_result(table_res);
7072 
7073     /* Get the result from "select ... information_schema" */
7074     if (!(table_res= mysql_store_result(mysql)) ||
7075         !(row= mysql_fetch_row(table_res)))
7076     {
7077       if (table_res)
7078         mysql_free_result(table_res);
7079       dynstr_free(&ds_view);
7080       DB_error(mysql, "when trying to save the result of SHOW CREATE TABLE in ds_view.");
7081       DBUG_RETURN(1);
7082     }
7083 
7084     lengths= mysql_fetch_lengths(table_res);
7085 
7086     /*
7087       "WITH %s CHECK OPTION" is available from 5.0.2
7088       Surround it with !50002 comments
7089     */
7090     if (strcmp(row[0], "NONE"))
7091     {
7092 
7093       ptr= search_buf;
7094       search_len= (ulong)(strxmov(ptr, "WITH ", row[0],
7095                                   " CHECK OPTION", NullS) - ptr);
7096       ptr= replace_buf;
7097       replace_len=(ulong)(strxmov(ptr, "*/\n/*!50002 WITH ", row[0],
7098                                   " CHECK OPTION", NullS) - ptr);
7099       replace(&ds_view, search_buf, search_len, replace_buf, replace_len);
7100     }
7101 
7102     /*
7103       "DEFINER=%s SQL SECURITY %s" is available from 5.0.13
7104       Surround it with !50013 comments
7105     */
7106     {
7107       size_t     user_name_len;
7108       char       user_name_str[USERNAME_LENGTH + 1];
7109       char       quoted_user_name_str[USERNAME_LENGTH * 2 + 3];
7110       size_t     host_name_len;
7111       char       host_name_str[HOSTNAME_LENGTH + 1];
7112       char       quoted_host_name_str[HOSTNAME_LENGTH * 2 + 3];
7113 
7114       parse_user(row[1], lengths[1], user_name_str, &user_name_len,
7115                  host_name_str, &host_name_len);
7116 
7117       ptr= search_buf;
7118       search_len=
7119         (ulong)(strxmov(ptr, "DEFINER=",
7120                         quote_name(user_name_str, quoted_user_name_str, FALSE),
7121                         "@",
7122                         quote_name(host_name_str, quoted_host_name_str, FALSE),
7123                         " SQL SECURITY ", row[2], NullS) - ptr);
7124       ptr= replace_buf;
7125       replace_len=
7126         (ulong)(strxmov(ptr, "*/\n/*!50013 DEFINER=",
7127                         quote_name(user_name_str, quoted_user_name_str, FALSE),
7128                         "@",
7129                         quote_name(host_name_str, quoted_host_name_str, FALSE),
7130                         " SQL SECURITY ", row[2],
7131                         " */\n/*!50001", NullS) - ptr);
7132       replace(&ds_view, search_buf, search_len, replace_buf, replace_len);
7133     }
7134 
7135     /* Dump view structure to file */
7136 
7137     fprintf(sql_file,
7138             "/*!50001 SET @saved_cs_client          = @@character_set_client */;\n"
7139             "/*!50001 SET @saved_cs_results         = @@character_set_results */;\n"
7140             "/*!50001 SET @saved_col_connection     = @@collation_connection */;\n"
7141             "/*!50001 SET character_set_client      = %s */;\n"
7142             "/*!50001 SET character_set_results     = %s */;\n"
7143             "/*!50001 SET collation_connection      = %s */;\n"
7144             "/*!50001 %s */;\n"
7145             "/*!50001 SET character_set_client      = @saved_cs_client */;\n"
7146             "/*!50001 SET character_set_results     = @saved_cs_results */;\n"
7147             "/*!50001 SET collation_connection      = @saved_col_connection */;\n",
7148             (const char *) row[3],
7149             (const char *) row[3],
7150             (const char *) row[4],
7151             (const char *) ds_view.str);
7152 
7153     check_io(sql_file);
7154     mysql_free_result(table_res);
7155     dynstr_free(&ds_view);
7156   }
7157 
7158   if (switch_character_set_results(mysql, default_charset))
7159     DBUG_RETURN(1);
7160 
7161   /* If a separate .sql file was opened, close it now */
7162   if (sql_file != md_result_file)
7163   {
7164     fputs("\n", sql_file);
7165     write_footer(sql_file);
7166     my_fclose(sql_file, MYF(MY_WME));
7167   }
7168   DBUG_RETURN(0);
7169 }
7170 
7171 /*
7172   The following functions are wrappers for the dynamic string functions
7173   and if they fail, the wrappers will terminate the current process.
7174 */
7175 
7176 #define DYNAMIC_STR_ERROR_MSG "Couldn't perform DYNAMIC_STRING operation"
7177 
init_dynamic_string_checked(DYNAMIC_STRING * str,const char * init_str,size_t init_alloc,size_t alloc_increment)7178 static void init_dynamic_string_checked(DYNAMIC_STRING *str, const char *init_str,
7179 			    size_t init_alloc, size_t alloc_increment)
7180 {
7181   if (init_dynamic_string(str, init_str, init_alloc, alloc_increment))
7182     die(EX_MYSQLERR, DYNAMIC_STR_ERROR_MSG);
7183 }
7184 
dynstr_append_checked(DYNAMIC_STRING * dest,const char * src)7185 static void dynstr_append_checked(DYNAMIC_STRING* dest, const char* src)
7186 {
7187   if (dynstr_append(dest, src))
7188     die(EX_MYSQLERR, DYNAMIC_STR_ERROR_MSG);
7189 }
7190 
dynstr_set_checked(DYNAMIC_STRING * str,const char * init_str)7191 static void dynstr_set_checked(DYNAMIC_STRING *str, const char *init_str)
7192 {
7193   if (dynstr_set(str, init_str))
7194     die(EX_MYSQLERR, DYNAMIC_STR_ERROR_MSG);
7195 }
7196 
dynstr_append_mem_checked(DYNAMIC_STRING * str,const char * append,size_t length)7197 static void dynstr_append_mem_checked(DYNAMIC_STRING *str, const char *append,
7198 			  size_t length)
7199 {
7200   if (dynstr_append_mem(str, append, length))
7201     die(EX_MYSQLERR, DYNAMIC_STR_ERROR_MSG);
7202 }
7203 
dynstr_realloc_checked(DYNAMIC_STRING * str,size_t additional_size)7204 static void dynstr_realloc_checked(DYNAMIC_STRING *str, size_t additional_size)
7205 {
7206   if (dynstr_realloc(str, additional_size))
7207     die(EX_MYSQLERR, DYNAMIC_STR_ERROR_MSG);
7208 }
7209 
has_session_variables_like(MYSQL * mysql_con,const char * var_name)7210 static my_bool has_session_variables_like(MYSQL *mysql_con, const char *var_name)
7211 {
7212   MYSQL_RES  *res;
7213   MYSQL_ROW  row;
7214   char       *val= 0;
7215   char       buf[32], query[256];
7216   my_bool    has_var= FALSE;
7217   my_bool    has_table= FALSE;
7218 
7219   my_snprintf(query, sizeof(query), "SELECT COUNT(*) FROM"
7220               " INFORMATION_SCHEMA.TABLES WHERE table_schema ="
7221               " 'performance_schema' AND table_name = 'session_variables'");
7222   if (mysql_query_with_error_report(mysql_con, &res, query))
7223     return FALSE;
7224 
7225   row = mysql_fetch_row(res);
7226   val = row ? (char*)row[0] : NULL;
7227   has_table = val && strcmp(val, "0") != 0;
7228   mysql_free_result(res);
7229 
7230   if (has_table)
7231   {
7232     my_snprintf(query, sizeof(query), "SELECT COUNT(*) FROM"
7233                 " performance_schema.session_variables WHERE VARIABLE_NAME LIKE"
7234                 " %s", quote_for_like(var_name, buf));
7235     if (mysql_query_with_error_report(mysql_con, &res, query))
7236       return FALSE;
7237 
7238     row = mysql_fetch_row(res);
7239     val = row ? (char*)row[0] : NULL;
7240     has_var = val && strcmp(val, "0") != 0;
7241     mysql_free_result(res);
7242   }
7243 
7244   return has_var;
7245 }
7246 
7247 /**
7248    Check if the server supports LOCK TABLES FOR BACKUP.
7249 
7250    @returns  TRUE if there is support, FALSE otherwise.
7251 */
7252 
server_supports_backup_locks(void)7253 static my_bool server_supports_backup_locks(void)
7254 {
7255   MYSQL_RES *res;
7256   MYSQL_ROW row;
7257   my_bool rc;
7258 
7259   if (mysql_query_with_error_report(mysql, &res,
7260                                     "SHOW VARIABLES LIKE 'have_backup_locks'"))
7261     return FALSE;
7262 
7263   if ((row= mysql_fetch_row(res)) == NULL)
7264   {
7265     mysql_free_result(res);
7266     return FALSE;
7267   }
7268 
7269   rc= mysql_num_fields(res) > 1 && !strcmp(row[1], "YES");
7270 
7271   mysql_free_result(res);
7272 
7273   return rc;
7274 }
7275 
7276 /*
7277  This function executes all sql statements from the given file.
7278  Each statement lenght is limited to 1023 characters including
7279  trailing semicolon.
7280  Each statement has to be in its own line.
7281 
7282   @param[in]   sql_file      File name containing sql statements to be
7283                              executed.
7284   @retval  1 failure
7285            0 success
7286 */
7287 #ifndef NDEBUG
7288 #define SQL_STATEMENT_MAX_LEN 1024  // 1023 chars for statement + trailing 0
execute_sql_file(const char * sql_file)7289 static int execute_sql_file(const char *sql_file)
7290 {
7291   static const char *win_eol= "\r\n";
7292   static const char *linux_eol= "\n";
7293   static const char *win_semicolon= ";\r\n";
7294   static const char *linux_semicolon= ";\n";
7295   static const char *semicolon= ";";
7296   static const char *comment= "#";
7297 
7298   FILE *file;
7299   char  buf[SQL_STATEMENT_MAX_LEN];
7300 
7301   if (!sql_file)
7302     return 0;
7303 
7304   if (!(file= fopen(sql_file, "r")))
7305   {
7306     fprintf(stderr, "Cannot open file %s\n", sql_file);
7307     return 1;
7308   }
7309 
7310   while (fgets(buf, SQL_STATEMENT_MAX_LEN, file))
7311   {
7312     // simple validation
7313     size_t query_len= strlen(buf);
7314 
7315     // empty file
7316     if (query_len == 0)
7317     {
7318       fclose(file);
7319       return 1;
7320     }
7321 
7322     // If this is empty or comment line, skip it
7323     if (strcmp(buf, linux_eol) == 0 || strcmp(buf, win_eol) == 0 ||
7324         strstr(buf, comment) == buf)
7325     {
7326       continue;
7327     }
7328 
7329     // we need line ending with semicolon and optionally a new line
7330     // which differs on Windows and Linux
7331     if (strstr(buf, linux_semicolon) == 0 &&
7332         strstr(buf, win_semicolon) == 0 && strstr(buf, semicolon) == 0)
7333     {
7334       fclose(file);
7335       return 1;
7336     }
7337 
7338     if (mysql_query_with_error_report(mysql, 0, buf))
7339     {
7340       fclose(file);
7341       return 1;
7342     }
7343   }
7344 
7345   fclose(file);
7346   return 0;
7347 }
7348 #endif  // NDEBUG
7349 
main(int argc,char ** argv)7350 int main(int argc, char **argv)
7351 {
7352   char bin_log_name[FN_REFLEN];
7353   int exit_code, md_result_fd;
7354   int has_consistent_binlog_pos= 0;
7355   int has_consistent_gtid_executed= 0;
7356   my_bool ftwrl_done= FALSE;
7357 
7358   MY_INIT("mysqldump");
7359 
7360   compatible_mode_normal_str[0]= 0;
7361   default_charset= (char *)mysql_universal_client_charset;
7362   memset(&ignore_table, 0, sizeof(ignore_table));
7363 
7364   exit_code= get_options(&argc, &argv);
7365   if (exit_code)
7366   {
7367     free_resources();
7368     exit(exit_code);
7369   }
7370 
7371   /*
7372     Disable comments in xml mode if 'comments' option is not explicitly used.
7373   */
7374   if (opt_xml && !opt_comments_used)
7375     opt_comments= 0;
7376 
7377   if (log_error_file)
7378   {
7379     if(!(stderror_file= freopen(log_error_file, "a+", stderr)))
7380     {
7381       free_resources();
7382       exit(EX_MYSQLERR);
7383     }
7384   }
7385 
7386   if (connect_to_db(current_host, current_user, opt_password))
7387   {
7388     free_resources();
7389     exit(EX_MYSQLERR);
7390   }
7391 
7392 #ifndef NDEBUG
7393   if (execute_sql_file(start_sql_file))
7394     goto err;
7395 #endif
7396 
7397   if (!path)
7398     write_header(md_result_file, *argv);
7399 
7400   if (opt_lock_for_backup && !server_supports_backup_locks())
7401   {
7402     fprintf(stderr, "%s: Error: --lock-for-backup was specified with "
7403             "--single-transaction, but the server does not support "
7404             "LOCK TABLES FOR BACKUP.\n",
7405             my_progname);
7406     goto err;
7407   }
7408 
7409   if (opt_slave_data && do_stop_slave_sql(mysql))
7410     goto err;
7411 
7412   if (opt_single_transaction && opt_master_data)
7413   {
7414     /*
7415        See if we can avoid FLUSH TABLES WITH READ LOCK with Binlog_snapshot_*
7416        variables.
7417     */
7418     has_consistent_binlog_pos= check_consistent_binlog_pos(NULL, NULL);
7419   }
7420 
7421   has_consistent_gtid_executed= consistent_gtid_executed_supported(mysql);
7422 
7423   /*
7424    NOTE:
7425    1. --lock-all-tables and --single-transaction are mutually exclusive
7426    2. has_consistent_binlog_pos == true => opt_single_transaction == true
7427    3. --master-data => implicitly adds --lock-all-tables
7428    4. --master-data + --single-transaction => does not add implicit --lock-all-tables
7429 
7430    We require FTWRL if any of the following:
7431    1. explicitly requested by --lock-all-tables
7432    2. --master-data requested, but server does not provide consistent
7433       binlog position (or does not support consistent gtid_executed)
7434       Having consistent gtid_executed condition is here to be compatible with upstream
7435       behavior on servers which support consistent binlog position, but do not
7436       support consistent gtid_executed. In such case we need FTWRL.
7437    3. --single-transaction and --flush-logs
7438    */
7439   if (opt_lock_all_tables ||
7440       (opt_master_data &&
7441        (!has_consistent_binlog_pos || !has_consistent_gtid_executed)) ||
7442       (opt_single_transaction && flush_logs))
7443   {
7444     if (do_flush_tables_read_lock(mysql))
7445       goto err;
7446     ftwrl_done = TRUE;
7447   }
7448   else if (opt_lock_for_backup && do_lock_tables_for_backup(mysql))
7449     goto err;
7450 
7451   /*
7452     Flush logs before starting transaction since
7453     this causes implicit commit starting mysql-5.5.
7454   */
7455   if (opt_lock_all_tables || opt_master_data ||
7456       (opt_single_transaction && flush_logs) ||
7457       opt_delete_master_logs)
7458   {
7459     if (flush_logs || opt_delete_master_logs)
7460     {
7461       if (mysql_refresh(mysql, REFRESH_LOG))
7462       {
7463         DB_error(mysql, "when doing refresh");
7464         goto err;
7465       }
7466       verbose_msg("-- main : logs flushed successfully!\n");
7467     }
7468 
7469     /* Not anymore! That would not be sensible. */
7470     flush_logs= 0;
7471   }
7472 
7473   if (opt_delete_master_logs)
7474   {
7475     if (get_bin_log_name(mysql, bin_log_name, sizeof(bin_log_name)))
7476       goto err;
7477   }
7478 
7479   if (has_session_variables_like(mysql, "rocksdb_skip_fill_cache"))
7480     mysql_query_with_error_report(mysql, 0,
7481                                   "SET SESSION rocksdb_skip_fill_cache=1");
7482 
7483   if (opt_single_transaction && start_transaction(mysql))
7484     goto err;
7485 
7486   /* Add 'STOP SLAVE to beginning of dump */
7487   if (opt_slave_apply && add_stop_slave())
7488     goto err;
7489 
7490   /* Process opt_set_gtid_purged and add SET disable binlog if required. */
7491   if (process_set_gtid_purged(mysql, ftwrl_done, FALSE))
7492     goto err;
7493 
7494 
7495   if (opt_master_data && do_show_master_status(mysql, has_consistent_binlog_pos))
7496     goto err;
7497   if (opt_slave_data && do_show_slave_status(mysql))
7498     goto err;
7499 
7500   /**
7501     Note:
7502     opt_single_transaction == true => opt_lock_all_tables == false and vice versa
7503 
7504     We acquired the lock (FTWRL or LTFB) if
7505     1. --lock-all-tables => FTWRL + opt_single_transaction == false
7506     2. --lock-for-backup => LTFB + opt_single_transaction == true
7507     3. --master-data => (--lock-all-tables) FTWRL + opt_single_transaction == false
7508     4. --master-data + --single-transaction =>
7509          FTWRL if consistent snapshot not supported + opt_single_transaction == true
7510     5. --single-transaction + --flush-logs =>  FTWRL + opt_single_transaction == true
7511 
7512     We have to unlock in cases: 4, 5
7513 
7514     We need to keep the lock up to the end of backup if:
7515     1. we have --lock-for-backup
7516     2. we have --lock-all-tables (so opt_single_transaction == false)
7517     We unlock if none of above.
7518 
7519     If not locked previously, unlocking will not do any harm.
7520    */
7521   if (!(opt_lock_all_tables || opt_lock_for_backup))
7522   {
7523     if (do_unlock_tables(mysql)) /* unlock but no commit! */
7524       goto err;
7525   }
7526 
7527   if (opt_alltspcs)
7528     dump_all_tablespaces();
7529 
7530   if (opt_alldbs)
7531   {
7532     if (!opt_alltspcs && !opt_notspcs)
7533       dump_all_tablespaces();
7534     dump_all_databases();
7535   }
7536   else
7537   {
7538     // Check all arguments meet length condition. Currently database and table
7539     // names are limited to NAME_LEN bytes and stack-based buffers assumes
7540     // that escaped name will be not longer than NAME_LEN*2 + 2 bytes long.
7541     int argument;
7542     for (argument= 0; argument < argc; argument++)
7543     {
7544       size_t argument_length= strlen(argv[argument]);
7545       if (argument_length > NAME_LEN)
7546       {
7547         die(EX_CONSCHECK, "[ERROR] Argument '%s' is too long, it cannot be "
7548           "name for any table or database.\n", argv[argument]);
7549       }
7550     }
7551 
7552     if (argc > 1 && !opt_databases)
7553     {
7554       /* Only one database and selected table(s) */
7555       if (!opt_alltspcs && !opt_notspcs)
7556         dump_tablespaces_for_tables(*argv, (argv + 1), (argc - 1));
7557       dump_selected_tables(*argv, (argv + 1), (argc - 1));
7558     }
7559     else
7560     {
7561       /* One or more databases, all tables */
7562       if (!opt_alltspcs && !opt_notspcs)
7563         dump_tablespaces_for_databases(argv);
7564       dump_databases(argv);
7565     }
7566   }
7567 
7568   /* if --dump-slave , start the slave sql thread */
7569   if (opt_slave_data && do_start_slave_sql(mysql))
7570     goto err;
7571 
7572   /* Process opt_set_gtid_purged and add SET @@GLOBAL.GTID_PURGED if required. */
7573    if (process_set_gtid_purged(mysql, ftwrl_done, TRUE))
7574      goto err;
7575 
7576   /* add 'START SLAVE' to end of dump */
7577   if (opt_slave_apply && add_slave_statements())
7578     goto err;
7579 
7580   if (md_result_file)
7581     md_result_fd= my_fileno(md_result_file);
7582 
7583   /*
7584      Ensure dumped data flushed.
7585      First we will flush the file stream data to kernel buffers with fflush().
7586      Second we will flush the kernel buffers data to physical disk file with
7587      my_sync(), this will make sure the data succeessfully dumped to disk file.
7588      fsync() fails with EINVAL if stdout is not redirected to any file, hence
7589      MY_IGNORE_BADFD is passed to ingnore that error.
7590   */
7591   if (md_result_file &&
7592       (fflush(md_result_file) || my_sync(md_result_fd, MYF(MY_IGNORE_BADFD))))
7593   {
7594     if (!first_error)
7595       first_error= EX_MYSQLERR;
7596     goto err;
7597   }
7598   /* everything successful, purge the old logs files */
7599   if (opt_delete_master_logs && purge_bin_logs_to(mysql, bin_log_name))
7600     goto err;
7601 
7602 #if defined (_WIN32) && !defined (EMBEDDED_LIBRARY)
7603   my_free(shared_memory_base_name);
7604 #endif
7605   /*
7606     No reason to explicitely COMMIT the transaction, neither to explicitely
7607     UNLOCK TABLES: these will be automatically be done by the server when we
7608     disconnect now. Saves some code here, some network trips, adds nothing to
7609     server.
7610   */
7611 err:
7612 #ifndef NDEBUG
7613   execute_sql_file(finish_sql_file);
7614 #endif
7615 
7616   dbDisconnect(current_host);
7617   if (!path)
7618     write_footer(md_result_file);
7619   free_resources();
7620 
7621   if (stderror_file)
7622     fclose(stderror_file);
7623 
7624   return(first_error);
7625 } /* main */
7626