~stewart/drizzle/embedded-innodb-create-select-transaction-arrgh

« back to all changes in this revision

Viewing changes to client/mysql_upgrade.c

  • Committer: brian
  • Date: 2008-06-25 05:29:13 UTC
  • Revision ID: brian@localhost.localdomain-20080625052913-6upwo0jsrl4lnapl
clean slate

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (C) 2000 MySQL AB
 
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
#include "client_priv.h"
 
17
#include "../scripts/mysql_fix_privilege_tables_sql.c"
 
18
 
 
19
#define VER "1.1"
 
20
 
 
21
#ifdef HAVE_SYS_WAIT_H
 
22
#include <sys/wait.h>
 
23
#endif
 
24
 
 
25
#ifndef WEXITSTATUS
 
26
# ifdef __WIN__
 
27
#  define WEXITSTATUS(stat_val) (stat_val)
 
28
# else
 
29
#  define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
 
30
# endif
 
31
#endif
 
32
 
 
33
static char mysql_path[FN_REFLEN];
 
34
static char mysqlcheck_path[FN_REFLEN];
 
35
 
 
36
static my_bool opt_force, opt_verbose, debug_info_flag, debug_check_flag;
 
37
static uint my_end_arg= 0;
 
38
static char *opt_user= (char*)"root";
 
39
 
 
40
static DYNAMIC_STRING ds_args;
 
41
 
 
42
static char *opt_password= 0;
 
43
static my_bool tty_password= 0;
 
44
 
 
45
#ifndef DBUG_OFF
 
46
static char *default_dbug_option= (char*) "d:t:O,/tmp/mysql_upgrade.trace";
 
47
#endif
 
48
 
 
49
static char **defaults_argv;
 
50
 
 
51
static my_bool not_used; /* Can't use GET_BOOL without a value pointer */
 
52
 
 
53
#include <help_start.h>
 
54
 
 
55
static struct my_option my_long_options[]=
 
56
{
 
57
  {"help", '?', "Display this help message and exit.", 0, 0, 0, GET_NO_ARG,
 
58
   NO_ARG, 0, 0, 0, 0, 0, 0},
 
59
  {"basedir", 'b', "Not used by mysql_upgrade. Only for backward compatibilty",
 
60
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 
61
  {"character-sets-dir", OPT_CHARSETS_DIR,
 
62
   "Directory where character sets are.", 0,
 
63
   0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 
64
  {"compress", OPT_COMPRESS, "Use compression in server/client protocol.",
 
65
   (uchar**)&not_used, (uchar**)&not_used, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
 
66
  {"datadir", 'd',
 
67
   "Not used by mysql_upgrade. Only for backward compatibilty",
 
68
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 
69
#ifdef DBUG_OFF
 
70
  {"debug", '#', "This is a non-debug version. Catch this and exit",
 
71
   0, 0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0},
 
72
#else
 
73
  {"debug", '#', "Output debug log", (uchar* *) & default_dbug_option,
 
74
   (uchar* *) & default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
 
75
#endif
 
76
  {"debug-check", OPT_DEBUG_CHECK, "Check memory and open file usage at exit.",
 
77
   (uchar**) &debug_check_flag, (uchar**) &debug_check_flag, 0,
 
78
   GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
 
79
  {"debug-info", 'T', "Print some debug info at exit.", (uchar**) &debug_info_flag,
 
80
   (uchar**) &debug_info_flag, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
 
81
  {"default-character-set", OPT_DEFAULT_CHARSET,
 
82
   "Set the default character set.", 0,
 
83
   0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 
84
  {"force", 'f', "Force execution of mysqlcheck even if mysql_upgrade "
 
85
   "has already been executed for the current version of MySQL.",
 
86
   (uchar**)&opt_force, (uchar**)&opt_force, 0,
 
87
   GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
 
88
  {"host",'h', "Connect to host.", 0,
 
89
   0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 
90
  {"password", 'p',
 
91
   "Password to use when connecting to server. If password is not given"
 
92
   " it's solicited on the tty.", (uchar**) &opt_password,(uchar**) &opt_password,
 
93
   0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
 
94
#ifdef __WIN__
 
95
  {"pipe", 'W', "Use named pipes to connect to server.", 0, 0, 0,
 
96
   GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
 
97
#endif
 
98
  {"port", 'P', "Port number to use for connection or 0 for default to, in "
 
99
   "order of preference, my.cnf, $MYSQL_TCP_PORT, "
 
100
#if MYSQL_PORT_DEFAULT == 0
 
101
   "/etc/services, "
 
102
#endif
 
103
   "built-in default (" STRINGIFY_ARG(MYSQL_PORT) ").",
 
104
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 
105
  {"protocol", OPT_MYSQL_PROTOCOL,
 
106
   "The protocol of connection (tcp,socket,pipe,memory).",
 
107
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 
108
#ifdef HAVE_SMEM
 
109
  {"shared-memory-base-name", OPT_SHARED_MEMORY_BASE_NAME,
 
110
   "Base name of shared memory.", 0,
 
111
   0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 
112
#endif
 
113
  {"socket", 'S', "Socket file to use for connection.",
 
114
   0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 
115
  {"user", 'u', "User for login if not current user.", (uchar**) &opt_user,
 
116
   (uchar**) &opt_user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
 
117
  {"verbose", 'v', "Display more output about the process",
 
118
   (uchar**) &opt_verbose, (uchar**) &opt_verbose, 0,
 
119
   GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
 
120
  {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
 
121
};
 
122
 
 
123
#include <help_end.h>
 
124
 
 
125
 
 
126
static void free_used_memory(void)
 
127
{
 
128
  /* Free memory allocated by 'load_defaults' */
 
129
  free_defaults(defaults_argv);
 
130
 
 
131
  dynstr_free(&ds_args);
 
132
}
 
133
 
 
134
 
 
135
static void die(const char *fmt, ...)
 
136
{
 
137
  va_list args;
 
138
  DBUG_ENTER("die");
 
139
 
 
140
  /* Print the error message */
 
141
  va_start(args, fmt);
 
142
  if (fmt)
 
143
  {
 
144
    fprintf(stderr, "FATAL ERROR: ");
 
145
    vfprintf(stderr, fmt, args);
 
146
    fprintf(stderr, "\n");
 
147
    fflush(stderr);
 
148
  }
 
149
  va_end(args);
 
150
 
 
151
  free_used_memory();
 
152
  my_end(my_end_arg);
 
153
  exit(1);
 
154
}
 
155
 
 
156
 
 
157
static void verbose(const char *fmt, ...)
 
158
{
 
159
  va_list args;
 
160
 
 
161
  if (!opt_verbose)
 
162
    return;
 
163
 
 
164
  /* Print the verbose message */
 
165
  va_start(args, fmt);
 
166
  if (fmt)
 
167
  {
 
168
    vfprintf(stdout, fmt, args);
 
169
    fprintf(stdout, "\n");
 
170
    fflush(stdout);
 
171
  }
 
172
  va_end(args);
 
173
}
 
174
 
 
175
 
 
176
/*
 
177
  Add one option - passed to mysql_upgrade on command line
 
178
  or by defaults file(my.cnf) - to a dynamic string, in
 
179
  this way we pass the same arguments on to mysql and mysql_check
 
180
*/
 
181
 
 
182
static void add_one_option(DYNAMIC_STRING* ds,
 
183
                           const struct my_option *opt,
 
184
                           const char* argument)
 
185
 
 
186
{
 
187
  const char* eq= NullS;
 
188
  const char* arg= NullS;
 
189
  if (opt->arg_type != NO_ARG)
 
190
  {
 
191
    eq= "=";
 
192
    switch (opt->var_type & GET_TYPE_MASK) {
 
193
    case GET_STR:
 
194
      arg= argument;
 
195
      break;
 
196
    default:
 
197
      die("internal error at %s: %d",__FILE__, __LINE__);
 
198
    }
 
199
  }
 
200
  dynstr_append_os_quoted(ds, "--", opt->name, eq, arg, NullS);
 
201
  dynstr_append(&ds_args, " ");
 
202
}
 
203
 
 
204
 
 
205
static my_bool
 
206
get_one_option(int optid, const struct my_option *opt,
 
207
               char *argument)
 
208
{
 
209
  my_bool add_option= TRUE;
 
210
 
 
211
  switch (optid) {
 
212
 
 
213
  case '?':
 
214
    printf("%s  Ver %s Distrib %s, for %s (%s)\n",
 
215
           my_progname, VER, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE);
 
216
    puts("MySQL utility for upgrading databases to new MySQL versions\n");
 
217
    my_print_help(my_long_options);
 
218
    exit(0);
 
219
    break;
 
220
 
 
221
  case '#':
 
222
    DBUG_PUSH(argument ? argument : default_dbug_option);
 
223
    add_option= FALSE;
 
224
    debug_check_flag= 1;
 
225
    break;
 
226
 
 
227
  case 'p':
 
228
    tty_password= 1;
 
229
    add_option= FALSE;
 
230
    if (argument)
 
231
    {
 
232
      /* Add password to ds_args before overwriting the arg with x's */
 
233
      add_one_option(&ds_args, opt, argument);
 
234
      while (*argument)
 
235
        *argument++= 'x';                       /* Destroy argument */
 
236
      tty_password= 0;
 
237
    }
 
238
    break;
 
239
 
 
240
  case 'b': /* --basedir   */
 
241
  case 'v': /* --verbose   */
 
242
  case 'd': /* --datadir   */
 
243
  case 'f': /* --force     */
 
244
    add_option= FALSE;
 
245
    break;
 
246
  }
 
247
 
 
248
  if (add_option)
 
249
  {
 
250
    /*
 
251
      This is an option that is accpted by mysql_upgrade just so
 
252
      it can be passed on to "mysql" and "mysqlcheck"
 
253
      Save it in the ds_args string
 
254
    */
 
255
    add_one_option(&ds_args, opt, argument);
 
256
  }
 
257
  return 0;
 
258
}
 
259
 
 
260
 
 
261
static int run_command(char* cmd,
 
262
                       DYNAMIC_STRING *ds_res)
 
263
{
 
264
  char buf[512]= {0};
 
265
  FILE *res_file;
 
266
  int error;
 
267
 
 
268
  if (!(res_file= popen(cmd, "r")))
 
269
    die("popen(\"%s\", \"r\") failed", cmd);
 
270
 
 
271
  while (fgets(buf, sizeof(buf), res_file))
 
272
  {
 
273
    DBUG_PRINT("info", ("buf: %s", buf));
 
274
    if(ds_res)
 
275
    {
 
276
      /* Save the output of this command in the supplied string */
 
277
      dynstr_append(ds_res, buf);
 
278
    }
 
279
    else
 
280
    {
 
281
      /* Print it directly on screen */
 
282
      fprintf(stdout, "%s", buf);
 
283
    }
 
284
  }
 
285
 
 
286
  error= pclose(res_file);
 
287
  return WEXITSTATUS(error);
 
288
}
 
289
 
 
290
 
 
291
static int run_tool(char *tool_path, DYNAMIC_STRING *ds_res, ...)
 
292
{
 
293
  int ret;
 
294
  const char* arg;
 
295
  va_list args;
 
296
  DYNAMIC_STRING ds_cmdline;
 
297
 
 
298
  DBUG_ENTER("run_tool");
 
299
  DBUG_PRINT("enter", ("tool_path: %s", tool_path));
 
300
 
 
301
  if (init_dynamic_string(&ds_cmdline, IF_WIN("\"", ""), FN_REFLEN, FN_REFLEN))
 
302
    die("Out of memory");
 
303
 
 
304
  dynstr_append_os_quoted(&ds_cmdline, tool_path, NullS);
 
305
  dynstr_append(&ds_cmdline, " ");
 
306
 
 
307
  va_start(args, ds_res);
 
308
 
 
309
  while ((arg= va_arg(args, char *)))
 
310
  {
 
311
    /* Options should be os quoted */
 
312
    if (strncmp(arg, "--", 2) == 0)
 
313
      dynstr_append_os_quoted(&ds_cmdline, arg, NullS);
 
314
    else
 
315
      dynstr_append(&ds_cmdline, arg);
 
316
    dynstr_append(&ds_cmdline, " ");
 
317
  }
 
318
 
 
319
  va_end(args);
 
320
 
 
321
#ifdef __WIN__
 
322
  dynstr_append(&ds_cmdline, "\"");
 
323
#endif
 
324
 
 
325
  DBUG_PRINT("info", ("Running: %s", ds_cmdline.str));
 
326
  ret= run_command(ds_cmdline.str, ds_res);
 
327
  DBUG_PRINT("exit", ("ret: %d", ret));
 
328
  dynstr_free(&ds_cmdline);
 
329
  DBUG_RETURN(ret);
 
330
}
 
331
 
 
332
 
 
333
/*
 
334
  Try to get the full path to this exceutable
 
335
 
 
336
  Return 0 if path found
 
337
 
 
338
*/
 
339
 
 
340
static my_bool get_full_path_to_executable(char* path)
 
341
{
 
342
  my_bool ret;
 
343
  DBUG_ENTER("get_full_path_to_executable");
 
344
#ifdef __WIN__
 
345
  ret= (GetModuleFileName(NULL, path, FN_REFLEN) == 0);
 
346
#else
 
347
  /* my_readlink returns 0 if a symlink was read */
 
348
  ret= (my_readlink(path, "/proc/self/exe", MYF(0)) != 0);
 
349
  /* Might also want to try with /proc/$$/exe if the above fails */
 
350
#endif
 
351
  DBUG_PRINT("exit", ("path: %s", path));
 
352
  DBUG_RETURN(ret);
 
353
}
 
354
 
 
355
 
 
356
/*
 
357
  Look for the tool in the same directory as mysql_upgrade.
 
358
*/
 
359
 
 
360
static void find_tool(char *tool_path, const char *tool_name)
 
361
{
 
362
  size_t path_len;
 
363
  char path[FN_REFLEN];
 
364
  DYNAMIC_STRING ds_tmp;
 
365
  DBUG_ENTER("find_tool");
 
366
  DBUG_PRINT("enter", ("progname: %s", my_progname));
 
367
 
 
368
  if (init_dynamic_string(&ds_tmp, "", 32, 32))
 
369
    die("Out of memory");
 
370
 
 
371
  /* Initialize path with the full path to this program */
 
372
  if (get_full_path_to_executable(path))
 
373
  {
 
374
    /*
 
375
      Easy way to get full executable path failed, try
 
376
      other methods
 
377
    */
 
378
    if (my_progname[0] == FN_LIBCHAR)
 
379
    {
 
380
      /* 1. my_progname contains full path */
 
381
      strmake(path, my_progname, FN_REFLEN);
 
382
    }
 
383
    else if (my_progname[0] == '.')
 
384
    {
 
385
      /* 2. my_progname contains relative path, prepend wd */
 
386
      char buf[FN_REFLEN];
 
387
      my_getwd(buf, FN_REFLEN, MYF(0));
 
388
      my_snprintf(path, FN_REFLEN, "%s%s", buf, my_progname);
 
389
    }
 
390
    else
 
391
    {
 
392
      /* 3. Just go for it and hope tool is in path */
 
393
      path[0]= 0;
 
394
    }
 
395
  }
 
396
 
 
397
  DBUG_PRINT("info", ("path: '%s'", path));
 
398
 
 
399
  /* Chop off binary name (i.e mysql-upgrade) from path */
 
400
  dirname_part(path, path, &path_len);
 
401
 
 
402
  /*
 
403
    When running in a not yet installed build and using libtool,
 
404
    the program(mysql_upgrade) will be in .libs/ and executed
 
405
    through a libtool wrapper in order to use the dynamic libraries
 
406
    from this build. The same must be done for the tools(mysql and
 
407
    mysqlcheck). Thus if path ends in .libs/, step up one directory
 
408
    and execute the tools from there
 
409
  */
 
410
  path[max(path_len-1, 0)]= 0;   /* Chop off last / */
 
411
  if (strncmp(path + dirname_length(path), ".libs", 5) == 0)
 
412
  {
 
413
    DBUG_PRINT("info", ("Chopping off .libs from '%s'", path));
 
414
 
 
415
    /* Chop off .libs */
 
416
    dirname_part(path, path, &path_len);
 
417
  }
 
418
 
 
419
 
 
420
  DBUG_PRINT("info", ("path: '%s'", path));
 
421
 
 
422
  /* Format name of the tool to search for */
 
423
  fn_format(tool_path, tool_name,
 
424
            path, "", MYF(MY_REPLACE_DIR));
 
425
 
 
426
  verbose("Looking for '%s' in: %s", tool_name, tool_path);
 
427
 
 
428
  /* Make sure the tool exists */
 
429
  if (my_access(tool_path, F_OK) != 0)
 
430
    die("Can't find '%s'", tool_path);
 
431
 
 
432
  /*
 
433
    Make sure it can be executed
 
434
  */
 
435
  if (run_tool(tool_path,
 
436
               &ds_tmp, /* Get output from command, discard*/
 
437
               "--help",
 
438
               "2>&1",
 
439
               IF_WIN("> NUL", "> /dev/null"),
 
440
               NULL))
 
441
    die("Can't execute '%s'", tool_path);
 
442
 
 
443
  dynstr_free(&ds_tmp);
 
444
 
 
445
  DBUG_VOID_RETURN;
 
446
}
 
447
 
 
448
 
 
449
/*
 
450
  Run query using "mysql"
 
451
*/
 
452
 
 
453
static int run_query(const char *query, DYNAMIC_STRING *ds_res,
 
454
                     my_bool force)
 
455
{
 
456
  int ret;
 
457
  File fd;
 
458
  char query_file_path[FN_REFLEN];
 
459
  DBUG_ENTER("run_query");
 
460
  DBUG_PRINT("enter", ("query: %s", query));
 
461
  if ((fd= create_temp_file(query_file_path, NULL,
 
462
                            "sql", O_CREAT | O_SHARE | O_RDWR,
 
463
                            MYF(MY_WME))) < 0)
 
464
    die("Failed to create temporary file for defaults");
 
465
 
 
466
  if (my_write(fd, (uchar*) query, strlen(query),
 
467
               MYF(MY_FNABP | MY_WME)))
 
468
  {
 
469
    my_close(fd, MYF(0));
 
470
    my_delete(query_file_path, MYF(0));
 
471
    die("Failed to write to '%s'", query_file_path);
 
472
  }
 
473
 
 
474
  ret= run_tool(mysql_path,
 
475
                ds_res,
 
476
                "--no-defaults",
 
477
                ds_args.str,
 
478
                "--database=mysql",
 
479
                "--batch", /* Turns off pager etc. */
 
480
                force ? "--force": "--skip-force",
 
481
                ds_res ? "--silent": "",
 
482
                "<",
 
483
                query_file_path,
 
484
                "2>&1",
 
485
                NULL);
 
486
 
 
487
  my_close(fd, MYF(0));
 
488
  my_delete(query_file_path, MYF(0));
 
489
 
 
490
  DBUG_RETURN(ret);
 
491
}
 
492
 
 
493
 
 
494
/*
 
495
  Extract the value returned from result of "show variable like ..."
 
496
*/
 
497
 
 
498
static int extract_variable_from_show(DYNAMIC_STRING* ds, char* value)
 
499
{
 
500
  char *value_start, *value_end;
 
501
  /*
 
502
    The query returns "datadir\t<datadir>\n", skip past
 
503
    the tab
 
504
  */
 
505
  if ((value_start= strchr(ds->str, '\t')) == NULL)
 
506
    return 1; /* Unexpected result */
 
507
  value_start++;
 
508
 
 
509
  /* Don't copy the ending newline */
 
510
  if ((value_end= strchr(value_start, '\n')) == NULL)
 
511
    return 1; /* Unexpected result */
 
512
 
 
513
  strncpy(value, value_start, min(FN_REFLEN, value_end-value_start));
 
514
  return 0;
 
515
}
 
516
 
 
517
 
 
518
static int get_upgrade_info_file_name(char* name)
 
519
{
 
520
  DYNAMIC_STRING ds_datadir;
 
521
  DBUG_ENTER("get_upgrade_info_file_name");
 
522
 
 
523
  if (init_dynamic_string(&ds_datadir, NULL, 32, 32))
 
524
    die("Out of memory");
 
525
 
 
526
  if (run_query("show variables like 'datadir'",
 
527
                &ds_datadir, FALSE) ||
 
528
      extract_variable_from_show(&ds_datadir, name))
 
529
  {
 
530
    dynstr_free(&ds_datadir);
 
531
    DBUG_RETURN(1); /* Query failed */
 
532
  }
 
533
 
 
534
  dynstr_free(&ds_datadir);
 
535
 
 
536
  fn_format(name, "mysql_upgrade_info", name, "", MYF(0));
 
537
  DBUG_PRINT("exit", ("name: %s", name));
 
538
  DBUG_RETURN(0);
 
539
}
 
540
 
 
541
 
 
542
/*
 
543
  Read the content of mysql_upgrade_info file and
 
544
  compare the version number form file against
 
545
  version number wich mysql_upgrade was compiled for
 
546
 
 
547
  NOTE
 
548
  This is an optimization to avoid running mysql_upgrade
 
549
  when it's already been performed for the particular
 
550
  version of MySQL.
 
551
 
 
552
  In case the MySQL server can't return the upgrade info
 
553
  file it's always better to report that the upgrade hasn't
 
554
  been performed.
 
555
 
 
556
*/
 
557
 
 
558
static int upgrade_already_done(void)
 
559
{
 
560
  FILE *in;
 
561
  char upgrade_info_file[FN_REFLEN]= {0};
 
562
  char buf[sizeof(MYSQL_SERVER_VERSION)+1];
 
563
 
 
564
  if (get_upgrade_info_file_name(upgrade_info_file))
 
565
    return 0; /* Could not get filename => not sure */
 
566
 
 
567
  if (!(in= my_fopen(upgrade_info_file, O_RDONLY, MYF(0))))
 
568
    return 0; /* Could not open file => not sure */
 
569
 
 
570
  /*
 
571
    Read from file, don't care if it fails since it
 
572
    will be detected by the strncmp
 
573
  */
 
574
  bzero(buf, sizeof(buf));
 
575
  fgets(buf, sizeof(buf), in);
 
576
 
 
577
  my_fclose(in, MYF(0));
 
578
 
 
579
  return (strncmp(buf, MYSQL_SERVER_VERSION,
 
580
                  sizeof(MYSQL_SERVER_VERSION)-1)==0);
 
581
}
 
582
 
 
583
 
 
584
/*
 
585
  Write mysql_upgrade_info file in servers data dir indicating that
 
586
  upgrade has been done for this version
 
587
 
 
588
  NOTE
 
589
  This might very well fail but since it's just an optimization
 
590
  to run mysql_upgrade only when necessary the error can be
 
591
  ignored.
 
592
 
 
593
*/
 
594
 
 
595
static void create_mysql_upgrade_info_file(void)
 
596
{
 
597
  FILE *out;
 
598
  char upgrade_info_file[FN_REFLEN]= {0};
 
599
 
 
600
  if (get_upgrade_info_file_name(upgrade_info_file))
 
601
    return; /* Could not get filename => skip */
 
602
 
 
603
  if (!(out= my_fopen(upgrade_info_file, O_TRUNC | O_WRONLY, MYF(0))))
 
604
  {
 
605
    fprintf(stderr,
 
606
            "Could not create the upgrade info file '%s' in "
 
607
            "the MySQL Servers datadir, errno: %d\n",
 
608
            upgrade_info_file, errno);
 
609
    return;
 
610
  }
 
611
 
 
612
  /* Write new version to file */
 
613
  fputs(MYSQL_SERVER_VERSION, out);
 
614
  my_fclose(out, MYF(0));
 
615
 
 
616
  /*
 
617
    Check if the upgrad_info_file was properly created/updated
 
618
    It's not a fatal error -> just print a message if it fails
 
619
  */
 
620
  if (!upgrade_already_done())
 
621
    fprintf(stderr,
 
622
            "Could not write to the upgrade info file '%s' in "
 
623
            "the MySQL Servers datadir, errno: %d\n",
 
624
            upgrade_info_file, errno);
 
625
  return;
 
626
}
 
627
 
 
628
 
 
629
/*
 
630
  Check and upgrade(if neccessary) all tables
 
631
  in the server using "mysqlcheck --check-upgrade .."
 
632
*/
 
633
 
 
634
static int run_mysqlcheck_upgrade(void)
 
635
{
 
636
  verbose("Running 'mysqlcheck'...");
 
637
  return run_tool(mysqlcheck_path,
 
638
                  NULL, /* Send output from mysqlcheck directly to screen */
 
639
                  "--no-defaults",
 
640
                  ds_args.str,
 
641
                  "--check-upgrade",
 
642
                  "--all-databases",
 
643
                  "--auto-repair",
 
644
                  NULL);
 
645
}
 
646
 
 
647
 
 
648
static const char *expected_errors[]=
 
649
{
 
650
  "ERROR 1060", /* Duplicate column name */
 
651
  "ERROR 1061", /* Duplicate key name */
 
652
  "ERROR 1054", /* Unknown column */
 
653
  0
 
654
};
 
655
 
 
656
 
 
657
static my_bool is_expected_error(const char* line)
 
658
{
 
659
  const char** error= expected_errors;
 
660
  while (*error)
 
661
  {
 
662
    /*
 
663
      Check if lines starting with ERROR
 
664
      are in the list of expected errors
 
665
    */
 
666
    if (strncmp(line, "ERROR", 5) != 0 ||
 
667
        strncmp(line, *error, strlen(*error)) == 0)
 
668
      return 1; /* Found expected error */
 
669
    error++;
 
670
  }
 
671
  return 0;
 
672
}
 
673
 
 
674
 
 
675
static char* get_line(char* line)
 
676
{
 
677
  while (*line && *line != '\n')
 
678
    line++;
 
679
  if (*line)
 
680
    line++;
 
681
  return line;
 
682
}
 
683
 
 
684
 
 
685
/* Print the current line to stderr */
 
686
static void print_line(char* line)
 
687
{
 
688
  while (*line && *line != '\n')
 
689
  {
 
690
    fputc(*line, stderr);
 
691
    line++;
 
692
  }
 
693
  fputc('\n', stderr);
 
694
}
 
695
 
 
696
 
 
697
/*
 
698
  Update all system tables in MySQL Server to current
 
699
  version using "mysql" to execute all the SQL commands
 
700
  compiled into the mysql_fix_privilege_tables array
 
701
*/
 
702
 
 
703
static int run_sql_fix_privilege_tables(void)
 
704
{
 
705
  int found_real_errors= 0;
 
706
  DYNAMIC_STRING ds_result;
 
707
  DBUG_ENTER("run_sql_fix_privilege_tables");
 
708
 
 
709
  if (init_dynamic_string(&ds_result, "", 512, 512))
 
710
    die("Out of memory");
 
711
 
 
712
  verbose("Running 'mysql_fix_privilege_tables'...");
 
713
  run_query(mysql_fix_privilege_tables,
 
714
            &ds_result, /* Collect result */
 
715
            TRUE);
 
716
 
 
717
  {
 
718
    /*
 
719
      Scan each line of the result for real errors
 
720
      and ignore the expected one(s) like "Duplicate column name",
 
721
      "Unknown column" and "Duplicate key name" since they just
 
722
      indicate the system tables are already up to date
 
723
    */
 
724
    char *line= ds_result.str;
 
725
    do
 
726
    {
 
727
      if (!is_expected_error(line))
 
728
      {
 
729
        /* Something unexpected failed, dump error line to screen */
 
730
        found_real_errors++;
 
731
        print_line(line);
 
732
      }
 
733
    } while ((line= get_line(line)) && *line);
 
734
  }
 
735
 
 
736
  dynstr_free(&ds_result);
 
737
  return found_real_errors;
 
738
}
 
739
 
 
740
 
 
741
static const char *load_default_groups[]=
 
742
{
 
743
  "client", /* Read settings how to connect to server */
 
744
  "mysql_upgrade", /* Read special settings for mysql_upgrade*/
 
745
  0
 
746
};
 
747
 
 
748
 
 
749
int main(int argc, char **argv)
 
750
{
 
751
  MY_INIT(argv[0]);
 
752
#ifdef __NETWARE__
 
753
  setscreenmode(SCR_AUTOCLOSE_ON_EXIT);
 
754
#endif
 
755
 
 
756
  if (init_dynamic_string(&ds_args, "", 512, 256))
 
757
    die("Out of memory");
 
758
 
 
759
  load_defaults("my", load_default_groups, &argc, &argv);
 
760
  defaults_argv= argv; /* Must be freed by 'free_defaults' */
 
761
 
 
762
  if (handle_options(&argc, &argv, my_long_options, get_one_option))
 
763
    die(NULL);
 
764
  if (debug_info_flag)
 
765
    my_end_arg= MY_CHECK_ERROR | MY_GIVE_INFO;
 
766
  if (debug_check_flag)
 
767
    my_end_arg= MY_CHECK_ERROR;
 
768
 
 
769
  if (tty_password)
 
770
  {
 
771
    opt_password= get_tty_password(NullS);
 
772
    /* add password to defaults file */
 
773
    dynstr_append_os_quoted(&ds_args, "--password=", opt_password, NullS);
 
774
    dynstr_append(&ds_args, " ");
 
775
  }
 
776
  /* add user to defaults file */
 
777
  dynstr_append_os_quoted(&ds_args, "--user=", opt_user, NullS);
 
778
  dynstr_append(&ds_args, " ");
 
779
 
 
780
  /* Find mysql */
 
781
  find_tool(mysql_path, IF_WIN("mysql.exe", "mysql"));
 
782
 
 
783
  /* Find mysqlcheck */
 
784
  find_tool(mysqlcheck_path, IF_WIN("mysqlcheck.exe", "mysqlcheck"));
 
785
 
 
786
  /*
 
787
    Read the mysql_upgrade_info file to check if mysql_upgrade
 
788
    already has been run for this installation of MySQL
 
789
  */
 
790
  if (!opt_force && upgrade_already_done())
 
791
  {
 
792
    printf("This installation of MySQL is already upgraded to %s, "
 
793
           "use --force if you still need to run mysql_upgrade\n",
 
794
           MYSQL_SERVER_VERSION);
 
795
    die(NULL);
 
796
  }
 
797
 
 
798
  /*
 
799
    Run "mysqlcheck" and "mysql_fix_privilege_tables.sql"
 
800
  */
 
801
  if (run_mysqlcheck_upgrade() ||
 
802
      run_sql_fix_privilege_tables())
 
803
  {
 
804
    /*
 
805
      The upgrade failed to complete in some way or another,
 
806
      significant error message should have been printed to the screen
 
807
    */
 
808
    die("Upgrade failed" );
 
809
  }
 
810
  verbose("OK");
 
811
 
 
812
  /* Create a file indicating upgrade has been performed */
 
813
  create_mysql_upgrade_info_file();
 
814
 
 
815
  free_used_memory();
 
816
  my_end(my_end_arg);
 
817
  exit(0);
 
818
}
 
819