1 /*
2 Copyright (c) 2001, 2017, Oracle and/or its affiliates. All rights reserved.
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 as published by
6 the Free Software Foundation; version 2 of the License.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16 */
17
18 #define CHECK_VERSION "2.5.1"
19
20 #include "client_priv.h"
21 #include <m_ctype.h>
22 #include <mysql_version.h>
23 #include <mysqld_error.h>
24 #include <sslopt-vars.h>
25 #include <welcome_copyright_notice.h> /* ORACLE_WELCOME_COPYRIGHT_NOTICE */
26
27 /* Exit codes */
28
29 #define EX_USAGE 1
30 #define EX_MYSQLERR 2
31
32 /* ALTER instead of repair. */
33 #define MAX_ALTER_STR_SIZE 128 * 1024
34 #define KEY_PARTITIONING_CHANGED_STR "KEY () partitioning changed"
35
36 static MYSQL mysql_connection, *sock = 0;
37 static my_bool opt_alldbs = 0, opt_check_only_changed = 0, opt_extended = 0,
38 opt_compress = 0, opt_databases = 0, opt_fast = 0,
39 opt_medium_check = 0, opt_quick = 0, opt_all_in_1 = 0,
40 opt_silent = 0, opt_auto_repair = 0, ignore_errors = 0,
41 tty_password= 0, opt_frm= 0, debug_info_flag= 0, debug_check_flag= 0,
42 opt_fix_table_names= 0, opt_fix_db_names= 0, opt_upgrade= 0,
43 opt_write_binlog= 1;
44 static uint verbose = 0, opt_mysql_port=0;
45 static uint opt_enable_cleartext_plugin= 0;
46 static my_bool using_opt_enable_cleartext_plugin= 0;
47 static int my_end_arg;
48 static char * opt_mysql_unix_port = 0;
49 static char *opt_password = 0, *current_user = 0,
50 *default_charset= 0, *current_host= 0;
51 static char *opt_plugin_dir= 0, *opt_default_auth= 0;
52 static int first_error = 0;
53 DYNAMIC_ARRAY tables4repair, tables4rebuild, alter_table_cmds;
54 #ifdef HAVE_SMEM
55 static char *shared_memory_base_name=0;
56 #endif
57 static uint opt_protocol=0;
58
59 enum operations { DO_CHECK=1, DO_REPAIR, DO_ANALYZE, DO_OPTIMIZE, DO_UPGRADE };
60
61 static struct my_option my_long_options[] =
62 {
63 {"all-databases", 'A',
64 "Check all the databases. This is the same as --databases with all databases selected.",
65 &opt_alldbs, &opt_alldbs, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
66 0, 0},
67 {"analyze", 'a', "Analyze given tables.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0,
68 0, 0, 0, 0},
69 {"all-in-1", '1',
70 "Instead of issuing one query for each table, use one query per database, naming all tables in the database in a comma-separated list.",
71 &opt_all_in_1, &opt_all_in_1, 0, GET_BOOL, NO_ARG, 0, 0, 0,
72 0, 0, 0},
73 {"auto-repair", OPT_AUTO_REPAIR,
74 "If a checked table is corrupted, automatically fix it. Repairing will be done after all tables have been checked, if corrupted ones were found.",
75 &opt_auto_repair, &opt_auto_repair, 0, GET_BOOL, NO_ARG, 0,
76 0, 0, 0, 0, 0},
77 {"character-sets-dir", OPT_CHARSETS_DIR,
78 "Directory for character set files.", &charsets_dir,
79 &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
80 {"check", 'c', "Check table for errors.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0,
81 0, 0, 0, 0},
82 {"check-only-changed", 'C',
83 "Check only tables that have changed since last check or haven't been closed properly.",
84 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
85 {"check-upgrade", 'g',
86 "Check tables for version-dependent changes. May be used with --auto-repair to correct tables requiring version-dependent updates.",
87 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
88 {"compress", OPT_COMPRESS, "Use compression in server/client protocol.",
89 &opt_compress, &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0,
90 0, 0, 0},
91 {"databases", 'B',
92 "Check several databases. Note the difference in usage; in this case no tables are given. All name arguments are regarded as database names.",
93 &opt_databases, &opt_databases, 0, GET_BOOL, NO_ARG,
94 0, 0, 0, 0, 0, 0},
95 #ifdef DBUG_OFF
96 {"debug", '#', "This is a non-debug version. Catch this and exit.",
97 0, 0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0},
98 #else
99 {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.",
100 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
101 #endif
102 {"debug-check", OPT_DEBUG_CHECK, "Check memory and open file usage at exit.",
103 &debug_check_flag, &debug_check_flag, 0,
104 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
105 {"debug-info", OPT_DEBUG_INFO, "Print some debug info at exit.",
106 &debug_info_flag, &debug_info_flag,
107 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
108 {"default-character-set", OPT_DEFAULT_CHARSET,
109 "Set the default character set.", &default_charset,
110 &default_charset, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
111 {"default_auth", OPT_DEFAULT_AUTH,
112 "Default authentication client-side plugin to use.",
113 &opt_default_auth, &opt_default_auth, 0,
114 GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
115 {"enable_cleartext_plugin", OPT_ENABLE_CLEARTEXT_PLUGIN,
116 "Enable/disable the clear text authentication plugin.",
117 &opt_enable_cleartext_plugin, &opt_enable_cleartext_plugin,
118 0, GET_BOOL, OPT_ARG, 0, 0, 0, 0, 0, 0},
119 {"fast",'F', "Check only tables that haven't been closed properly.",
120 &opt_fast, &opt_fast, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0,
121 0},
122 {"fix-db-names", OPT_FIX_DB_NAMES, "Fix database names.",
123 &opt_fix_db_names, &opt_fix_db_names,
124 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
125 {"fix-table-names", OPT_FIX_TABLE_NAMES, "Fix table names.",
126 &opt_fix_table_names, &opt_fix_table_names,
127 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
128 {"force", 'f', "Continue even if we get an SQL error.",
129 &ignore_errors, &ignore_errors, 0, GET_BOOL, NO_ARG, 0, 0,
130 0, 0, 0, 0},
131 {"extended", 'e',
132 "If you are using this option with CHECK TABLE, it will ensure that the table is 100 percent consistent, but will take a long time. If you are using this option with REPAIR TABLE, it will force using old slow repair with keycache method, instead of much faster repair by sorting.",
133 &opt_extended, &opt_extended, 0, GET_BOOL, NO_ARG, 0, 0, 0,
134 0, 0, 0},
135 {"help", '?', "Display this help message and exit.", 0, 0, 0, GET_NO_ARG,
136 NO_ARG, 0, 0, 0, 0, 0, 0},
137 {"host",'h', "Connect to host.", ¤t_host,
138 ¤t_host, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
139 {"medium-check", 'm',
140 "Faster than extended-check, but only finds 99.99 percent of all errors. Should be good enough for most cases.",
141 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
142 {"write-binlog", OPT_WRITE_BINLOG,
143 "Log ANALYZE, OPTIMIZE and REPAIR TABLE commands. Use --skip-write-binlog "
144 "when commands should not be sent to replication slaves.",
145 &opt_write_binlog, &opt_write_binlog, 0, GET_BOOL, NO_ARG,
146 1, 0, 0, 0, 0, 0},
147 {"optimize", 'o', "Optimize table.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0,
148 0, 0},
149 {"password", 'p',
150 "Password to use when connecting to server. If password is not given, it's solicited on the tty.",
151 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
152 #ifdef __WIN__
153 {"pipe", 'W', "Use named pipes to connect to server.", 0, 0, 0, GET_NO_ARG,
154 NO_ARG, 0, 0, 0, 0, 0, 0},
155 #endif
156 {"plugin_dir", OPT_PLUGIN_DIR, "Directory for client-side plugins.",
157 &opt_plugin_dir, &opt_plugin_dir, 0,
158 GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
159 {"port", 'P', "Port number to use for connection or 0 for default to, in "
160 "order of preference, my.cnf, $MYSQL_TCP_PORT, "
161 #if MYSQL_PORT_DEFAULT == 0
162 "/etc/services, "
163 #endif
164 "built-in default (" STRINGIFY_ARG(MYSQL_PORT) ").",
165 &opt_mysql_port, &opt_mysql_port, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0,
166 0},
167 {"protocol", OPT_MYSQL_PROTOCOL, "The protocol to use for connection (tcp, socket, pipe, memory).",
168 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
169 {"quick", 'q',
170 "If you are using this option with CHECK TABLE, it prevents the check from scanning the rows to check for wrong links. This is the fastest check. If you are using this option with REPAIR TABLE, it will try to repair only the index tree. This is the fastest repair method for a table.",
171 &opt_quick, &opt_quick, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0,
172 0},
173 {"repair", 'r',
174 "Can fix almost anything except unique keys that aren't unique.",
175 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
176 #ifdef HAVE_SMEM
177 {"shared-memory-base-name", OPT_SHARED_MEMORY_BASE_NAME,
178 "Base name of shared memory.", &shared_memory_base_name, &shared_memory_base_name,
179 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
180 #endif
181 {"silent", 's', "Print only error messages.", &opt_silent,
182 &opt_silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
183 {"socket", 'S', "The socket file to use for connection.",
184 &opt_mysql_unix_port, &opt_mysql_unix_port, 0, GET_STR,
185 REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
186 #include <sslopt-longopts.h>
187 {"tables", OPT_TABLES, "Overrides option --databases (-B).", 0, 0, 0,
188 GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
189 {"use-frm", OPT_FRM,
190 "When used with REPAIR, get table structure from .frm file, so the table can be repaired even if .MYI header is corrupted.",
191 &opt_frm, &opt_frm, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0,
192 0},
193 #ifndef DONT_ALLOW_USER_CHANGE
194 {"user", 'u', "User for login if not current user.", ¤t_user,
195 ¤t_user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
196 #endif
197 {"verbose", 'v', "Print info about the various stages.", 0, 0, 0, GET_NO_ARG,
198 NO_ARG, 0, 0, 0, 0, 0, 0},
199 {"version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG,
200 NO_ARG, 0, 0, 0, 0, 0, 0},
201 {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
202 };
203
204 static const char *load_default_groups[] = { "mysqlcheck", "client", 0 };
205
206
207 static void print_version(void);
208 static void usage(void);
209 static int get_options(int *argc, char ***argv);
210 static int process_all_databases();
211 static int process_databases(char **db_names);
212 static int process_selected_tables(char *db, char **table_names, int tables);
213 static int process_all_tables_in_db(char *database);
214 static int process_one_db(char *database);
215 static int use_db(char *database);
216 static int handle_request_for_tables(char *tables, size_t length);
217 static int dbConnect(char *host, char *user,char *passwd);
218 static void dbDisconnect(char *host);
219 static void DBerror(MYSQL *mysql, const char *when);
220 static void safe_exit(int error);
221 static void print_result();
222 static size_t fixed_name_length(const char *name);
223 static char *fix_table_name(char *dest, char *src);
224 int what_to_do = 0;
225
226
print_version(void)227 static void print_version(void)
228 {
229 printf("%s Ver %s Distrib %s, for %s (%s)\n", my_progname, CHECK_VERSION,
230 MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE);
231 } /* print_version */
232
233
usage(void)234 static void usage(void)
235 {
236 print_version();
237 puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000"));
238 puts("This program can be used to CHECK (-c, -m, -C), REPAIR (-r), ANALYZE (-a),");
239 puts("or OPTIMIZE (-o) tables. Some of the options (like -e or -q) can be");
240 puts("used at the same time. Not all options are supported by all storage engines.");
241 puts("Please consult the MySQL manual for latest information about the");
242 puts("above. The options -c, -r, -a, and -o are exclusive to each other, which");
243 puts("means that the last option will be used, if several was specified.\n");
244 puts("The option -c will be used by default, if none was specified. You");
245 puts("can change the default behavior by making a symbolic link, or");
246 puts("copying this file somewhere with another name, the alternatives are:");
247 puts("mysqlrepair: The default option will be -r");
248 puts("mysqlanalyze: The default option will be -a");
249 puts("mysqloptimize: The default option will be -o\n");
250 printf("Usage: %s [OPTIONS] database [tables]\n", my_progname);
251 printf("OR %s [OPTIONS] --databases DB1 [DB2 DB3...]\n",
252 my_progname);
253 printf("OR %s [OPTIONS] --all-databases\n", my_progname);
254 print_defaults("my", load_default_groups);
255 my_print_help(my_long_options);
256 my_print_variables(my_long_options);
257 } /* usage */
258
259
260 static my_bool
get_one_option(int optid,const struct my_option * opt,char * argument)261 get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
262 char *argument)
263 {
264 int orig_what_to_do= what_to_do;
265
266 switch(optid) {
267 case 'a':
268 what_to_do = DO_ANALYZE;
269 break;
270 case 'c':
271 what_to_do = DO_CHECK;
272 break;
273 case 'C':
274 what_to_do = DO_CHECK;
275 opt_check_only_changed = 1;
276 break;
277 case 'I': /* Fall through */
278 case '?':
279 usage();
280 exit(0);
281 case 'm':
282 what_to_do = DO_CHECK;
283 opt_medium_check = 1;
284 break;
285 case 'o':
286 what_to_do = DO_OPTIMIZE;
287 break;
288 case OPT_FIX_DB_NAMES:
289 what_to_do= DO_UPGRADE;
290 opt_databases= 1;
291 break;
292 case OPT_FIX_TABLE_NAMES:
293 what_to_do= DO_UPGRADE;
294 break;
295 case 'p':
296 if (argument == disabled_my_option)
297 argument= (char*) ""; /* Don't require password */
298 if (argument)
299 {
300 char *start = argument;
301 my_free(opt_password);
302 opt_password = my_strdup(argument, MYF(MY_FAE));
303 while (*argument) *argument++= 'x'; /* Destroy argument */
304 if (*start)
305 start[1] = 0; /* Cut length of argument */
306 tty_password= 0;
307 }
308 else
309 tty_password = 1;
310 break;
311 case 'r':
312 what_to_do = DO_REPAIR;
313 break;
314 case 'g':
315 what_to_do= DO_CHECK;
316 opt_upgrade= 1;
317 break;
318 case 'W':
319 #ifdef __WIN__
320 opt_protocol = MYSQL_PROTOCOL_PIPE;
321 #endif
322 break;
323 case '#':
324 DBUG_PUSH(argument ? argument : "d:t:o");
325 debug_check_flag= 1;
326 break;
327 #include <sslopt-case.h>
328 case OPT_TABLES:
329 opt_databases = 0;
330 break;
331 case 'v':
332 verbose++;
333 break;
334 case 'V': print_version(); exit(0);
335 case OPT_ENABLE_CLEARTEXT_PLUGIN:
336 using_opt_enable_cleartext_plugin= TRUE;
337 break;
338 case OPT_MYSQL_PROTOCOL:
339 opt_protocol= find_type_or_exit(argument, &sql_protocol_typelib,
340 opt->name);
341 break;
342 }
343
344 if (orig_what_to_do && (what_to_do != orig_what_to_do))
345 {
346 fprintf(stderr, "Error: %s doesn't support multiple contradicting commands.\n",
347 my_progname);
348 return 1;
349 }
350 return 0;
351 }
352
353
get_options(int * argc,char *** argv)354 static int get_options(int *argc, char ***argv)
355 {
356 int ho_error;
357
358 if (*argc == 1)
359 {
360 usage();
361 exit(0);
362 }
363
364 if ((ho_error= load_defaults("my", load_default_groups, argc, argv)) ||
365 (ho_error=handle_options(argc, argv, my_long_options, get_one_option)))
366 exit(ho_error);
367
368 if (!what_to_do)
369 {
370 size_t pnlen= strlen(my_progname);
371
372 if (pnlen < 6) /* name too short */
373 what_to_do = DO_CHECK;
374 else if (!strcmp("repair", my_progname + pnlen - 6))
375 what_to_do = DO_REPAIR;
376 else if (!strcmp("analyze", my_progname + pnlen - 7))
377 what_to_do = DO_ANALYZE;
378 else if (!strcmp("optimize", my_progname + pnlen - 8))
379 what_to_do = DO_OPTIMIZE;
380 else
381 what_to_do = DO_CHECK;
382 }
383
384 /*
385 If there's no --default-character-set option given with
386 --fix-table-name or --fix-db-name set the default character set to "utf8".
387 */
388 if (!default_charset)
389 {
390 if (opt_fix_db_names || opt_fix_table_names)
391 default_charset= (char*) "utf8";
392 else
393 default_charset= (char*) MYSQL_AUTODETECT_CHARSET_NAME;
394 }
395 if (strcmp(default_charset, MYSQL_AUTODETECT_CHARSET_NAME) &&
396 !get_charset_by_csname(default_charset, MY_CS_PRIMARY, MYF(MY_WME)))
397 {
398 printf("Unsupported character set: %s\n", default_charset);
399 return 1;
400 }
401 if (*argc > 0 && opt_alldbs)
402 {
403 printf("You should give only options, no arguments at all, with option\n");
404 printf("--all-databases. Please see %s --help for more information.\n",
405 my_progname);
406 return 1;
407 }
408 if (*argc < 1 && !opt_alldbs)
409 {
410 printf("You forgot to give the arguments! Please see %s --help\n",
411 my_progname);
412 printf("for more information.\n");
413 return 1;
414 }
415 if (tty_password)
416 opt_password = get_tty_password(NullS);
417 if (debug_info_flag)
418 my_end_arg= MY_CHECK_ERROR | MY_GIVE_INFO;
419 if (debug_check_flag)
420 my_end_arg= MY_CHECK_ERROR;
421 return(0);
422 } /* get_options */
423
424
process_all_databases()425 static int process_all_databases()
426 {
427 MYSQL_ROW row;
428 MYSQL_RES *tableres;
429 int result = 0;
430
431 if (mysql_query(sock, "SHOW DATABASES") ||
432 !(tableres = mysql_store_result(sock)))
433 {
434 my_printf_error(0, "Error: Couldn't execute 'SHOW DATABASES': %s",
435 MYF(0), mysql_error(sock));
436 return 1;
437 }
438 while ((row = mysql_fetch_row(tableres)))
439 {
440 if (process_one_db(row[0]))
441 result = 1;
442 }
443 return result;
444 }
445 /* process_all_databases */
446
447
process_databases(char ** db_names)448 static int process_databases(char **db_names)
449 {
450 int result = 0;
451 for ( ; *db_names ; db_names++)
452 {
453 if (process_one_db(*db_names))
454 result = 1;
455 }
456 return result;
457 } /* process_databases */
458
459
process_selected_tables(char * db,char ** table_names,int tables)460 static int process_selected_tables(char *db, char **table_names, int tables)
461 {
462 if (use_db(db))
463 return 1;
464 if (opt_all_in_1 && what_to_do != DO_UPGRADE)
465 {
466 /*
467 We need table list in form `a`, `b`, `c`
468 that's why we need 2 more chars added to to each table name
469 space is for more readable output in logs and in case of error
470 */
471 char *table_names_comma_sep, *end;
472 size_t tot_length= 0;
473 int i= 0;
474
475 for (i = 0; i < tables; i++)
476 tot_length+= fixed_name_length(*(table_names + i)) + 2;
477
478 if (!(table_names_comma_sep = (char *)
479 my_malloc((sizeof(char) * tot_length) + 4, MYF(MY_WME))))
480 return 1;
481
482 for (end = table_names_comma_sep + 1; tables > 0;
483 tables--, table_names++)
484 {
485 end= fix_table_name(end, *table_names);
486 *end++= ',';
487 }
488 *--end = 0;
489 handle_request_for_tables(table_names_comma_sep + 1, tot_length - 1);
490 my_free(table_names_comma_sep);
491 }
492 else
493 for (; tables > 0; tables--, table_names++)
494 handle_request_for_tables(*table_names, fixed_name_length(*table_names));
495 return 0;
496 } /* process_selected_tables */
497
498
fixed_name_length(const char * name)499 static size_t fixed_name_length(const char *name)
500 {
501 const char *p;
502 size_t extra_length= 2; /* count the first/last backticks */
503
504 for (p= name; *p; p++)
505 {
506 if (*p == '`')
507 extra_length++;
508 else if (*p == '.')
509 extra_length+= 2;
510 }
511 return (size_t) ((p - name) + extra_length);
512 }
513
514
fix_table_name(char * dest,char * src)515 static char *fix_table_name(char *dest, char *src)
516 {
517 *dest++= '`';
518 for (; *src; src++)
519 {
520 switch (*src) {
521 case '.': /* add backticks around '.' */
522 *dest++= '`';
523 *dest++= '.';
524 *dest++= '`';
525 break;
526 case '`': /* escape backtick character */
527 *dest++= '`';
528 /* fall through */
529 default:
530 *dest++= *src;
531 }
532 }
533 *dest++= '`';
534 return dest;
535 }
536
537
process_all_tables_in_db(char * database)538 static int process_all_tables_in_db(char *database)
539 {
540 MYSQL_RES *res;
541 MYSQL_ROW row;
542 uint num_columns;
543
544 LINT_INIT(res);
545 if (use_db(database))
546 return 1;
547 if ((mysql_query(sock, "SHOW /*!50002 FULL*/ TABLES") &&
548 mysql_query(sock, "SHOW TABLES")) ||
549 !(res= mysql_store_result(sock)))
550 {
551 my_printf_error(0, "Error: Couldn't get table list for database %s: %s",
552 MYF(0), database, mysql_error(sock));
553 return 1;
554 }
555
556 num_columns= mysql_num_fields(res);
557
558 if (opt_all_in_1 && what_to_do != DO_UPGRADE)
559 {
560 /*
561 We need table list in form `a`, `b`, `c`
562 that's why we need 2 more chars added to to each table name
563 space is for more readable output in logs and in case of error
564 */
565
566 char *tables, *end;
567 size_t tot_length = 0;
568
569 while ((row = mysql_fetch_row(res)))
570 tot_length+= fixed_name_length(row[0]) + 2;
571 mysql_data_seek(res, 0);
572
573 if (!(tables=(char *) my_malloc(sizeof(char)*tot_length+4, MYF(MY_WME))))
574 {
575 mysql_free_result(res);
576 return 1;
577 }
578 for (end = tables + 1; (row = mysql_fetch_row(res)) ;)
579 {
580 if ((num_columns == 2) && (strcmp(row[1], "VIEW") == 0))
581 continue;
582
583 end= fix_table_name(end, row[0]);
584 *end++= ',';
585 }
586 *--end = 0;
587 if (tot_length)
588 handle_request_for_tables(tables + 1, tot_length - 1);
589 my_free(tables);
590 }
591 else
592 {
593 while ((row = mysql_fetch_row(res)))
594 {
595 /* Skip views if we don't perform renaming. */
596 if ((what_to_do != DO_UPGRADE) && (num_columns == 2) && (strcmp(row[1], "VIEW") == 0))
597 continue;
598
599 handle_request_for_tables(row[0], fixed_name_length(row[0]));
600 }
601 }
602 mysql_free_result(res);
603 return 0;
604 } /* process_all_tables_in_db */
605
606
run_query(const char * query)607 static int run_query(const char *query)
608 {
609 if (mysql_query(sock, query))
610 {
611 fprintf(stderr, "Failed to %s\n", query);
612 fprintf(stderr, "Error: %s\n", mysql_error(sock));
613 return 1;
614 }
615 return 0;
616 }
617
618
fix_table_storage_name(const char * name)619 static int fix_table_storage_name(const char *name)
620 {
621 char qbuf[100 + NAME_LEN*4];
622 int rc= 0;
623 if (strncmp(name, "#mysql50#", 9))
624 return 1;
625 my_snprintf(qbuf, sizeof(qbuf), "RENAME TABLE `%s` TO `%s`",
626 name, name + 9);
627
628 rc= run_query(qbuf);
629 if (verbose)
630 printf("%-50s %s\n", name, rc ? "FAILED" : "OK");
631 return rc;
632 }
633
fix_database_storage_name(const char * name)634 static int fix_database_storage_name(const char *name)
635 {
636 char qbuf[100 + NAME_LEN*4];
637 int rc= 0;
638 if (strncmp(name, "#mysql50#", 9))
639 return 1;
640 my_snprintf(qbuf, sizeof(qbuf), "ALTER DATABASE `%s` UPGRADE DATA DIRECTORY "
641 "NAME", name);
642 rc= run_query(qbuf);
643 if (verbose)
644 printf("%-50s %s\n", name, rc ? "FAILED" : "OK");
645 return rc;
646 }
647
rebuild_table(char * name)648 static int rebuild_table(char *name)
649 {
650 char *query, *ptr;
651 int rc= 0;
652 query= (char*)my_malloc(sizeof(char) * (12 + fixed_name_length(name) + 6 + 1),
653 MYF(MY_WME));
654 if (!query)
655 return 1;
656 ptr= strmov(query, "ALTER TABLE ");
657 ptr= fix_table_name(ptr, name);
658 ptr= strxmov(ptr, " FORCE", NullS);
659 if (mysql_real_query(sock, query, (ulong)(ptr - query)))
660 {
661 fprintf(stderr, "Failed to %s\n", query);
662 fprintf(stderr, "Error: %s\n", mysql_error(sock));
663 rc= 1;
664 }
665 my_free(query);
666 return rc;
667 }
668
process_one_db(char * database)669 static int process_one_db(char *database)
670 {
671 if (what_to_do == DO_UPGRADE)
672 {
673 int rc= 0;
674 if (opt_fix_db_names && !strncmp(database,"#mysql50#", 9))
675 {
676 rc= fix_database_storage_name(database);
677 database+= 9;
678 }
679 if (rc || !opt_fix_table_names)
680 return rc;
681 }
682 return process_all_tables_in_db(database);
683 }
684
685
use_db(char * database)686 static int use_db(char *database)
687 {
688 if (mysql_get_server_version(sock) >= FIRST_INFORMATION_SCHEMA_VERSION &&
689 !my_strcasecmp(&my_charset_latin1, database, INFORMATION_SCHEMA_DB_NAME))
690 return 1;
691 if (mysql_get_server_version(sock) >= FIRST_PERFORMANCE_SCHEMA_VERSION &&
692 !my_strcasecmp(&my_charset_latin1, database, PERFORMANCE_SCHEMA_DB_NAME))
693 return 1;
694 if (mysql_select_db(sock, database))
695 {
696 DBerror(sock, "when selecting the database");
697 return 1;
698 }
699 return 0;
700 } /* use_db */
701
disable_binlog()702 static int disable_binlog()
703 {
704 const char *stmt= "SET SQL_LOG_BIN=0";
705 return run_query(stmt);
706 }
707
handle_request_for_tables(char * tables,size_t length)708 static int handle_request_for_tables(char *tables, size_t length)
709 {
710 char *query, *end, options[100], message[100];
711 size_t query_length= 0, query_size= sizeof(char)*(length+110);
712 const char *op = 0;
713
714 options[0] = 0;
715 end = options;
716 switch (what_to_do) {
717 case DO_CHECK:
718 op = "CHECK";
719 if (opt_quick) end = strmov(end, " QUICK");
720 if (opt_fast) end = strmov(end, " FAST");
721 if (opt_medium_check) end = strmov(end, " MEDIUM"); /* Default */
722 if (opt_extended) end = strmov(end, " EXTENDED");
723 if (opt_check_only_changed) end = strmov(end, " CHANGED");
724 if (opt_upgrade) end = strmov(end, " FOR UPGRADE");
725 break;
726 case DO_REPAIR:
727 op= (opt_write_binlog) ? "REPAIR" : "REPAIR NO_WRITE_TO_BINLOG";
728 if (opt_quick) end = strmov(end, " QUICK");
729 if (opt_extended) end = strmov(end, " EXTENDED");
730 if (opt_frm) end = strmov(end, " USE_FRM");
731 break;
732 case DO_ANALYZE:
733 op= (opt_write_binlog) ? "ANALYZE" : "ANALYZE NO_WRITE_TO_BINLOG";
734 break;
735 case DO_OPTIMIZE:
736 op= (opt_write_binlog) ? "OPTIMIZE" : "OPTIMIZE NO_WRITE_TO_BINLOG";
737 break;
738 case DO_UPGRADE:
739 return fix_table_storage_name(tables);
740 }
741
742 if (!(query =(char *) my_malloc(query_size, MYF(MY_WME))))
743 {
744 return 1;
745 }
746 if (opt_all_in_1)
747 {
748 DBUG_ASSERT(strlen(op)+strlen(tables)+strlen(options)+8+1 <= query_size);
749
750 /* No backticks here as we added them before */
751 query_length= sprintf(query, "%s TABLE %s %s", op, tables, options);
752 }
753 else
754 {
755 char *ptr;
756
757 ptr= strmov(strmov(query, op), " TABLE ");
758 ptr= fix_table_name(ptr, tables);
759 ptr= strxmov(ptr, " ", options, NullS);
760 query_length= (size_t) (ptr - query);
761 }
762 if (mysql_real_query(sock, query, query_length))
763 {
764 sprintf(message, "when executing '%s TABLE ... %s'", op, options);
765 DBerror(sock, message);
766 return 1;
767 }
768 print_result();
769 my_free(query);
770 return 0;
771 }
772
773
print_result()774 static void print_result()
775 {
776 MYSQL_RES *res;
777 MYSQL_ROW row;
778 char prev[NAME_LEN*2+2];
779 char prev_alter[MAX_ALTER_STR_SIZE];
780 uint i;
781 my_bool found_error=0, table_rebuild=0;
782
783 res = mysql_use_result(sock);
784
785 prev[0] = '\0';
786 prev_alter[0]= 0;
787 for (i = 0; (row = mysql_fetch_row(res)); i++)
788 {
789 int changed = strcmp(prev, row[0]);
790 my_bool status = !strcmp(row[2], "status");
791
792 if (status)
793 {
794 /*
795 if there was an error with the table, we have --auto-repair set,
796 and this isn't a repair op, then add the table to the tables4repair
797 list
798 */
799 if (found_error && opt_auto_repair && what_to_do != DO_REPAIR &&
800 strcmp(row[3],"OK"))
801 {
802 if (table_rebuild)
803 {
804 if (prev_alter[0])
805 insert_dynamic(&alter_table_cmds, (uchar*) prev_alter);
806 else
807 insert_dynamic(&tables4rebuild, (uchar*) prev);
808 }
809 else
810 insert_dynamic(&tables4repair, (uchar*) prev);
811 }
812 found_error=0;
813 table_rebuild=0;
814 prev_alter[0]= 0;
815 if (opt_silent)
816 continue;
817 }
818 if (status && changed)
819 printf("%-50s %s", row[0], row[3]);
820 else if (!status && changed)
821 {
822 printf("%s\n%-9s: %s", row[0], row[2], row[3]);
823 if (opt_auto_repair && strcmp(row[2],"note"))
824 {
825 const char *alter_txt= strstr(row[3], "ALTER TABLE");
826 found_error=1;
827 if (alter_txt)
828 {
829 table_rebuild=1;
830 if (!strncmp(row[3], KEY_PARTITIONING_CHANGED_STR,
831 strlen(KEY_PARTITIONING_CHANGED_STR)) &&
832 strstr(alter_txt, "PARTITION BY"))
833 {
834 if (strlen(alter_txt) >= MAX_ALTER_STR_SIZE)
835 {
836 printf("Error: Alter command too long (>= %d),"
837 " please do \"%s\" or dump/reload to fix it!\n",
838 MAX_ALTER_STR_SIZE,
839 alter_txt);
840 table_rebuild= 0;
841 prev_alter[0]= 0;
842 }
843 else
844 {
845 strncpy(prev_alter, alter_txt, MAX_ALTER_STR_SIZE-1);
846 prev_alter[MAX_ALTER_STR_SIZE-1]= 0;
847 }
848 }
849 }
850 }
851 }
852 else
853 printf("%-9s: %s", row[2], row[3]);
854 strmov(prev, row[0]);
855 putchar('\n');
856 }
857 /* add the last table to be repaired to the list */
858 if (found_error && opt_auto_repair && what_to_do != DO_REPAIR)
859 {
860 if (table_rebuild)
861 {
862 if (prev_alter[0])
863 insert_dynamic(&alter_table_cmds, (uchar*) prev_alter);
864 else
865 insert_dynamic(&tables4rebuild, (uchar*) prev);
866 }
867 else
868 insert_dynamic(&tables4repair, (uchar*) prev);
869 }
870 mysql_free_result(res);
871 }
872
873
dbConnect(char * host,char * user,char * passwd)874 static int dbConnect(char *host, char *user, char *passwd)
875 {
876 DBUG_ENTER("dbConnect");
877 if (verbose)
878 {
879 fprintf(stderr, "# Connecting to %s...\n", host ? host : "localhost");
880 }
881 mysql_init(&mysql_connection);
882 if (opt_compress)
883 mysql_options(&mysql_connection, MYSQL_OPT_COMPRESS, NullS);
884 #ifdef HAVE_OPENSSL
885 if (opt_use_ssl)
886 mysql_ssl_set(&mysql_connection, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
887 opt_ssl_capath, opt_ssl_cipher);
888 #endif
889 if (opt_protocol)
890 mysql_options(&mysql_connection,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol);
891 #ifdef HAVE_SMEM
892 if (shared_memory_base_name)
893 mysql_options(&mysql_connection,MYSQL_SHARED_MEMORY_BASE_NAME,shared_memory_base_name);
894 #endif
895
896 if (opt_plugin_dir && *opt_plugin_dir)
897 mysql_options(&mysql_connection, MYSQL_PLUGIN_DIR, opt_plugin_dir);
898
899 if (opt_default_auth && *opt_default_auth)
900 mysql_options(&mysql_connection, MYSQL_DEFAULT_AUTH, opt_default_auth);
901
902 if (using_opt_enable_cleartext_plugin)
903 mysql_options(&mysql_connection, MYSQL_ENABLE_CLEARTEXT_PLUGIN,
904 (char *) &opt_enable_cleartext_plugin);
905
906 mysql_options(&mysql_connection, MYSQL_SET_CHARSET_NAME, default_charset);
907 if (!(sock = mysql_connect_ssl_check(&mysql_connection, host, user, passwd,
908 NULL, opt_mysql_port,
909 opt_mysql_unix_port, 0,
910 opt_ssl_mode == SSL_MODE_REQUIRED)))
911 {
912 DBerror(&mysql_connection, "when trying to connect");
913 return 1;
914 }
915 mysql_connection.reconnect= 1;
916 return 0;
917 } /* dbConnect */
918
919
dbDisconnect(char * host)920 static void dbDisconnect(char *host)
921 {
922 if (verbose)
923 fprintf(stderr, "# Disconnecting from %s...\n", host ? host : "localhost");
924 mysql_close(sock);
925 } /* dbDisconnect */
926
927
DBerror(MYSQL * mysql,const char * when)928 static void DBerror(MYSQL *mysql, const char *when)
929 {
930 DBUG_ENTER("DBerror");
931 my_printf_error(0,"Got error: %d: %s %s", MYF(0),
932 mysql_errno(mysql), mysql_error(mysql), when);
933 safe_exit(EX_MYSQLERR);
934 DBUG_VOID_RETURN;
935 } /* DBerror */
936
937
safe_exit(int error)938 static void safe_exit(int error)
939 {
940 if (!first_error)
941 first_error= error;
942 if (ignore_errors)
943 return;
944 if (sock)
945 mysql_close(sock);
946 exit(error);
947 }
948
949
main(int argc,char ** argv)950 int main(int argc, char **argv)
951 {
952 MY_INIT(argv[0]);
953 /*
954 ** Check out the args
955 */
956 if (get_options(&argc, &argv))
957 {
958 my_end(my_end_arg);
959 exit(EX_USAGE);
960 }
961 if (dbConnect(current_host, current_user, opt_password))
962 exit(EX_MYSQLERR);
963
964 if (!opt_write_binlog)
965 {
966 if (disable_binlog()) {
967 first_error= 1;
968 goto end;
969 }
970 }
971
972 if (opt_auto_repair &&
973 (my_init_dynamic_array(&tables4repair, sizeof(char)*(NAME_LEN*2+2),16,64) ||
974 my_init_dynamic_array(&tables4rebuild, sizeof(char)*(NAME_LEN*2+2),16,64) ||
975 my_init_dynamic_array(&alter_table_cmds, MAX_ALTER_STR_SIZE, 0, 1)))
976 {
977 first_error = 1;
978 goto end;
979 }
980
981 if (opt_alldbs)
982 process_all_databases();
983 /* Only one database and selected table(s) */
984 else if (argc > 1 && !opt_databases)
985 process_selected_tables(*argv, (argv + 1), (argc - 1));
986 /* One or more databases, all tables */
987 else
988 process_databases(argv);
989 if (opt_auto_repair)
990 {
991 size_t i;
992
993 if (!opt_silent && (tables4repair.elements || tables4rebuild.elements))
994 puts("\nRepairing tables");
995 what_to_do = DO_REPAIR;
996 for (i = 0; i < tables4repair.elements ; i++)
997 {
998 char *name= (char*) dynamic_array_ptr(&tables4repair, i);
999 handle_request_for_tables(name, fixed_name_length(name));
1000 }
1001 for (i = 0; i < tables4rebuild.elements ; i++)
1002 rebuild_table((char*) dynamic_array_ptr(&tables4rebuild, i));
1003 for (i = 0; i < alter_table_cmds.elements ; i++)
1004 run_query((char*) dynamic_array_ptr(&alter_table_cmds, i));
1005 }
1006 end:
1007 dbDisconnect(current_host);
1008 if (opt_auto_repair)
1009 {
1010 delete_dynamic(&tables4repair);
1011 delete_dynamic(&tables4rebuild);
1012 }
1013 my_free(opt_password);
1014 #ifdef HAVE_SMEM
1015 my_free(shared_memory_base_name);
1016 #endif
1017 my_end(my_end_arg);
1018 return(first_error!=0);
1019 } /* main */
1020