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.", &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_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.", &current_user,
195    &current_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