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