~drizzle-trunk/drizzle/development

« back to all changes in this revision

Viewing changes to client/drizzlecheck.cc

  • Committer: Monty Taylor
  • Date: 2009-03-04 02:48:12 UTC
  • mto: (917.1.2 mordred)
  • mto: This revision was merged to the branch mainline in revision 918.
  • Revision ID: mordred@inaugust.com-20090304024812-5wb6wpye5c1iitbq
Applied atomic patch to current tree.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (C) 2008 Drizzle development team
 
2
 
 
3
   This program is free software; you can redistribute it and/or modify
 
4
   it under the terms of the GNU General Public License as published by
 
5
   the Free Software Foundation; version 2 of the License.
 
6
 
 
7
   This program is distributed in the hope that it will be useful,
 
8
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
9
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
10
   GNU General Public License for more details.
 
11
 
 
12
   You should have received a copy of the GNU General Public License
 
13
   along with this program; if not, write to the Free Software
 
14
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
15
 
 
16
/* By Jani Tolonen, 2001-04-20, MySQL, MYSQL Development Team */
 
17
 
 
18
#define CHECK_VERSION "2.5.0"
 
19
 
 
20
#include "client_priv.h"
 
21
#include <vector>
 
22
#include <string>
 
23
#include <mystrings/m_ctype.h>
 
24
 
 
25
/* Added this for string translation. */
 
26
#include <drizzled/gettext.h>
 
27
 
 
28
 
 
29
using namespace std;
 
30
 
 
31
template class vector<string>;
 
32
 
 
33
/* Exit codes */
 
34
 
 
35
#define EX_USAGE 1
 
36
#define EX_MYSQLERR 2
 
37
 
 
38
static DRIZZLE drizzleclient_connection, *sock= 0;
 
39
static bool opt_alldbs= false, opt_check_only_changed= false,
 
40
            opt_extended= false, opt_compress= false, opt_databases= false,
 
41
            opt_fast= false, opt_medium_check= false, opt_quick= false,
 
42
            opt_all_in_1= false, opt_silent= false, opt_auto_repair= false,
 
43
            ignore_errors= false, tty_password= false, opt_frm= false,
 
44
            debug_info_flag= false, debug_check_flag= false,
 
45
            opt_fix_table_names= false, opt_fix_db_names= false,
 
46
            opt_upgrade= false, opt_write_binlog= true;
 
47
static uint32_t verbose= 0;
 
48
static uint32_t opt_drizzle_port= 0;
 
49
static int my_end_arg;
 
50
static char * opt_drizzle_unix_port= NULL;
 
51
static char *opt_password= NULL, *current_user= NULL,
 
52
      *default_charset= (char *)DRIZZLE_DEFAULT_CHARSET_NAME,
 
53
      *current_host= NULL;
 
54
static int first_error= 0;
 
55
vector<string> tables4repair;
 
56
static uint32_t opt_protocol=0;
 
57
static const CHARSET_INFO *charset_info= &my_charset_utf8_general_ci;
 
58
 
 
59
enum operations { DO_CHECK, 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 will be same as  --databases with all databases selected.",
 
65
   (char**) &opt_alldbs, (char**) &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
   (char**) &opt_all_in_1, (char**) &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
   (char**) &opt_auto_repair, (char**) &opt_auto_repair, 0, GET_BOOL, NO_ARG, 0,
 
76
   0, 0, 0, 0, 0},
 
77
  {"check", 'c', "Check table for errors.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0,
 
78
   0, 0, 0, 0},
 
79
  {"check-only-changed", 'C',
 
80
   "Check only tables that have changed since last check or haven't been closed properly.",
 
81
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
82
  {"check-upgrade", 'g',
 
83
   "Check tables for version-dependent changes. May be used with --auto-repair to correct tables requiring version-dependent updates.",
 
84
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
85
  {"compress", OPT_COMPRESS, "Use compression in server/client protocol.",
 
86
   (char**) &opt_compress, (char**) &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0,
 
87
   0, 0, 0},
 
88
  {"databases", 'B',
 
89
   "To check several databases. Note the difference in usage; In this case no tables are given. All name arguments are regarded as databasenames.",
 
90
   (char**) &opt_databases, (char**) &opt_databases, 0, GET_BOOL, NO_ARG,
 
91
   0, 0, 0, 0, 0, 0},
 
92
  {"debug-check", OPT_DEBUG_CHECK, "Check memory and open file usage at exit.",
 
93
   (char**) &debug_check_flag, (char**) &debug_check_flag, 0,
 
94
   GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
 
95
  {"debug-info", OPT_DEBUG_INFO, "Print some debug info at exit.",
 
96
   (char**) &debug_info_flag, (char**) &debug_info_flag,
 
97
   0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
 
98
  {"default-character-set", OPT_DEFAULT_CHARSET,
 
99
   "Set the default character set.", (char**) &default_charset,
 
100
   (char**) &default_charset, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 
101
  {"fast",'F', "Check only tables that haven't been closed properly.",
 
102
   (char**) &opt_fast, (char**) &opt_fast, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0,
 
103
   0},
 
104
  {"fix-db-names", OPT_FIX_DB_NAMES, "Fix database names.",
 
105
    (char**) &opt_fix_db_names, (char**) &opt_fix_db_names,
 
106
    0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
 
107
  {"fix-table-names", OPT_FIX_TABLE_NAMES, "Fix table names.",
 
108
    (char**) &opt_fix_table_names, (char**) &opt_fix_table_names,
 
109
    0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
 
110
  {"force", 'f', "Continue even if we get an sql-error.",
 
111
   (char**) &ignore_errors, (char**) &ignore_errors, 0, GET_BOOL, NO_ARG, 0, 0,
 
112
   0, 0, 0, 0},
 
113
  {"extended", 'e',
 
114
   "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.",
 
115
   (char**) &opt_extended, (char**) &opt_extended, 0, GET_BOOL, NO_ARG, 0, 0, 0,
 
116
   0, 0, 0},
 
117
  {"help", '?', "Display this help message and exit.", 0, 0, 0, GET_NO_ARG,
 
118
   NO_ARG, 0, 0, 0, 0, 0, 0},
 
119
  {"host",'h', "Connect to host.", (char**) &current_host,
 
120
   (char**) &current_host, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 
121
  {"medium-check", 'm',
 
122
   "Faster than extended-check, but only finds 99.99 percent of all errors. Should be good enough for most cases.",
 
123
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
124
  {"write-binlog", OPT_WRITE_BINLOG,
 
125
   "Log ANALYZE, OPTIMIZE and REPAIR TABLE commands. Use --skip-write-binlog when commands should not be sent to replication slaves.",
 
126
   (char**) &opt_write_binlog, (char**) &opt_write_binlog, 0, GET_BOOL, NO_ARG,
 
127
   1, 0, 0, 0, 0, 0},
 
128
  {"optimize", 'o', "Optimize table.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0,
 
129
   0, 0},
 
130
  {"password", 'P',
 
131
   "Password to use when connecting to server. If password is not given it's solicited on the tty.",
 
132
   0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
 
133
  {"port", 'p', "Port number to use for connection or 0 for default to, in "
 
134
   "order of preference, drizzle.cnf, $DRIZZLE_TCP_PORT, "
 
135
   "built-in default (" STRINGIFY_ARG(DRIZZLE_PORT) ").",
 
136
   0, 0, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 
137
  {"protocol", OPT_DRIZZLE_PROTOCOL, "The protocol of connection (tcp,socket,pipe,memory).",
 
138
   0, 0, 0, GET_STR,  REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 
139
  {"quick", 'q',
 
140
   "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.",
 
141
   (char**) &opt_quick, (char**) &opt_quick, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0,
 
142
   0},
 
143
  {"repair", 'r',
 
144
   "Can fix almost anything except unique keys that aren't unique.",
 
145
   0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
146
  {"silent", 's', "Print only error messages.", (char**) &opt_silent,
 
147
   (char**) &opt_silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
 
148
  {"socket", 'S', "Socket file to use for connection.",
 
149
   (char**) &opt_drizzle_unix_port, (char**) &opt_drizzle_unix_port, 0, GET_STR,
 
150
   REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 
151
  {"tables", OPT_TABLES, "Overrides option --databases (-B).", 0, 0, 0,
 
152
   GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
153
  {"use-frm", OPT_FRM,
 
154
   "When used with REPAIR, get table structure from .frm file, so the table can be repaired even if .MYI header is corrupted.",
 
155
   (char**) &opt_frm, (char**) &opt_frm, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0,
 
156
   0},
 
157
#ifndef DONT_ALLOW_USER_CHANGE
 
158
  {"user", 'u', "User for login if not current user.", (char**) &current_user,
 
159
   (char**) &current_user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 
160
#endif
 
161
  {"verbose", 'v', "Print info about the various stages.", 0, 0, 0, GET_NO_ARG,
 
162
   NO_ARG, 0, 0, 0, 0, 0, 0},
 
163
  {"version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG,
 
164
   NO_ARG, 0, 0, 0, 0, 0, 0},
 
165
  {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
 
166
};
 
167
 
 
168
static const char *load_default_groups[] = { "mysqlcheck", "client", 0 };
 
169
 
 
170
 
 
171
static void print_version(void);
 
172
static void usage(void);
 
173
static int get_options(int *argc, char ***argv);
 
174
static int process_all_databases(void);
 
175
static int process_databases(char **db_names);
 
176
static int process_selected_tables(char *db, char **table_names, int tables);
 
177
static int process_all_tables_in_db(char *database);
 
178
static int process_one_db(char *database);
 
179
static int use_db(char *database);
 
180
static int handle_request_for_tables(const char *tables, uint32_t length);
 
181
static int dbConnect(char *host, char *user,char *passwd);
 
182
static void dbDisconnect(char *host);
 
183
static void DBerror(DRIZZLE *drizzle, const char *when);
 
184
static void safe_exit(int error);
 
185
static void print_result(void);
 
186
static uint32_t fixed_name_length(const char *name);
 
187
static char *fix_table_name(char *dest, const char *src);
 
188
int what_to_do = 0;
 
189
 
 
190
static void print_version(void)
 
191
{
 
192
  printf("%s  Ver %s Distrib %s, for %s (%s)\n", my_progname, CHECK_VERSION,
 
193
   drizzleclient_get_client_info(), SYSTEM_TYPE, MACHINE_TYPE);
 
194
} /* print_version */
 
195
 
 
196
static void usage(void)
 
197
{
 
198
  print_version();
 
199
  puts("By Jani Tolonen, 2001-04-20, MySQL Development Team\n");
 
200
  puts("This software comes with ABSOLUTELY NO WARRANTY. This is free software,\n");
 
201
  puts("and you are welcome to modify and redistribute it under the GPL license.\n");
 
202
  puts("This program can be used to CHECK (-c,-m,-C), REPAIR (-r), ANALYZE (-a)");
 
203
  puts("or OPTIMIZE (-o) tables. Some of the options (like -e or -q) can be");
 
204
  puts("used at the same time. Not all options are supported by all storage engines.");
 
205
  puts("Please consult the Drizzle manual for latest information about the");
 
206
  puts("above. The options -c,-r,-a and -o are exclusive to each other, which");
 
207
  puts("means that the last option will be used, if several was specified.\n");
 
208
  puts("The option -c will be used by default, if none was specified. You");
 
209
  puts("can change the default behavior by making a symbolic link, or");
 
210
  puts("copying this file somewhere with another name, the alternatives are:");
 
211
  puts("mysqlrepair:   The default option will be -r");
 
212
  puts("mysqlanalyze:  The default option will be -a");
 
213
  puts("mysqloptimize: The default option will be -o\n");
 
214
  printf("Usage: %s [OPTIONS] database [tables]\n", my_progname);
 
215
  printf("OR     %s [OPTIONS] --databases DB1 [DB2 DB3...]\n",
 
216
   my_progname);
 
217
  printf("OR     %s [OPTIONS] --all-databases\n", my_progname);
 
218
  print_defaults("drizzle", load_default_groups);
 
219
  my_print_help(my_long_options);
 
220
  my_print_variables(my_long_options);
 
221
} /* usage */
 
222
 
 
223
extern "C"
 
224
bool get_one_option(int optid, const struct my_option *, char *argument)
 
225
{
 
226
  char *endchar= NULL;
 
227
  uint64_t temp_drizzle_port= 0;
 
228
 
 
229
  switch(optid) {
 
230
  case 'a':
 
231
    what_to_do = DO_ANALYZE;
 
232
    break;
 
233
  case 'c':
 
234
    what_to_do = DO_CHECK;
 
235
    break;
 
236
  case 'C':
 
237
    what_to_do = DO_CHECK;
 
238
    opt_check_only_changed = 1;
 
239
    break;
 
240
  case 'I': /* Fall through */
 
241
  case '?':
 
242
    usage();
 
243
    exit(0);
 
244
  case 'm':
 
245
    what_to_do = DO_CHECK;
 
246
    opt_medium_check = 1;
 
247
    break;
 
248
  case 'o':
 
249
    what_to_do = DO_OPTIMIZE;
 
250
    break;
 
251
  case OPT_FIX_DB_NAMES:
 
252
    what_to_do= DO_UPGRADE;
 
253
    default_charset= (char*) "utf8";
 
254
    opt_databases= 1;
 
255
    break;
 
256
  case OPT_FIX_TABLE_NAMES:
 
257
    what_to_do= DO_UPGRADE;
 
258
    default_charset= (char*) "utf8";
 
259
    break;
 
260
  case 'p':
 
261
    temp_drizzle_port= (uint64_t) strtoul(argument, &endchar, 10);
 
262
    /* if there is an alpha character this is not a valid port */
 
263
    if (strlen(endchar) != 0)
 
264
    {
 
265
      fprintf(stderr, _("Non-integer value supplied for port.  If you are trying to enter a password please use --password instead.\n"));
 
266
      exit(EX_USAGE);
 
267
    }
 
268
    /* If the port number is > 65535 it is not a valid port
 
269
       This also helps with potential data loss casting unsigned long to a
 
270
       uint32_t. */
 
271
    if ((temp_drizzle_port == 0) || (temp_drizzle_port > 65535))
 
272
    {
 
273
      fprintf(stderr, _("Value supplied for port is not valid.\n"));
 
274
      exit(EX_USAGE);
 
275
    }
 
276
    else
 
277
    {
 
278
      opt_drizzle_port= (uint32_t) temp_drizzle_port;
 
279
    }
 
280
    break;
 
281
  case 'P':
 
282
    if (argument)
 
283
    {
 
284
      char *start= argument;
 
285
      if (opt_password)
 
286
        free(opt_password);
 
287
      opt_password = strdup(argument);
 
288
      if (opt_password == NULL)
 
289
      {
 
290
        fprintf(stderr, "Memory allocation error while copying password. "
 
291
                        "Aborting.\n");
 
292
        exit(ENOMEM);
 
293
      }
 
294
      while (*argument)
 
295
      {
 
296
        /* Overwriting password with 'x' */
 
297
        *argument++= 'x';
 
298
      }
 
299
      if (*start)
 
300
      {
 
301
        /* Cut length of argument */
 
302
        start[1] = 0;
 
303
      }
 
304
      tty_password= 0;
 
305
    }
 
306
    else
 
307
      tty_password = 1;
 
308
    break;
 
309
  case 'r':
 
310
    what_to_do = DO_REPAIR;
 
311
    break;
 
312
  case 'g':
 
313
    what_to_do= DO_CHECK;
 
314
    opt_upgrade= 1;
 
315
    break;
 
316
  case OPT_TABLES:
 
317
    opt_databases = 0;
 
318
    break;
 
319
  case 'v':
 
320
    verbose++;
 
321
    break;
 
322
  case 'V': print_version(); exit(0);
 
323
  case OPT_DRIZZLE_PROTOCOL:
 
324
    break;
 
325
  }
 
326
  return 0;
 
327
}
 
328
 
 
329
 
 
330
static int get_options(int *argc, char ***argv)
 
331
{
 
332
  int ho_error;
 
333
 
 
334
  if (*argc == 1)
 
335
  {
 
336
    usage();
 
337
    exit(0);
 
338
  }
 
339
 
 
340
  load_defaults("drizzle", load_default_groups, argc, argv);
 
341
 
 
342
  if ((ho_error=handle_options(argc, argv, my_long_options, get_one_option)))
 
343
    exit(ho_error);
 
344
 
 
345
  if (!what_to_do)
 
346
  {
 
347
    int pnlen = strlen(my_progname);
 
348
 
 
349
    if (pnlen < 6) /* name too short */
 
350
      what_to_do = DO_CHECK;
 
351
    else if (!strcmp("repair", my_progname + pnlen - 6))
 
352
      what_to_do = DO_REPAIR;
 
353
    else if (!strcmp("analyze", my_progname + pnlen - 7))
 
354
      what_to_do = DO_ANALYZE;
 
355
    else if  (!strcmp("optimize", my_progname + pnlen - 8))
 
356
      what_to_do = DO_OPTIMIZE;
 
357
    else
 
358
      what_to_do = DO_CHECK;
 
359
  }
 
360
 
 
361
  /* TODO: This variable is not yet used */
 
362
  if (strcmp(default_charset, charset_info->csname) &&
 
363
      !(charset_info= get_charset_by_csname(default_charset, MY_CS_PRIMARY)))
 
364
      exit(1);
 
365
  if (*argc > 0 && opt_alldbs)
 
366
  {
 
367
    printf("You should give only options, no arguments at all, with option\n");
 
368
    printf("--all-databases. Please see %s --help for more information.\n",
 
369
     my_progname);
 
370
    return 1;
 
371
  }
 
372
  if (*argc < 1 && !opt_alldbs)
 
373
  {
 
374
    printf("You forgot to give the arguments! Please see %s --help\n",
 
375
     my_progname);
 
376
    printf("for more information.\n");
 
377
    return 1;
 
378
  }
 
379
  if (tty_password)
 
380
    opt_password = drizzleclient_get_tty_password(NULL);
 
381
  if (debug_info_flag)
 
382
    my_end_arg= MY_CHECK_ERROR | MY_GIVE_INFO;
 
383
  if (debug_check_flag)
 
384
    my_end_arg= MY_CHECK_ERROR;
 
385
  return(0);
 
386
} /* get_options */
 
387
 
 
388
 
 
389
static int process_all_databases()
 
390
{
 
391
  DRIZZLE_ROW row;
 
392
  DRIZZLE_RES *tableres;
 
393
  int result = 0;
 
394
 
 
395
  if (drizzleclient_query(sock, "SHOW DATABASES") ||
 
396
      !(tableres = drizzleclient_store_result(sock)))
 
397
  {
 
398
    my_printf_error(0, "Error: Couldn't execute 'SHOW DATABASES': %s",
 
399
        MYF(0), drizzleclient_error(sock));
 
400
    return 1;
 
401
  }
 
402
  while ((row = drizzleclient_fetch_row(tableres)))
 
403
  {
 
404
    if (process_one_db(row[0]))
 
405
      result = 1;
 
406
  }
 
407
  return result;
 
408
}
 
409
/* process_all_databases */
 
410
 
 
411
 
 
412
static int process_databases(char **db_names)
 
413
{
 
414
  int result = 0;
 
415
  for ( ; *db_names ; db_names++)
 
416
  {
 
417
    if (process_one_db(*db_names))
 
418
      result = 1;
 
419
  }
 
420
  return result;
 
421
} /* process_databases */
 
422
 
 
423
 
 
424
static int process_selected_tables(char *db, char **table_names, int tables)
 
425
{
 
426
  if (use_db(db))
 
427
    return 1;
 
428
  if (opt_all_in_1)
 
429
  {
 
430
    /*
 
431
      We need table list in form `a`, `b`, `c`
 
432
      that's why we need 2 more chars added to to each table name
 
433
      space is for more readable output in logs and in case of error
 
434
    */
 
435
    char *table_names_comma_sep, *end;
 
436
    int i, tot_length = 0;
 
437
 
 
438
    for (i = 0; i < tables; i++)
 
439
      tot_length+= fixed_name_length(*(table_names + i)) + 2;
 
440
 
 
441
    if (!(table_names_comma_sep = (char *)
 
442
          malloc((sizeof(char) * tot_length) + 4)))
 
443
      return 1;
 
444
 
 
445
    for (end = table_names_comma_sep + 1; tables > 0;
 
446
         tables--, table_names++)
 
447
    {
 
448
      end= fix_table_name(end, *table_names);
 
449
      *end++= ',';
 
450
    }
 
451
    *--end = 0;
 
452
    handle_request_for_tables(table_names_comma_sep + 1, tot_length - 1);
 
453
    free(table_names_comma_sep);
 
454
  }
 
455
  else
 
456
    for (; tables > 0; tables--, table_names++)
 
457
      handle_request_for_tables(*table_names, fixed_name_length(*table_names));
 
458
  return 0;
 
459
} /* process_selected_tables */
 
460
 
 
461
 
 
462
static uint32_t fixed_name_length(const char *name)
 
463
{
 
464
  const char *p;
 
465
  uint32_t extra_length= 2;  /* count the first/last backticks */
 
466
 
 
467
  for (p= name; *p; p++)
 
468
  {
 
469
    if (*p == '`')
 
470
      extra_length++;
 
471
    else if (*p == '.')
 
472
      extra_length+= 2;
 
473
  }
 
474
  return (p - name) + extra_length;
 
475
}
 
476
 
 
477
 
 
478
static char *fix_table_name(char *dest, const char *src)
 
479
{
 
480
  *dest++= '`';
 
481
  for (; *src; src++)
 
482
  {
 
483
    switch (*src) {
 
484
    case '.':            /* add backticks around '.' */
 
485
      *dest++= '`';
 
486
      *dest++= '.';
 
487
      *dest++= '`';
 
488
      break;
 
489
    case '`':            /* escape backtick character */
 
490
      *dest++= '`';
 
491
      /* fall through */
 
492
    default:
 
493
      *dest++= *src;
 
494
    }
 
495
  }
 
496
  *dest++= '`';
 
497
  return dest;
 
498
}
 
499
 
 
500
 
 
501
static int process_all_tables_in_db(char *database)
 
502
{
 
503
  DRIZZLE_RES *res;
 
504
  DRIZZLE_ROW row;
 
505
  uint32_t num_columns;
 
506
 
 
507
  if (use_db(database))
 
508
    return 1;
 
509
  if (drizzleclient_query(sock, "SHOW /*!50002 FULL*/ TABLES") ||
 
510
  !((res= drizzleclient_store_result(sock))))
 
511
    return 1;
 
512
 
 
513
  num_columns= drizzleclient_num_fields(res);
 
514
 
 
515
  if (opt_all_in_1)
 
516
  {
 
517
    /*
 
518
      We need table list in form `a`, `b`, `c`
 
519
      that's why we need 2 more chars added to to each table name
 
520
      space is for more readable output in logs and in case of error
 
521
     */
 
522
 
 
523
    char *tables, *end;
 
524
    uint32_t tot_length = 0;
 
525
 
 
526
    while ((row = drizzleclient_fetch_row(res)))
 
527
      tot_length+= fixed_name_length(row[0]) + 2;
 
528
    drizzleclient_data_seek(res, 0);
 
529
 
 
530
    if (!(tables=(char *) malloc(sizeof(char)*tot_length+4)))
 
531
    {
 
532
      drizzleclient_free_result(res);
 
533
      return 1;
 
534
    }
 
535
    for (end = tables + 1; (row = drizzleclient_fetch_row(res)) ;)
 
536
    {
 
537
      if ((num_columns == 2) && (strcmp(row[1], "VIEW") == 0))
 
538
        continue;
 
539
 
 
540
      end= fix_table_name(end, row[0]);
 
541
      *end++= ',';
 
542
    }
 
543
    *--end = 0;
 
544
    if (tot_length)
 
545
      handle_request_for_tables(tables + 1, tot_length - 1);
 
546
    free(tables);
 
547
  }
 
548
  else
 
549
  {
 
550
    while ((row = drizzleclient_fetch_row(res)))
 
551
    {
 
552
      /* Skip views if we don't perform renaming. */
 
553
      if ((what_to_do != DO_UPGRADE) && (num_columns == 2) && (strcmp(row[1], "VIEW") == 0))
 
554
        continue;
 
555
 
 
556
      handle_request_for_tables(row[0], fixed_name_length(row[0]));
 
557
    }
 
558
  }
 
559
  drizzleclient_free_result(res);
 
560
  return 0;
 
561
} /* process_all_tables_in_db */
 
562
 
 
563
 
 
564
 
 
565
static int fix_table_storage_name(const char *name)
 
566
{
 
567
  char qbuf[100 + NAME_LEN*4];
 
568
  int rc= 0;
 
569
  if (strncmp(name, "#mysql50#", 9))
 
570
    return 1;
 
571
  sprintf(qbuf, "RENAME TABLE `%s` TO `%s`", name, name + 9);
 
572
  if (drizzleclient_query(sock, qbuf))
 
573
  {
 
574
    fprintf(stderr, "Failed to %s\n", qbuf);
 
575
    fprintf(stderr, "Error: %s\n", drizzleclient_error(sock));
 
576
    rc= 1;
 
577
  }
 
578
  if (verbose)
 
579
    printf("%-50s %s\n", name, rc ? "FAILED" : "OK");
 
580
  return rc;
 
581
}
 
582
 
 
583
static int fix_database_storage_name(const char *name)
 
584
{
 
585
  char qbuf[100 + NAME_LEN*4];
 
586
  int rc= 0;
 
587
  if (strncmp(name, "#mysql50#", 9))
 
588
    return 1;
 
589
  sprintf(qbuf, "ALTER DATABASE `%s` UPGRADE DATA DIRECTORY NAME", name);
 
590
  if (drizzleclient_query(sock, qbuf))
 
591
  {
 
592
    fprintf(stderr, "Failed to %s\n", qbuf);
 
593
    fprintf(stderr, "Error: %s\n", drizzleclient_error(sock));
 
594
    rc= 1;
 
595
  }
 
596
  if (verbose)
 
597
    printf("%-50s %s\n", name, rc ? "FAILED" : "OK");
 
598
  return rc;
 
599
}
 
600
 
 
601
static int process_one_db(char *database)
 
602
{
 
603
  if (what_to_do == DO_UPGRADE)
 
604
  {
 
605
    int rc= 0;
 
606
    if (opt_fix_db_names && !strncmp(database,"#mysql50#", 9))
 
607
    {
 
608
      rc= fix_database_storage_name(database);
 
609
      database+= 9;
 
610
    }
 
611
    if (rc || !opt_fix_table_names)
 
612
      return rc;
 
613
  }
 
614
  return process_all_tables_in_db(database);
 
615
}
 
616
 
 
617
 
 
618
static int use_db(char *database)
 
619
{
 
620
  if (drizzleclient_get_server_version(sock) >= 50003 &&
 
621
      !my_strcasecmp(&my_charset_utf8_general_ci, database, "information_schema"))
 
622
    return 1;
 
623
  if (drizzleclient_select_db(sock, database))
 
624
  {
 
625
    DBerror(sock, "when selecting the database");
 
626
    return 1;
 
627
  }
 
628
  return 0;
 
629
} /* use_db */
 
630
 
 
631
 
 
632
static int handle_request_for_tables(const char *tables, uint32_t length)
 
633
{
 
634
  char *query, *end, options[100], message[100];
 
635
  uint32_t query_length= 0;
 
636
  const char *op = 0;
 
637
 
 
638
  options[0] = 0;
 
639
  end = options;
 
640
  switch (what_to_do) {
 
641
  case DO_CHECK:
 
642
    op = "CHECK";
 
643
    if (opt_quick)              end = strcpy(end, " QUICK")+6;
 
644
    if (opt_fast)               end = strcpy(end, " FAST")+5;
 
645
    if (opt_medium_check)       end = strcpy(end, " MEDIUM")+7; /* Default */
 
646
    if (opt_extended)           end = strcpy(end, " EXTENDED")+9;
 
647
    if (opt_check_only_changed) end = strcpy(end, " CHANGED")+8;
 
648
    if (opt_upgrade)            end = strcpy(end, " FOR UPGRADE")+12;
 
649
    break;
 
650
  case DO_REPAIR:
 
651
    op= (opt_write_binlog) ? "REPAIR" : "REPAIR NO_WRITE_TO_BINLOG";
 
652
    if (opt_quick)              end = strcpy(end, " QUICK")+6;
 
653
    if (opt_extended)           end = strcpy(end, " EXTENDED")+9;
 
654
    if (opt_frm)                end = strcpy(end, " USE_FRM")+8;
 
655
    break;
 
656
  case DO_ANALYZE:
 
657
    op= (opt_write_binlog) ? "ANALYZE" : "ANALYZE NO_WRITE_TO_BINLOG";
 
658
    break;
 
659
  case DO_OPTIMIZE:
 
660
    op= (opt_write_binlog) ? "OPTIMIZE" : "OPTIMIZE NO_WRITE_TO_BINLOG";
 
661
    break;
 
662
  case DO_UPGRADE:
 
663
    return fix_table_storage_name(tables);
 
664
  }
 
665
 
 
666
  if (!(query =(char *) malloc((sizeof(char)*(length+110)))))
 
667
    return 1;
 
668
  if (opt_all_in_1)
 
669
  {
 
670
    /* No backticks here as we added them before */
 
671
    query_length= sprintf(query, "%s TABLE %s %s", op, tables, options);
 
672
  }
 
673
  else
 
674
  {
 
675
    char *ptr;
 
676
    ptr= query;
 
677
    ptr= strcpy(query, op)+strlen(op);
 
678
    ptr= strcpy(ptr, " TABLE ")+7;
 
679
    ptr= fix_table_name(ptr, tables);
 
680
    ptr+= sprintf(ptr," %s",options);
 
681
    query_length= (uint32_t) (ptr - query);
 
682
  }
 
683
  if (drizzleclient_real_query(sock, query, query_length))
 
684
  {
 
685
    sprintf(message, "when executing '%s TABLE ... %s'", op, options);
 
686
    DBerror(sock, message);
 
687
    return 1;
 
688
  }
 
689
  print_result();
 
690
  free(query);
 
691
  return 0;
 
692
}
 
693
 
 
694
 
 
695
static void print_result()
 
696
{
 
697
  DRIZZLE_RES *res;
 
698
  DRIZZLE_ROW row;
 
699
  char prev[NAME_LEN*2+2];
 
700
  uint32_t i;
 
701
  bool found_error=0;
 
702
 
 
703
  res = drizzleclient_use_result(sock);
 
704
 
 
705
  prev[0] = '\0';
 
706
  for (i = 0; (row = drizzleclient_fetch_row(res)); i++)
 
707
  {
 
708
    int changed = strcmp(prev, row[0]);
 
709
    bool status = !strcmp(row[2], "status");
 
710
 
 
711
    if (status)
 
712
    {
 
713
      /*
 
714
        if there was an error with the table, we have --auto-repair set,
 
715
        and this isn't a repair op, then add the table to the tables4repair
 
716
        list
 
717
      */
 
718
      if (found_error && opt_auto_repair && what_to_do != DO_REPAIR &&
 
719
          strcmp(row[3],"OK"))
 
720
        tables4repair.push_back(string(prev));
 
721
      found_error=0;
 
722
      if (opt_silent)
 
723
  continue;
 
724
    }
 
725
    if (status && changed)
 
726
      printf("%-50s %s", row[0], row[3]);
 
727
    else if (!status && changed)
 
728
    {
 
729
      printf("%s\n%-9s: %s", row[0], row[2], row[3]);
 
730
      if (strcmp(row[2],"note"))
 
731
  found_error=1;
 
732
    }
 
733
    else
 
734
      printf("%-9s: %s", row[2], row[3]);
 
735
    strcpy(prev, row[0]);
 
736
    putchar('\n');
 
737
  }
 
738
  /* add the last table to be repaired to the list */
 
739
  if (found_error && opt_auto_repair && what_to_do != DO_REPAIR)
 
740
    tables4repair.push_back(string(prev));
 
741
  drizzleclient_free_result(res);
 
742
}
 
743
 
 
744
 
 
745
static int dbConnect(char *host, char *user, char *passwd)
 
746
{
 
747
 
 
748
  if (verbose)
 
749
  {
 
750
    fprintf(stderr, "# Connecting to %s...\n", host ? host : "localhost");
 
751
  }
 
752
  drizzleclient_create(&drizzleclient_connection);
 
753
  if (opt_compress)
 
754
    drizzleclient_options(&drizzleclient_connection, DRIZZLE_OPT_COMPRESS, NULL);
 
755
  if (opt_protocol)
 
756
    drizzleclient_options(&drizzleclient_connection,DRIZZLE_OPT_PROTOCOL,(char*)&opt_protocol);
 
757
  if (!(sock = drizzleclient_connect(&drizzleclient_connection, host, user, passwd,
 
758
         NULL, opt_drizzle_port, opt_drizzle_unix_port, 0)))
 
759
  {
 
760
    DBerror(&drizzleclient_connection, "when trying to connect");
 
761
    return 1;
 
762
  }
 
763
  drizzleclient_connection.reconnect= 1;
 
764
  return 0;
 
765
} /* dbConnect */
 
766
 
 
767
 
 
768
static void dbDisconnect(char *host)
 
769
{
 
770
  if (verbose)
 
771
    fprintf(stderr, "# Disconnecting from %s...\n", host ? host : "localhost");
 
772
  drizzleclient_close(sock);
 
773
} /* dbDisconnect */
 
774
 
 
775
 
 
776
static void DBerror(DRIZZLE *drizzle, const char *when)
 
777
{
 
778
  my_printf_error(0,"Got error: %d: %s %s", MYF(0),
 
779
      drizzleclient_errno(drizzle), drizzleclient_error(drizzle), when);
 
780
  safe_exit(EX_MYSQLERR);
 
781
  return;
 
782
} /* DBerror */
 
783
 
 
784
 
 
785
static void safe_exit(int error)
 
786
{
 
787
  if (!first_error)
 
788
    first_error= error;
 
789
  if (ignore_errors)
 
790
    return;
 
791
  if (sock)
 
792
    drizzleclient_close(sock);
 
793
  exit(error);
 
794
}
 
795
 
 
796
 
 
797
int main(int argc, char **argv)
 
798
{
 
799
  MY_INIT(argv[0]);
 
800
  /*
 
801
  ** Check out the args
 
802
  */
 
803
  if (get_options(&argc, &argv))
 
804
  {
 
805
    my_end(my_end_arg);
 
806
    exit(EX_USAGE);
 
807
  }
 
808
  if (dbConnect(current_host, current_user, opt_password))
 
809
    exit(EX_MYSQLERR);
 
810
 
 
811
  if (opt_auto_repair)
 
812
  {
 
813
    tables4repair.reserve(64);
 
814
    if (tables4repair.capacity() == 0)
 
815
    {
 
816
      first_error = 1;
 
817
      goto end;
 
818
    }
 
819
  }
 
820
 
 
821
 
 
822
  if (opt_alldbs)
 
823
    process_all_databases();
 
824
  /* Only one database and selected table(s) */
 
825
  else if (argc > 1 && !opt_databases)
 
826
    process_selected_tables(*argv, (argv + 1), (argc - 1));
 
827
  /* One or more databases, all tables */
 
828
  else
 
829
    process_databases(argv);
 
830
  if (opt_auto_repair)
 
831
  {
 
832
 
 
833
    if (!opt_silent && (tables4repair.size() > 0))
 
834
      puts("\nRepairing tables");
 
835
    what_to_do = DO_REPAIR;
 
836
    vector<string>::iterator i;
 
837
    for ( i= tables4repair.begin() ; i < tables4repair.end() ; i++)
 
838
    {
 
839
      const char *name= (*i).c_str();
 
840
      handle_request_for_tables(name, fixed_name_length(name));
 
841
    }
 
842
  }
 
843
 end:
 
844
  dbDisconnect(current_host);
 
845
  free(opt_password);
 
846
  my_end(my_end_arg);
 
847
  return(first_error!=0);
 
848
} /* main */