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