MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
mysqlcheck.c
1 /*
2  Copyright (c) 2001, 2013, 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 "my_default.h"
22 #include <m_ctype.h>
23 #include <mysql_version.h>
24 #include <mysqld_error.h>
25 #include <sslopt-vars.h>
26 #include <welcome_copyright_notice.h> /* ORACLE_WELCOME_COPYRIGHT_NOTICE */
27 
28 /* Exit codes */
29 
30 #define EX_USAGE 1
31 #define EX_MYSQLERR 2
32 
33 /* ALTER instead of repair. */
34 #define MAX_ALTER_STR_SIZE 128 * 1024
35 #define KEY_PARTITIONING_CHANGED_STR "KEY () partitioning changed"
36 
37 static MYSQL mysql_connection, *sock = 0;
38 static my_bool opt_alldbs = 0, opt_check_only_changed = 0, opt_extended = 0,
39  opt_compress = 0, opt_databases = 0, opt_fast = 0,
40  opt_medium_check = 0, opt_quick = 0, opt_all_in_1 = 0,
41  opt_silent = 0, opt_auto_repair = 0, ignore_errors = 0,
42  tty_password= 0, opt_frm= 0, debug_info_flag= 0, debug_check_flag= 0,
43  opt_fix_table_names= 0, opt_fix_db_names= 0, opt_upgrade= 0,
44  opt_write_binlog= 1;
45 static uint verbose = 0, opt_mysql_port=0;
46 static int my_end_arg;
47 static char * opt_mysql_unix_port = 0;
48 static char *opt_password = 0, *current_user = 0,
49  *default_charset= 0, *current_host= 0;
50 static char *opt_plugin_dir= 0, *opt_default_auth= 0;
51 static int first_error = 0;
52 static char *opt_skip_database;
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 static char *opt_bind_addr = NULL;
59 
60 enum operations { DO_CHECK=1, DO_REPAIR, DO_ANALYZE, DO_OPTIMIZE, DO_UPGRADE };
61 
62 static struct my_option my_long_options[] =
63 {
64  {"all-databases", 'A',
65  "Check all the databases. This is the same as --databases with all databases selected.",
66  &opt_alldbs, &opt_alldbs, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0,
67  0, 0},
68  {"analyze", 'a', "Analyze given tables.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0,
69  0, 0, 0, 0},
70  {"all-in-1", '1',
71  "Instead of issuing one query for each table, use one query per database, naming all tables in the database in a comma-separated list.",
72  &opt_all_in_1, &opt_all_in_1, 0, GET_BOOL, NO_ARG, 0, 0, 0,
73  0, 0, 0},
74  {"auto-repair", OPT_AUTO_REPAIR,
75  "If a checked table is corrupted, automatically fix it. Repairing will be done after all tables have been checked, if corrupted ones were found.",
76  &opt_auto_repair, &opt_auto_repair, 0, GET_BOOL, NO_ARG, 0,
77  0, 0, 0, 0, 0},
78  {"bind-address", 0, "IP address to bind to.",
79  (uchar**) &opt_bind_addr, (uchar**) &opt_bind_addr, 0, GET_STR,
80  REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
81  {"character-sets-dir", OPT_CHARSETS_DIR,
82  "Directory for character set files.", &charsets_dir,
83  &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
84  {"check", 'c', "Check table for errors.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0,
85  0, 0, 0, 0},
86  {"check-only-changed", 'C',
87  "Check only tables that have changed since last check or haven't been closed properly.",
88  0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
89  {"check-upgrade", 'g',
90  "Check tables for version-dependent changes. May be used with --auto-repair to correct tables requiring version-dependent updates.",
91  0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
92  {"compress", OPT_COMPRESS, "Use compression in server/client protocol.",
93  &opt_compress, &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0,
94  0, 0, 0},
95  {"databases", 'B',
96  "Check several databases. Note the difference in usage; in this case no tables are given. All name arguments are regarded as database names.",
97  &opt_databases, &opt_databases, 0, GET_BOOL, NO_ARG,
98  0, 0, 0, 0, 0, 0},
99 #ifdef DBUG_OFF
100  {"debug", '#', "This is a non-debug version. Catch this and exit.",
101  0, 0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0},
102 #else
103  {"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.",
104  0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
105 #endif
106  {"debug-check", OPT_DEBUG_CHECK, "Check memory and open file usage at exit.",
107  &debug_check_flag, &debug_check_flag, 0,
108  GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
109  {"debug-info", OPT_DEBUG_INFO, "Print some debug info at exit.",
110  &debug_info_flag, &debug_info_flag,
111  0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
112  {"default-character-set", OPT_DEFAULT_CHARSET,
113  "Set the default character set.", &default_charset,
114  &default_charset, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
115  {"default_auth", OPT_DEFAULT_AUTH,
116  "Default authentication client-side plugin to use.",
117  &opt_default_auth, &opt_default_auth, 0,
118  GET_STR, REQUIRED_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.", &current_host,
138  &current_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_PASSWORD, 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  {"skip_database", 0, "Don't process the database specified as argument",
184  &opt_skip_database, &opt_skip_database, 0, GET_STR, REQUIRED_ARG,
185  0, 0, 0, 0, 0, 0},
186  {"socket", 'S', "The socket file to use for connection.",
187  &opt_mysql_unix_port, &opt_mysql_unix_port, 0, GET_STR,
188  REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
189 #include <sslopt-longopts.h>
190  {"tables", OPT_TABLES, "Overrides option --databases (-B).", 0, 0, 0,
191  GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
192  {"use-frm", OPT_FRM,
193  "When used with REPAIR, get table structure from .frm file, so the table can be repaired even if .MYI header is corrupted.",
194  &opt_frm, &opt_frm, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0,
195  0},
196 #ifndef DONT_ALLOW_USER_CHANGE
197  {"user", 'u', "User for login if not current user.", &current_user,
198  &current_user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
199 #endif
200  {"verbose", 'v', "Print info about the various stages.", 0, 0, 0, GET_NO_ARG,
201  NO_ARG, 0, 0, 0, 0, 0, 0},
202  {"version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG,
203  NO_ARG, 0, 0, 0, 0, 0, 0},
204  {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
205 };
206 
207 static const char *load_default_groups[] = { "mysqlcheck", "client", 0 };
208 
209 
210 static void print_version(void);
211 static void usage(void);
212 static int get_options(int *argc, char ***argv);
213 static int process_all_databases();
214 static int process_databases(char **db_names);
215 static int process_selected_tables(char *db, char **table_names, int tables);
216 static int process_all_tables_in_db(char *database);
217 static int process_one_db(char *database);
218 static int use_db(char *database);
219 static int handle_request_for_tables(char *tables, uint length);
220 static int dbConnect(char *host, char *user,char *passwd);
221 static void dbDisconnect(char *host);
222 static void DBerror(MYSQL *mysql, const char *when);
223 static void safe_exit(int error);
224 static void print_result();
225 static uint fixed_name_length(const char *name);
226 static char *fix_table_name(char *dest, char *src);
227 int what_to_do = 0;
228 
229 
230 static void print_version(void)
231 {
232  printf("%s Ver %s Distrib %s, for %s (%s)\n", my_progname, CHECK_VERSION,
233  MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE);
234 } /* print_version */
235 
236 
237 static void usage(void)
238 {
239  print_version();
240  puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000"));
241  puts("This program can be used to CHECK (-c, -m, -C), REPAIR (-r), ANALYZE (-a),");
242  puts("or OPTIMIZE (-o) tables. Some of the options (like -e or -q) can be");
243  puts("used at the same time. Not all options are supported by all storage engines.");
244  puts("Please consult the MySQL manual for latest information about the");
245  puts("above. The options -c, -r, -a, and -o are exclusive to each other, which");
246  puts("means that the last option will be used, if several was specified.\n");
247  puts("The option -c will be used by default, if none was specified. You");
248  puts("can change the default behavior by making a symbolic link, or");
249  puts("copying this file somewhere with another name, the alternatives are:");
250  puts("mysqlrepair: The default option will be -r");
251  puts("mysqlanalyze: The default option will be -a");
252  puts("mysqloptimize: The default option will be -o\n");
253  printf("Usage: %s [OPTIONS] database [tables]\n", my_progname);
254  printf("OR %s [OPTIONS] --databases DB1 [DB2 DB3...]\n",
255  my_progname);
256  printf("OR %s [OPTIONS] --all-databases\n", my_progname);
257  print_defaults("my", load_default_groups);
258  my_print_help(my_long_options);
259  my_print_variables(my_long_options);
260 } /* usage */
261 
262 
263 static my_bool
264 get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
265  char *argument)
266 {
267  int orig_what_to_do= what_to_do;
268 
269  switch(optid) {
270  case 'a':
271  what_to_do = DO_ANALYZE;
272  break;
273  case 'c':
274  what_to_do = DO_CHECK;
275  break;
276  case 'C':
277  what_to_do = DO_CHECK;
278  opt_check_only_changed = 1;
279  break;
280  case 'I': /* Fall through */
281  case '?':
282  usage();
283  exit(0);
284  case 'm':
285  what_to_do = DO_CHECK;
286  opt_medium_check = 1;
287  break;
288  case 'o':
289  what_to_do = DO_OPTIMIZE;
290  break;
291  case OPT_FIX_DB_NAMES:
292  what_to_do= DO_UPGRADE;
293  opt_databases= 1;
294  break;
295  case OPT_FIX_TABLE_NAMES:
296  what_to_do= DO_UPGRADE;
297  break;
298  case 'p':
299  if (argument == disabled_my_option)
300  argument= (char*) ""; /* Don't require password */
301  if (argument)
302  {
303  char *start = argument;
304  my_free(opt_password);
305  opt_password = my_strdup(argument, MYF(MY_FAE));
306  while (*argument) *argument++= 'x'; /* Destroy argument */
307  if (*start)
308  start[1] = 0; /* Cut length of argument */
309  tty_password= 0;
310  }
311  else
312  tty_password = 1;
313  break;
314  case 'r':
315  what_to_do = DO_REPAIR;
316  break;
317  case 'g':
318  what_to_do= DO_CHECK;
319  opt_upgrade= 1;
320  break;
321  case 'W':
322 #ifdef __WIN__
323  opt_protocol = MYSQL_PROTOCOL_PIPE;
324 #endif
325  break;
326  case '#':
327  DBUG_PUSH(argument ? argument : "d:t:o");
328  debug_check_flag= 1;
329  break;
330 #include <sslopt-case.h>
331  case OPT_TABLES:
332  opt_databases = 0;
333  break;
334  case 'v':
335  verbose++;
336  break;
337  case 'V': print_version(); exit(0);
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 
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  my_getopt_use_args_separator= TRUE;
365  if ((ho_error= load_defaults("my", load_default_groups, argc, argv)) ||
366  (ho_error=handle_options(argc, argv, my_long_options, get_one_option)))
367  exit(ho_error);
368  my_getopt_use_args_separator= FALSE;
369 
370  if (!what_to_do)
371  {
372  size_t pnlen= strlen(my_progname);
373 
374  if (pnlen < 6) /* name too short */
375  what_to_do = DO_CHECK;
376  else if (!strcmp("repair", my_progname + pnlen - 6))
377  what_to_do = DO_REPAIR;
378  else if (!strcmp("analyze", my_progname + pnlen - 7))
379  what_to_do = DO_ANALYZE;
380  else if (!strcmp("optimize", my_progname + pnlen - 8))
381  what_to_do = DO_OPTIMIZE;
382  else
383  what_to_do = DO_CHECK;
384  }
385 
386  /*
387  If there's no --default-character-set option given with
388  --fix-table-name or --fix-db-name set the default character set to "utf8".
389  */
390  if (!default_charset)
391  {
392  if (opt_fix_db_names || opt_fix_table_names)
393  default_charset= (char*) "utf8";
394  else
395  default_charset= (char*) MYSQL_AUTODETECT_CHARSET_NAME;
396  }
397  if (strcmp(default_charset, MYSQL_AUTODETECT_CHARSET_NAME) &&
398  !get_charset_by_csname(default_charset, MY_CS_PRIMARY, MYF(MY_WME)))
399  {
400  printf("Unsupported character set: %s\n", default_charset);
401  return 1;
402  }
403  if (*argc > 0 && opt_alldbs)
404  {
405  printf("You should give only options, no arguments at all, with option\n");
406  printf("--all-databases. Please see %s --help for more information.\n",
407  my_progname);
408  return 1;
409  }
410 
411  if (*argc < 1 && !opt_alldbs)
412  {
413  printf("You forgot to give the arguments! Please see %s --help\n",
414  my_progname);
415  printf("for more information.\n");
416  return 1;
417  }
418  if (tty_password)
419  opt_password = get_tty_password(NullS);
420  if (debug_info_flag)
421  my_end_arg= MY_CHECK_ERROR | MY_GIVE_INFO;
422  if (debug_check_flag)
423  my_end_arg= MY_CHECK_ERROR;
424  return(0);
425 } /* get_options */
426 
427 
428 static int process_all_databases()
429 {
430  MYSQL_ROW row;
431  MYSQL_RES *tableres;
432  int result = 0;
433 
434  if (mysql_query(sock, "SHOW DATABASES") ||
435  !(tableres = mysql_store_result(sock)))
436  {
437  my_printf_error(0, "Error: Couldn't execute 'SHOW DATABASES': %s",
438  MYF(0), mysql_error(sock));
439  return 1;
440  }
441  while ((row = mysql_fetch_row(tableres)))
442  {
443  if (process_one_db(row[0]))
444  result = 1;
445  }
446  return result;
447 }
448 /* process_all_databases */
449 
450 
451 static int process_databases(char **db_names)
452 {
453  int result = 0;
454  for ( ; *db_names ; db_names++)
455  {
456  if (process_one_db(*db_names))
457  result = 1;
458  }
459  return result;
460 } /* process_databases */
461 
462 
463 static int process_selected_tables(char *db, char **table_names, int tables)
464 {
465  if (use_db(db))
466  return 1;
467  if (opt_all_in_1 && what_to_do != DO_UPGRADE)
468  {
469  /*
470  We need table list in form `a`, `b`, `c`
471  that's why we need 2 more chars added to to each table name
472  space is for more readable output in logs and in case of error
473  */
474  char *table_names_comma_sep, *end;
475  size_t tot_length= 0;
476  int i= 0;
477 
478  for (i = 0; i < tables; i++)
479  tot_length+= fixed_name_length(*(table_names + i)) + 2;
480 
481  if (!(table_names_comma_sep = (char *)
482  my_malloc((sizeof(char) * tot_length) + 4, MYF(MY_WME))))
483  return 1;
484 
485  for (end = table_names_comma_sep + 1; tables > 0;
486  tables--, table_names++)
487  {
488  end= fix_table_name(end, *table_names);
489  *end++= ',';
490  }
491  *--end = 0;
492  handle_request_for_tables(table_names_comma_sep + 1, (uint) (tot_length - 1));
493  my_free(table_names_comma_sep);
494  }
495  else
496  for (; tables > 0; tables--, table_names++)
497  handle_request_for_tables(*table_names, fixed_name_length(*table_names));
498  return 0;
499 } /* process_selected_tables */
500 
501 
502 static uint fixed_name_length(const char *name)
503 {
504  const char *p;
505  uint extra_length= 2; /* count the first/last backticks */
506 
507  for (p= name; *p; p++)
508  {
509  if (*p == '`')
510  extra_length++;
511  else if (*p == '.')
512  extra_length+= 2;
513  }
514  return (uint) ((p - name) + extra_length);
515 }
516 
517 
518 static char *fix_table_name(char *dest, char *src)
519 {
520  *dest++= '`';
521  for (; *src; src++)
522  {
523  switch (*src) {
524  case '.': /* add backticks around '.' */
525  *dest++= '`';
526  *dest++= '.';
527  *dest++= '`';
528  break;
529  case '`': /* escape backtick character */
530  *dest++= '`';
531  /* fall through */
532  default:
533  *dest++= *src;
534  }
535  }
536  *dest++= '`';
537  return dest;
538 }
539 
540 
541 static int process_all_tables_in_db(char *database)
542 {
543  MYSQL_RES *res;
544  MYSQL_ROW row;
545  uint num_columns;
546 
547  LINT_INIT(res);
548  if (use_db(database))
549  return 1;
550  if ((mysql_query(sock, "SHOW /*!50002 FULL*/ TABLES") &&
551  mysql_query(sock, "SHOW TABLES")) ||
552  !(res= mysql_store_result(sock)))
553  {
554  my_printf_error(0, "Error: Couldn't get table list for database %s: %s",
555  MYF(0), database, mysql_error(sock));
556  return 1;
557  }
558 
559  num_columns= mysql_num_fields(res);
560 
561  if (opt_all_in_1 && what_to_do != DO_UPGRADE)
562  {
563  /*
564  We need table list in form `a`, `b`, `c`
565  that's why we need 2 more chars added to to each table name
566  space is for more readable output in logs and in case of error
567  */
568 
569  char *tables, *end;
570  uint tot_length = 0;
571 
572  while ((row = mysql_fetch_row(res)))
573  tot_length+= fixed_name_length(row[0]) + 2;
574  mysql_data_seek(res, 0);
575 
576  if (!(tables=(char *) my_malloc(sizeof(char)*tot_length+4, MYF(MY_WME))))
577  {
578  mysql_free_result(res);
579  return 1;
580  }
581  for (end = tables + 1; (row = mysql_fetch_row(res)) ;)
582  {
583  if ((num_columns == 2) && (strcmp(row[1], "VIEW") == 0))
584  continue;
585 
586  end= fix_table_name(end, row[0]);
587  *end++= ',';
588  }
589  *--end = 0;
590  if (tot_length)
591  handle_request_for_tables(tables + 1, tot_length - 1);
592  my_free(tables);
593  }
594  else
595  {
596  while ((row = mysql_fetch_row(res)))
597  {
598  /* Skip views if we don't perform renaming. */
599  if ((what_to_do != DO_UPGRADE) && (num_columns == 2) && (strcmp(row[1], "VIEW") == 0))
600  continue;
601 
602  handle_request_for_tables(row[0], fixed_name_length(row[0]));
603  }
604  }
605  mysql_free_result(res);
606  return 0;
607 } /* process_all_tables_in_db */
608 
609 
610 static int run_query(const char *query)
611 {
612  if (mysql_query(sock, query))
613  {
614  fprintf(stderr, "Failed to %s\n", query);
615  fprintf(stderr, "Error: %s\n", mysql_error(sock));
616  return 1;
617  }
618  return 0;
619 }
620 
621 
622 static int fix_table_storage_name(const char *name)
623 {
624  char qbuf[100 + NAME_LEN*4];
625  int rc= 0;
626  if (strncmp(name, "#mysql50#", 9))
627  return 1;
628  sprintf(qbuf, "RENAME TABLE `%s` TO `%s`", name, name + 9);
629  rc= run_query(qbuf);
630  if (verbose)
631  printf("%-50s %s\n", name, rc ? "FAILED" : "OK");
632  return rc;
633 }
634 
635 static int fix_database_storage_name(const char *name)
636 {
637  char qbuf[100 + NAME_LEN*4];
638  int rc= 0;
639  if (strncmp(name, "#mysql50#", 9))
640  return 1;
641  sprintf(qbuf, "ALTER DATABASE `%s` UPGRADE DATA DIRECTORY NAME", name);
642  rc= run_query(qbuf);
643  if (verbose)
644  printf("%-50s %s\n", name, rc ? "FAILED" : "OK");
645  return rc;
646 }
647 
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, (uint)(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 
669 static int process_one_db(char *database)
670 {
671  if (opt_skip_database && opt_alldbs &&
672  !strcmp(database, opt_skip_database))
673  return 0;
674 
675  if (what_to_do == DO_UPGRADE)
676  {
677  int rc= 0;
678  if (opt_fix_db_names && !strncmp(database,"#mysql50#", 9))
679  {
680  rc= fix_database_storage_name(database);
681  database+= 9;
682  }
683  if (rc || !opt_fix_table_names)
684  return rc;
685  }
686  return process_all_tables_in_db(database);
687 }
688 
689 
690 static int use_db(char *database)
691 {
692  if (mysql_get_server_version(sock) >= FIRST_INFORMATION_SCHEMA_VERSION &&
693  !my_strcasecmp(&my_charset_latin1, database, INFORMATION_SCHEMA_DB_NAME))
694  return 1;
695  if (mysql_get_server_version(sock) >= FIRST_PERFORMANCE_SCHEMA_VERSION &&
696  !my_strcasecmp(&my_charset_latin1, database, PERFORMANCE_SCHEMA_DB_NAME))
697  return 1;
698  if (mysql_select_db(sock, database))
699  {
700  DBerror(sock, "when selecting the database");
701  return 1;
702  }
703  return 0;
704 } /* use_db */
705 
706 static int disable_binlog()
707 {
708  const char *stmt= "SET SQL_LOG_BIN=0";
709  return run_query(stmt);
710 }
711 
712 static int handle_request_for_tables(char *tables, uint length)
713 {
714  char *query, *end, options[100], message[100];
715  uint query_length= 0;
716  const char *op = 0;
717 
718  options[0] = 0;
719  end = options;
720  switch (what_to_do) {
721  case DO_CHECK:
722  op = "CHECK";
723  if (opt_quick) end = strmov(end, " QUICK");
724  if (opt_fast) end = strmov(end, " FAST");
725  if (opt_medium_check) end = strmov(end, " MEDIUM"); /* Default */
726  if (opt_extended) end = strmov(end, " EXTENDED");
727  if (opt_check_only_changed) end = strmov(end, " CHANGED");
728  if (opt_upgrade) end = strmov(end, " FOR UPGRADE");
729  break;
730  case DO_REPAIR:
731  op= (opt_write_binlog) ? "REPAIR" : "REPAIR NO_WRITE_TO_BINLOG";
732  if (opt_quick) end = strmov(end, " QUICK");
733  if (opt_extended) end = strmov(end, " EXTENDED");
734  if (opt_frm) end = strmov(end, " USE_FRM");
735  break;
736  case DO_ANALYZE:
737  op= (opt_write_binlog) ? "ANALYZE" : "ANALYZE NO_WRITE_TO_BINLOG";
738  break;
739  case DO_OPTIMIZE:
740  op= (opt_write_binlog) ? "OPTIMIZE" : "OPTIMIZE NO_WRITE_TO_BINLOG";
741  break;
742  case DO_UPGRADE:
743  return fix_table_storage_name(tables);
744  }
745 
746  if (!(query =(char *) my_malloc((sizeof(char)*(length+110)), MYF(MY_WME))))
747  return 1;
748  if (opt_all_in_1)
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= (uint) (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 
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, 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  strcpy(prev_alter, alter_txt);
845  }
846  }
847  }
848  }
849  else
850  printf("%-9s: %s", row[2], row[3]);
851  strmov(prev, row[0]);
852  putchar('\n');
853  }
854  /* add the last table to be repaired to the list */
855  if (found_error && opt_auto_repair && what_to_do != DO_REPAIR)
856  {
857  if (table_rebuild)
858  {
859  if (prev_alter[0])
860  insert_dynamic(&alter_table_cmds, (uchar*) prev_alter);
861  else
862  insert_dynamic(&tables4rebuild, (uchar*) prev);
863  }
864  else
865  insert_dynamic(&tables4repair, prev);
866  }
867  mysql_free_result(res);
868 }
869 
870 
871 static int dbConnect(char *host, char *user, char *passwd)
872 {
873  DBUG_ENTER("dbConnect");
874  if (verbose)
875  {
876  fprintf(stderr, "# Connecting to %s...\n", host ? host : "localhost");
877  }
878  mysql_init(&mysql_connection);
879  if (opt_compress)
880  mysql_options(&mysql_connection, MYSQL_OPT_COMPRESS, NullS);
881 #ifdef HAVE_OPENSSL
882  if (opt_use_ssl)
883  {
884  mysql_ssl_set(&mysql_connection, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
885  opt_ssl_capath, opt_ssl_cipher);
886  mysql_options(&mysql_connection, MYSQL_OPT_SSL_CRL, opt_ssl_crl);
887  mysql_options(&mysql_connection, MYSQL_OPT_SSL_CRLPATH, opt_ssl_crlpath);
888  }
889 #endif
890  if (opt_protocol)
891  mysql_options(&mysql_connection,MYSQL_OPT_PROTOCOL,(char*)&opt_protocol);
892  if (opt_bind_addr)
893  mysql_options(&mysql_connection, MYSQL_OPT_BIND, opt_bind_addr);
894 #ifdef HAVE_SMEM
895  if (shared_memory_base_name)
896  mysql_options(&mysql_connection,MYSQL_SHARED_MEMORY_BASE_NAME,shared_memory_base_name);
897 #endif
898 
899  if (opt_plugin_dir && *opt_plugin_dir)
900  mysql_options(&mysql_connection, MYSQL_PLUGIN_DIR, opt_plugin_dir);
901 
902  if (opt_default_auth && *opt_default_auth)
903  mysql_options(&mysql_connection, MYSQL_DEFAULT_AUTH, opt_default_auth);
904 
905  mysql_options(&mysql_connection, MYSQL_SET_CHARSET_NAME, default_charset);
906  mysql_options(&mysql_connection, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
907  mysql_options4(&mysql_connection, MYSQL_OPT_CONNECT_ATTR_ADD,
908  "program_name", "mysqlcheck");
909  if (!(sock = mysql_real_connect(&mysql_connection, host, user, passwd,
910  NULL, opt_mysql_port, opt_mysql_unix_port, 0)))
911  {
912  DBerror(&mysql_connection, "when trying to connect");
913  DBUG_RETURN(1);
914  }
915  mysql_connection.reconnect= 1;
916  DBUG_RETURN(0);
917 } /* dbConnect */
918 
919 
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 
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 
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 
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  uint 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 */