| 1 | /* |
| 2 | Copyright (c) 2006, 2013, Oracle and/or its affiliates. |
| 3 | Copyright (c) 2010, 2017, MariaDB |
| 4 | |
| 5 | This program is free software; you can redistribute it and/or modify |
| 6 | it under the terms of the GNU General Public License as published by |
| 7 | the Free Software Foundation; version 2 of the License. |
| 8 | |
| 9 | This program is distributed in the hope that it will be useful, |
| 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | GNU General Public License for more details. |
| 13 | |
| 14 | You should have received a copy of the GNU General Public License |
| 15 | along with this program; if not, write to the Free Software |
| 16 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| 17 | */ |
| 18 | |
| 19 | #include "client_priv.h" |
| 20 | #include <sslopt-vars.h> |
| 21 | #include "../scripts/mysql_fix_privilege_tables_sql.c" |
| 22 | |
| 23 | #include <welcome_copyright_notice.h> /* ORACLE_WELCOME_COPYRIGHT_NOTICE */ |
| 24 | |
| 25 | #define VER "1.4" |
| 26 | |
| 27 | #ifdef HAVE_SYS_WAIT_H |
| 28 | #include <sys/wait.h> |
| 29 | #endif |
| 30 | |
| 31 | #ifndef WEXITSTATUS |
| 32 | # ifdef __WIN__ |
| 33 | # define WEXITSTATUS(stat_val) (stat_val) |
| 34 | # else |
| 35 | # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) |
| 36 | # endif |
| 37 | #endif |
| 38 | |
| 39 | static int phase = 0; |
| 40 | static const int phases_total = 7; |
| 41 | static char mysql_path[FN_REFLEN]; |
| 42 | static char mysqlcheck_path[FN_REFLEN]; |
| 43 | |
| 44 | static my_bool opt_force, opt_verbose, debug_info_flag, debug_check_flag, |
| 45 | opt_systables_only, opt_version_check; |
| 46 | static my_bool opt_not_used, opt_silent; |
| 47 | static uint my_end_arg= 0; |
| 48 | static char *opt_user= (char*)"root" ; |
| 49 | |
| 50 | static my_bool upgrade_from_mysql; |
| 51 | |
| 52 | static DYNAMIC_STRING ds_args; |
| 53 | static DYNAMIC_STRING conn_args; |
| 54 | |
| 55 | static char *opt_password= 0; |
| 56 | static char *opt_plugin_dir= 0, *opt_default_auth= 0; |
| 57 | |
| 58 | static char *cnf_file_path= 0, defaults_file[FN_REFLEN + 32]; |
| 59 | |
| 60 | static my_bool tty_password= 0; |
| 61 | |
| 62 | static char opt_tmpdir[FN_REFLEN] = "" ; |
| 63 | |
| 64 | #ifndef DBUG_OFF |
| 65 | static char *default_dbug_option= (char*) "d:t:O,/tmp/mysql_upgrade.trace" ; |
| 66 | #endif |
| 67 | |
| 68 | static char **defaults_argv; |
| 69 | |
| 70 | static my_bool not_used; /* Can't use GET_BOOL without a value pointer */ |
| 71 | |
| 72 | char upgrade_from_version[sizeof("10.20.456-MariaDB" )+1]; |
| 73 | |
| 74 | static my_bool opt_write_binlog; |
| 75 | |
| 76 | #define OPT_SILENT OPT_MAX_CLIENT_OPTION |
| 77 | |
| 78 | static struct my_option my_long_options[]= |
| 79 | { |
| 80 | {"help" , '?', "Display this help message and exit." , 0, 0, 0, GET_NO_ARG, |
| 81 | NO_ARG, 0, 0, 0, 0, 0, 0}, |
| 82 | {"basedir" , 'b', |
| 83 | "Not used by mysql_upgrade. Only for backward compatibility." , |
| 84 | 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
| 85 | {"character-sets-dir" , OPT_CHARSETS_DIR, |
| 86 | "Not used by mysql_upgrade. Only for backward compatibility." , |
| 87 | 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, |
| 88 | {"compress" , OPT_COMPRESS, |
| 89 | "Not used by mysql_upgrade. Only for backward compatibility." , |
| 90 | ¬_used, ¬_used, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, |
| 91 | {"datadir" , 'd', |
| 92 | "Not used by mysql_upgrade. Only for backward compatibility." , |
| 93 | 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
| 94 | #ifdef DBUG_OFF |
| 95 | {"debug" , '#', "This is a non-debug version. Catch this and exit." , |
| 96 | 0, 0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0}, |
| 97 | #else |
| 98 | {"debug" , '#', "Output debug log." , &default_dbug_option, |
| 99 | &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, |
| 100 | #endif |
| 101 | {"debug-check" , OPT_DEBUG_CHECK, "Check memory and open file usage at exit." , |
| 102 | &debug_check_flag, &debug_check_flag, |
| 103 | 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, |
| 104 | {"debug-info" , 'T', "Print some debug info at exit." , &debug_info_flag, |
| 105 | &debug_info_flag, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, |
| 106 | {"default-character-set" , OPT_DEFAULT_CHARSET, |
| 107 | "Not used by mysql_upgrade. Only for backward compatibility." , |
| 108 | 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
| 109 | {"default_auth" , OPT_DEFAULT_AUTH, |
| 110 | "Default authentication client-side plugin to use." , |
| 111 | &opt_default_auth, &opt_default_auth, 0, |
| 112 | GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
| 113 | {"force" , 'f', "Force execution of mysqlcheck even if mysql_upgrade " |
| 114 | "has already been executed for the current version of MySQL." , |
| 115 | &opt_force, &opt_force, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, |
| 116 | {"host" , 'h', "Connect to host." , 0, |
| 117 | 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
| 118 | #define PASSWORD_OPT 12 |
| 119 | {"password" , 'p', |
| 120 | "Password to use when connecting to server. If password is not given," |
| 121 | " it's solicited on the tty." , &opt_password,&opt_password, |
| 122 | 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, |
| 123 | #ifdef __WIN__ |
| 124 | {"pipe" , 'W', "Use named pipes to connect to server." , 0, 0, 0, |
| 125 | GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, |
| 126 | #endif |
| 127 | {"plugin_dir" , OPT_PLUGIN_DIR, "Directory for client-side plugins." , |
| 128 | &opt_plugin_dir, &opt_plugin_dir, 0, |
| 129 | GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
| 130 | {"port" , 'P', "Port number to use for connection or 0 for default to, in " |
| 131 | "order of preference, my.cnf, $MYSQL_TCP_PORT, " |
| 132 | #if MYSQL_PORT_DEFAULT == 0 |
| 133 | "/etc/services, " |
| 134 | #endif |
| 135 | "built-in default (" STRINGIFY_ARG(MYSQL_PORT) ")." , |
| 136 | 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
| 137 | {"protocol" , OPT_MYSQL_PROTOCOL, |
| 138 | "The protocol to use for connection (tcp, socket, pipe, memory)." , |
| 139 | 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
| 140 | #ifdef HAVE_SMEM |
| 141 | {"shared-memory-base-name" , OPT_SHARED_MEMORY_BASE_NAME, |
| 142 | "Base name of shared memory." , 0, |
| 143 | 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
| 144 | #endif |
| 145 | {"silent" , OPT_SILENT, "Print less information" , &opt_silent, |
| 146 | &opt_silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, |
| 147 | {"socket" , 'S', "The socket file to use for connection." , |
| 148 | 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
| 149 | #include <sslopt-longopts.h> |
| 150 | {"tmpdir" , 't', "Directory for temporary files." , |
| 151 | 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
| 152 | {"upgrade-system-tables" , 's', "Only upgrade the system tables in the mysql database. Tables in other databases are not checked or touched." , |
| 153 | &opt_systables_only, &opt_systables_only, 0, |
| 154 | GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, |
| 155 | #define USER_OPT (array_elements(my_long_options) - 6) |
| 156 | {"user" , 'u', "User for login if not current user." , &opt_user, |
| 157 | &opt_user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
| 158 | {"verbose" , 'v', "Display more output about the process; Using it twice will print connection argument; Using it 3 times will print out all CHECK, RENAME and ALTER TABLE during the check phase." , |
| 159 | &opt_not_used, &opt_not_used, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, |
| 160 | {"version" , 'V', "Output version information and exit." , 0, 0, 0, |
| 161 | GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, |
| 162 | {"version-check" , 'k', |
| 163 | "Run this program only if its \'server version\' " |
| 164 | "matches the version of the server to which it's connecting. " |
| 165 | "Note: the \'server version\' of the program is the version of the MariaDB " |
| 166 | "server with which it was built/distributed." , |
| 167 | &opt_version_check, &opt_version_check, 0, |
| 168 | GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, |
| 169 | {"write-binlog" , OPT_WRITE_BINLOG, "All commands including those " |
| 170 | "issued by mysqlcheck are written to the binary log." , |
| 171 | &opt_write_binlog, &opt_write_binlog, 0, GET_BOOL, NO_ARG, |
| 172 | 0, 0, 0, 0, 0, 0}, |
| 173 | {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} |
| 174 | }; |
| 175 | |
| 176 | |
| 177 | static const char *load_default_groups[]= |
| 178 | { |
| 179 | "client" , /* Read settings how to connect to server */ |
| 180 | "mysql_upgrade" , /* Read special settings for mysql_upgrade */ |
| 181 | "client-server" , /* Reads settings common between client & server */ |
| 182 | "client-mariadb" , /* Read mariadb unique client settings */ |
| 183 | 0 |
| 184 | }; |
| 185 | |
| 186 | static void free_used_memory(void) |
| 187 | { |
| 188 | /* Free memory allocated by 'load_defaults' */ |
| 189 | if (defaults_argv) |
| 190 | free_defaults(defaults_argv); |
| 191 | |
| 192 | dynstr_free(&ds_args); |
| 193 | dynstr_free(&conn_args); |
| 194 | if (cnf_file_path) |
| 195 | my_delete(cnf_file_path, MYF(MY_WME)); |
| 196 | } |
| 197 | |
| 198 | |
| 199 | static void die(const char *fmt, ...) |
| 200 | { |
| 201 | va_list args; |
| 202 | DBUG_ENTER("die" ); |
| 203 | |
| 204 | /* Print the error message */ |
| 205 | fflush(stdout); |
| 206 | va_start(args, fmt); |
| 207 | if (fmt) |
| 208 | { |
| 209 | fprintf(stderr, "FATAL ERROR: " ); |
| 210 | vfprintf(stderr, fmt, args); |
| 211 | fprintf(stderr, "\n" ); |
| 212 | fflush(stderr); |
| 213 | } |
| 214 | va_end(args); |
| 215 | |
| 216 | free_used_memory(); |
| 217 | my_end(my_end_arg); |
| 218 | exit(1); |
| 219 | } |
| 220 | |
| 221 | |
| 222 | static void verbose(const char *fmt, ...) |
| 223 | { |
| 224 | va_list args; |
| 225 | |
| 226 | if (opt_silent) |
| 227 | return; |
| 228 | |
| 229 | /* Print the verbose message */ |
| 230 | va_start(args, fmt); |
| 231 | if (fmt) |
| 232 | { |
| 233 | vfprintf(stdout, fmt, args); |
| 234 | fprintf(stdout, "\n" ); |
| 235 | fflush(stdout); |
| 236 | } |
| 237 | va_end(args); |
| 238 | } |
| 239 | |
| 240 | |
| 241 | /* |
| 242 | Add one option - passed to mysql_upgrade on command line |
| 243 | or by defaults file(my.cnf) - to a dynamic string, in |
| 244 | this way we pass the same arguments on to mysql and mysql_check |
| 245 | */ |
| 246 | |
| 247 | static void add_one_option_cmd_line(DYNAMIC_STRING *ds, |
| 248 | const struct my_option *opt, |
| 249 | const char* arg) |
| 250 | { |
| 251 | dynstr_append(ds, "--" ); |
| 252 | dynstr_append(ds, opt->name); |
| 253 | if (arg) |
| 254 | { |
| 255 | dynstr_append(ds, "=" ); |
| 256 | dynstr_append_os_quoted(ds, arg, NullS); |
| 257 | } |
| 258 | dynstr_append(ds, " " ); |
| 259 | } |
| 260 | |
| 261 | static void add_one_option_cnf_file(DYNAMIC_STRING *ds, |
| 262 | const struct my_option *opt, |
| 263 | const char* arg) |
| 264 | { |
| 265 | dynstr_append(ds, opt->name); |
| 266 | if (arg) |
| 267 | { |
| 268 | dynstr_append(ds, "=" ); |
| 269 | dynstr_append_os_quoted(ds, arg, NullS); |
| 270 | } |
| 271 | dynstr_append(ds, "\n" ); |
| 272 | } |
| 273 | |
| 274 | static my_bool |
| 275 | get_one_option(int optid, const struct my_option *opt, |
| 276 | char *argument) |
| 277 | { |
| 278 | my_bool add_option= TRUE; |
| 279 | |
| 280 | switch (optid) { |
| 281 | |
| 282 | case '?': |
| 283 | printf("%s Ver %s Distrib %s, for %s (%s)\n" , |
| 284 | my_progname, VER, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE); |
| 285 | puts(ORACLE_WELCOME_COPYRIGHT_NOTICE("2000" )); |
| 286 | puts("MariaDB utility for upgrading databases to new MariaDB versions." ); |
| 287 | print_defaults("my" , load_default_groups); |
| 288 | puts("" ); |
| 289 | my_print_help(my_long_options); |
| 290 | my_print_variables(my_long_options); |
| 291 | die(0); |
| 292 | break; |
| 293 | |
| 294 | case '#': |
| 295 | DBUG_PUSH(argument ? argument : default_dbug_option); |
| 296 | add_option= FALSE; |
| 297 | debug_check_flag= 1; |
| 298 | break; |
| 299 | |
| 300 | case 'p': |
| 301 | if (argument == disabled_my_option) |
| 302 | argument= (char*) "" ; /* Don't require password */ |
| 303 | add_option= FALSE; |
| 304 | if (argument) |
| 305 | { |
| 306 | /* Add password to ds_args before overwriting the arg with x's */ |
| 307 | add_one_option_cnf_file(&ds_args, opt, argument); |
| 308 | while (*argument) |
| 309 | *argument++= 'x'; /* Destroy argument */ |
| 310 | tty_password= 0; |
| 311 | } |
| 312 | else |
| 313 | tty_password= 1; |
| 314 | break; |
| 315 | |
| 316 | case 't': |
| 317 | strnmov(opt_tmpdir, argument, sizeof(opt_tmpdir)); |
| 318 | add_option= FALSE; |
| 319 | break; |
| 320 | |
| 321 | case 'b': /* --basedir */ |
| 322 | case 'd': /* --datadir */ |
| 323 | fprintf(stderr, "%s: the '--%s' option is always ignored\n" , |
| 324 | my_progname, optid == 'b' ? "basedir" : "datadir" ); |
| 325 | /* FALLTHROUGH */ |
| 326 | |
| 327 | case 'k': /* --version-check */ |
| 328 | case 'v': /* --verbose */ |
| 329 | opt_verbose++; |
| 330 | if (argument == disabled_my_option) |
| 331 | { |
| 332 | opt_verbose= 0; |
| 333 | opt_silent= 1; |
| 334 | } |
| 335 | add_option= 0; |
| 336 | break; |
| 337 | case 'V': |
| 338 | printf("%s Ver %s Distrib %s, for %s (%s)\n" , |
| 339 | my_progname, VER, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE); |
| 340 | die(0); |
| 341 | break; |
| 342 | case OPT_SILENT: |
| 343 | opt_verbose= 0; |
| 344 | add_option= 0; |
| 345 | break; |
| 346 | case 'f': /* --force */ |
| 347 | case 's': /* --upgrade-system-tables */ |
| 348 | case OPT_WRITE_BINLOG: /* --write-binlog */ |
| 349 | add_option= FALSE; |
| 350 | break; |
| 351 | |
| 352 | case 'h': /* --host */ |
| 353 | case 'W': /* --pipe */ |
| 354 | case 'P': /* --port */ |
| 355 | case 'S': /* --socket */ |
| 356 | case OPT_MYSQL_PROTOCOL: /* --protocol */ |
| 357 | case OPT_SHARED_MEMORY_BASE_NAME: /* --shared-memory-base-name */ |
| 358 | case OPT_PLUGIN_DIR: /* --plugin-dir */ |
| 359 | case OPT_DEFAULT_AUTH: /* --default-auth */ |
| 360 | add_one_option_cmd_line(&conn_args, opt, argument); |
| 361 | break; |
| 362 | } |
| 363 | |
| 364 | if (add_option) |
| 365 | { |
| 366 | /* |
| 367 | This is an option that is accepted by mysql_upgrade just so |
| 368 | it can be passed on to "mysql" and "mysqlcheck" |
| 369 | Save it in the ds_args string |
| 370 | */ |
| 371 | add_one_option_cnf_file(&ds_args, opt, argument); |
| 372 | } |
| 373 | return 0; |
| 374 | } |
| 375 | |
| 376 | |
| 377 | /** |
| 378 | Run a command using the shell, storing its output in the supplied dynamic |
| 379 | string. |
| 380 | */ |
| 381 | static int run_command(char* cmd, |
| 382 | DYNAMIC_STRING *ds_res) |
| 383 | { |
| 384 | char buf[512]= {0}; |
| 385 | FILE *res_file; |
| 386 | int error; |
| 387 | |
| 388 | if (opt_verbose >= 4) |
| 389 | puts(cmd); |
| 390 | |
| 391 | if (!(res_file= popen(cmd, "r" ))) |
| 392 | die("popen(\"%s\", \"r\") failed" , cmd); |
| 393 | |
| 394 | while (fgets(buf, sizeof(buf), res_file)) |
| 395 | { |
| 396 | DBUG_PRINT("info" , ("buf: %s" , buf)); |
| 397 | if(ds_res) |
| 398 | { |
| 399 | /* Save the output of this command in the supplied string */ |
| 400 | dynstr_append(ds_res, buf); |
| 401 | } |
| 402 | else |
| 403 | { |
| 404 | /* Print it directly on screen */ |
| 405 | fprintf(stdout, "%s" , buf); |
| 406 | } |
| 407 | } |
| 408 | |
| 409 | error= pclose(res_file); |
| 410 | return WEXITSTATUS(error); |
| 411 | } |
| 412 | |
| 413 | |
| 414 | static int run_tool(char *tool_path, DYNAMIC_STRING *ds_res, ...) |
| 415 | { |
| 416 | int ret; |
| 417 | const char* arg; |
| 418 | va_list args; |
| 419 | DYNAMIC_STRING ds_cmdline; |
| 420 | |
| 421 | DBUG_ENTER("run_tool" ); |
| 422 | DBUG_PRINT("enter" , ("tool_path: %s" , tool_path)); |
| 423 | |
| 424 | if (init_dynamic_string(&ds_cmdline, IF_WIN("\"" , "" ), FN_REFLEN, FN_REFLEN)) |
| 425 | die("Out of memory" ); |
| 426 | |
| 427 | dynstr_append_os_quoted(&ds_cmdline, tool_path, NullS); |
| 428 | dynstr_append(&ds_cmdline, " " ); |
| 429 | |
| 430 | va_start(args, ds_res); |
| 431 | |
| 432 | while ((arg= va_arg(args, char *))) |
| 433 | { |
| 434 | /* Options should already be os quoted */ |
| 435 | dynstr_append(&ds_cmdline, arg); |
| 436 | dynstr_append(&ds_cmdline, " " ); |
| 437 | } |
| 438 | |
| 439 | va_end(args); |
| 440 | |
| 441 | #ifdef __WIN__ |
| 442 | dynstr_append(&ds_cmdline, "\"" ); |
| 443 | #endif |
| 444 | |
| 445 | DBUG_PRINT("info" , ("Running: %s" , ds_cmdline.str)); |
| 446 | ret= run_command(ds_cmdline.str, ds_res); |
| 447 | DBUG_PRINT("exit" , ("ret: %d" , ret)); |
| 448 | dynstr_free(&ds_cmdline); |
| 449 | DBUG_RETURN(ret); |
| 450 | } |
| 451 | |
| 452 | |
| 453 | /** |
| 454 | Look for the filename of given tool, with the presumption that it is in the |
| 455 | same directory as mysql_upgrade and that the same executable-searching |
| 456 | mechanism will be used when we run our sub-shells with popen() later. |
| 457 | */ |
| 458 | static void find_tool(char *tool_executable_name, const char *tool_name, |
| 459 | const char *self_name) |
| 460 | { |
| 461 | char *last_fn_libchar; |
| 462 | DYNAMIC_STRING ds_tmp; |
| 463 | DBUG_ENTER("find_tool" ); |
| 464 | DBUG_PRINT("enter" , ("progname: %s" , my_progname)); |
| 465 | |
| 466 | if (init_dynamic_string(&ds_tmp, "" , 32, 32)) |
| 467 | die("Out of memory" ); |
| 468 | |
| 469 | last_fn_libchar= strrchr(self_name, FN_LIBCHAR); |
| 470 | |
| 471 | if (last_fn_libchar == NULL) |
| 472 | { |
| 473 | /* |
| 474 | mysql_upgrade was found by the shell searching the path. A sibling |
| 475 | next to us should be found the same way. |
| 476 | */ |
| 477 | strncpy(tool_executable_name, tool_name, FN_REFLEN); |
| 478 | } |
| 479 | else |
| 480 | { |
| 481 | int len; |
| 482 | |
| 483 | /* |
| 484 | mysql_upgrade was run absolutely or relatively. We can find a sibling |
| 485 | by replacing our name after the LIBCHAR with the new tool name. |
| 486 | */ |
| 487 | |
| 488 | /* |
| 489 | When running in a not yet installed build and using libtool, |
| 490 | the program(mysql_upgrade) will be in .libs/ and executed |
| 491 | through a libtool wrapper in order to use the dynamic libraries |
| 492 | from this build. The same must be done for the tools(mysql and |
| 493 | mysqlcheck). Thus if path ends in .libs/, step up one directory |
| 494 | and execute the tools from there |
| 495 | */ |
| 496 | if (((last_fn_libchar - 6) >= self_name) && |
| 497 | (strncmp(last_fn_libchar - 5, ".libs" , 5) == 0) && |
| 498 | (*(last_fn_libchar - 6) == FN_LIBCHAR)) |
| 499 | { |
| 500 | DBUG_PRINT("info" , ("Chopping off \".libs\" from end of path" )); |
| 501 | last_fn_libchar -= 6; |
| 502 | } |
| 503 | |
| 504 | len= (int)(last_fn_libchar - self_name); |
| 505 | |
| 506 | my_snprintf(tool_executable_name, FN_REFLEN, "%.*s%c%s" , |
| 507 | len, self_name, FN_LIBCHAR, tool_name); |
| 508 | } |
| 509 | |
| 510 | if (opt_verbose) |
| 511 | verbose("Looking for '%s' as: %s" , tool_name, tool_executable_name); |
| 512 | |
| 513 | /* |
| 514 | Make sure it can be executed |
| 515 | */ |
| 516 | if (run_tool(tool_executable_name, |
| 517 | &ds_tmp, /* Get output from command, discard*/ |
| 518 | "--no-defaults" , |
| 519 | "--help" , |
| 520 | "2>&1" , |
| 521 | IF_WIN("> NUL" , "> /dev/null" ), |
| 522 | NULL)) |
| 523 | die("Can't execute '%s'" , tool_executable_name); |
| 524 | |
| 525 | dynstr_free(&ds_tmp); |
| 526 | |
| 527 | DBUG_VOID_RETURN; |
| 528 | } |
| 529 | |
| 530 | |
| 531 | /* |
| 532 | Run query using "mysql" |
| 533 | */ |
| 534 | |
| 535 | static int run_query(const char *query, DYNAMIC_STRING *ds_res, |
| 536 | my_bool force) |
| 537 | { |
| 538 | int ret; |
| 539 | File fd; |
| 540 | char query_file_path[FN_REFLEN]; |
| 541 | #ifdef WITH_WSREP |
| 542 | /* |
| 543 | Strictly speaking, WITH_WSREP on the client only means that the |
| 544 | client was compiled with WSREP, it doesn't mean the server was, |
| 545 | so the server might not have WSREP_ON variable. |
| 546 | |
| 547 | But mysql_upgrade is tightly bound to a specific server version |
| 548 | anyway - it was mysql_fix_privilege_tables_sql script embedded |
| 549 | into its binary - so even if it won't assume anything about server |
| 550 | wsrep-ness, it won't be any less server-dependent. |
| 551 | */ |
| 552 | const uchar sql_log_bin[]= "SET SQL_LOG_BIN=0, WSREP_ON=OFF;" ; |
| 553 | #else |
| 554 | const uchar sql_log_bin[]= "SET SQL_LOG_BIN=0;" ; |
| 555 | #endif /* WITH_WSREP */ |
| 556 | |
| 557 | DBUG_ENTER("run_query" ); |
| 558 | DBUG_PRINT("enter" , ("query: %s" , query)); |
| 559 | if ((fd= create_temp_file(query_file_path, |
| 560 | opt_tmpdir[0] ? opt_tmpdir : NULL, |
| 561 | "sql" , O_SHARE, MYF(MY_WME))) < 0) |
| 562 | die("Failed to create temporary file for defaults" ); |
| 563 | |
| 564 | /* |
| 565 | Master and slave should be upgraded separately. All statements executed |
| 566 | by mysql_upgrade will not be binlogged. |
| 567 | 'SET SQL_LOG_BIN=0' is executed before any other statements. |
| 568 | */ |
| 569 | if (!opt_write_binlog) |
| 570 | { |
| 571 | if (my_write(fd, sql_log_bin, sizeof(sql_log_bin)-1, |
| 572 | MYF(MY_FNABP | MY_WME))) |
| 573 | { |
| 574 | my_close(fd, MYF(0)); |
| 575 | my_delete(query_file_path, MYF(0)); |
| 576 | die("Failed to write to '%s'" , query_file_path); |
| 577 | } |
| 578 | } |
| 579 | |
| 580 | if (my_write(fd, (uchar*) query, strlen(query), |
| 581 | MYF(MY_FNABP | MY_WME))) |
| 582 | { |
| 583 | my_close(fd, MYF(0)); |
| 584 | my_delete(query_file_path, MYF(0)); |
| 585 | die("Failed to write to '%s'" , query_file_path); |
| 586 | } |
| 587 | |
| 588 | ret= run_tool(mysql_path, |
| 589 | ds_res, |
| 590 | defaults_file, |
| 591 | "--database=mysql" , |
| 592 | "--batch" , /* Turns off pager etc. */ |
| 593 | force ? "--force" : "--skip-force" , |
| 594 | ds_res || opt_silent ? "--silent" : "" , |
| 595 | "<" , |
| 596 | query_file_path, |
| 597 | "2>&1" , |
| 598 | NULL); |
| 599 | |
| 600 | my_close(fd, MYF(0)); |
| 601 | my_delete(query_file_path, MYF(0)); |
| 602 | |
| 603 | DBUG_RETURN(ret); |
| 604 | } |
| 605 | |
| 606 | |
| 607 | /* |
| 608 | Extract the value returned from result of "show variable like ..." |
| 609 | */ |
| 610 | |
| 611 | static int (DYNAMIC_STRING* ds, char* value) |
| 612 | { |
| 613 | char *value_start, *value_end; |
| 614 | size_t len; |
| 615 | |
| 616 | /* |
| 617 | The query returns "datadir\t<datadir>\n", skip past |
| 618 | the tab |
| 619 | */ |
| 620 | if ((value_start= strchr(ds->str, '\t')) == NULL) |
| 621 | return 1; /* Unexpected result */ |
| 622 | value_start++; |
| 623 | |
| 624 | /* Don't copy the ending newline */ |
| 625 | if ((value_end= strchr(value_start, '\n')) == NULL) |
| 626 | return 1; /* Unexpected result */ |
| 627 | |
| 628 | len= (size_t) MY_MIN(FN_REFLEN, value_end-value_start); |
| 629 | strncpy(value, value_start, len); |
| 630 | value[len]= '\0'; |
| 631 | return 0; |
| 632 | } |
| 633 | |
| 634 | |
| 635 | static int get_upgrade_info_file_name(char* name) |
| 636 | { |
| 637 | DYNAMIC_STRING ds_datadir; |
| 638 | DBUG_ENTER("get_upgrade_info_file_name" ); |
| 639 | |
| 640 | if (init_dynamic_string(&ds_datadir, NULL, 32, 32)) |
| 641 | die("Out of memory" ); |
| 642 | |
| 643 | if (run_query("show variables like 'datadir'" , |
| 644 | &ds_datadir, FALSE) || |
| 645 | extract_variable_from_show(&ds_datadir, name)) |
| 646 | { |
| 647 | dynstr_free(&ds_datadir); |
| 648 | DBUG_RETURN(1); /* Query failed */ |
| 649 | } |
| 650 | |
| 651 | dynstr_free(&ds_datadir); |
| 652 | |
| 653 | fn_format(name, "mysql_upgrade_info" , name, "" , MYF(0)); |
| 654 | DBUG_PRINT("exit" , ("name: %s" , name)); |
| 655 | DBUG_RETURN(0); |
| 656 | } |
| 657 | |
| 658 | |
| 659 | /* |
| 660 | Read the content of mysql_upgrade_info file and |
| 661 | compare the version number form file against |
| 662 | version number which mysql_upgrade was compiled for |
| 663 | |
| 664 | NOTE |
| 665 | This is an optimization to avoid running mysql_upgrade |
| 666 | when it's already been performed for the particular |
| 667 | version of MySQL. |
| 668 | |
| 669 | In case the MySQL server can't return the upgrade info |
| 670 | file it's always better to report that the upgrade hasn't |
| 671 | been performed. |
| 672 | |
| 673 | */ |
| 674 | |
| 675 | static int upgrade_already_done(myf flags) |
| 676 | { |
| 677 | FILE *in; |
| 678 | char upgrade_info_file[FN_REFLEN]= {0}; |
| 679 | |
| 680 | if (get_upgrade_info_file_name(upgrade_info_file)) |
| 681 | return 0; /* Could not get filename => not sure */ |
| 682 | |
| 683 | if (!(in= my_fopen(upgrade_info_file, O_RDONLY, flags))) |
| 684 | return 0; /* Could not open file => not sure */ |
| 685 | |
| 686 | bzero(upgrade_from_version, sizeof(upgrade_from_version)); |
| 687 | if (!fgets(upgrade_from_version, sizeof(upgrade_from_version), in)) |
| 688 | { |
| 689 | /* Preserve errno for caller */ |
| 690 | int save_errno= errno; |
| 691 | (void) my_fclose(in, flags); |
| 692 | errno= save_errno; |
| 693 | return 0; |
| 694 | } |
| 695 | |
| 696 | if (my_fclose(in, flags)) |
| 697 | return 0; |
| 698 | |
| 699 | errno= 0; |
| 700 | return (strncmp(upgrade_from_version, MYSQL_SERVER_VERSION, |
| 701 | sizeof(MYSQL_SERVER_VERSION)-1)==0); |
| 702 | } |
| 703 | |
| 704 | |
| 705 | /* |
| 706 | Write mysql_upgrade_info file in servers data dir indicating that |
| 707 | upgrade has been done for this version |
| 708 | |
| 709 | NOTE |
| 710 | This might very well fail but since it's just an optimization |
| 711 | to run mysql_upgrade only when necessary the error can be |
| 712 | ignored. |
| 713 | |
| 714 | */ |
| 715 | |
| 716 | static void create_mysql_upgrade_info_file(void) |
| 717 | { |
| 718 | FILE *out; |
| 719 | char upgrade_info_file[FN_REFLEN]= {0}; |
| 720 | |
| 721 | if (get_upgrade_info_file_name(upgrade_info_file)) |
| 722 | return; /* Could not get filename => skip */ |
| 723 | |
| 724 | if (!(out= my_fopen(upgrade_info_file, O_TRUNC | O_WRONLY, MYF(0)))) |
| 725 | { |
| 726 | fprintf(stderr, |
| 727 | "Could not create the upgrade info file '%s' in " |
| 728 | "the MariaDB Servers datadir, errno: %d\n" , |
| 729 | upgrade_info_file, errno); |
| 730 | return; |
| 731 | } |
| 732 | |
| 733 | /* Write new version to file */ |
| 734 | my_fwrite(out, (uchar*) MYSQL_SERVER_VERSION, |
| 735 | sizeof(MYSQL_SERVER_VERSION), MY_WME); |
| 736 | my_fclose(out, MYF(MY_WME)); |
| 737 | |
| 738 | /* |
| 739 | Check if the upgrad_info_file was properly created/updated |
| 740 | It's not a fatal error -> just print a message if it fails |
| 741 | */ |
| 742 | if (!upgrade_already_done(MY_WME)) |
| 743 | fprintf(stderr, |
| 744 | "Upgrade file '%s' was not properly created. " |
| 745 | "Got error errno while checking file content: %d\n" , |
| 746 | upgrade_info_file, errno); |
| 747 | return; |
| 748 | } |
| 749 | |
| 750 | |
| 751 | /* |
| 752 | Print connection-related arguments. |
| 753 | */ |
| 754 | |
| 755 | static void print_conn_args(const char *tool_name) |
| 756 | { |
| 757 | if (opt_verbose < 2) |
| 758 | return; |
| 759 | if (conn_args.str[0]) |
| 760 | verbose("Running '%s' with connection arguments: %s" , tool_name, |
| 761 | conn_args.str); |
| 762 | else |
| 763 | verbose("Running '%s with default connection arguments" , tool_name); |
| 764 | } |
| 765 | |
| 766 | /* |
| 767 | Check and upgrade(if necessary) all tables |
| 768 | in the server using "mysqlcheck --check-upgrade .." |
| 769 | */ |
| 770 | |
| 771 | static int run_mysqlcheck_upgrade(my_bool mysql_db_only) |
| 772 | { |
| 773 | const char *what= mysql_db_only ? "mysql database" : "tables" ; |
| 774 | const char *arg1= mysql_db_only ? "--databases" : "--all-databases" ; |
| 775 | const char *arg2= mysql_db_only ? "mysql" : "--skip-database=mysql" ; |
| 776 | int retch; |
| 777 | if (opt_systables_only && !mysql_db_only) |
| 778 | { |
| 779 | verbose("Phase %d/%d: Checking and upgrading %s... Skipped" , |
| 780 | ++phase, phases_total, what); |
| 781 | return 0; |
| 782 | } |
| 783 | verbose("Phase %d/%d: Checking and upgrading %s" , ++phase, phases_total, what); |
| 784 | print_conn_args("mysqlcheck" ); |
| 785 | retch= run_tool(mysqlcheck_path, |
| 786 | NULL, /* Send output from mysqlcheck directly to screen */ |
| 787 | defaults_file, |
| 788 | "--check-upgrade" , |
| 789 | "--auto-repair" , |
| 790 | !opt_silent || opt_verbose >= 1 ? "--verbose" : "" , |
| 791 | opt_verbose >= 2 ? "--verbose" : "" , |
| 792 | opt_verbose >= 3 ? "--verbose" : "" , |
| 793 | opt_silent ? "--silent" : "" , |
| 794 | opt_write_binlog ? "--write-binlog" : "--skip-write-binlog" , |
| 795 | arg1, arg2, |
| 796 | "2>&1" , |
| 797 | NULL); |
| 798 | return retch; |
| 799 | } |
| 800 | |
| 801 | #define EVENTS_STRUCT_LEN 7000 |
| 802 | |
| 803 | static my_bool is_mysql() |
| 804 | { |
| 805 | my_bool ret= TRUE; |
| 806 | DYNAMIC_STRING ds_events_struct; |
| 807 | |
| 808 | if (init_dynamic_string(&ds_events_struct, NULL, |
| 809 | EVENTS_STRUCT_LEN, EVENTS_STRUCT_LEN)) |
| 810 | die("Out of memory" ); |
| 811 | |
| 812 | if (run_query("show create table mysql.event" , |
| 813 | &ds_events_struct, FALSE) || |
| 814 | strstr(ds_events_struct.str, "IGNORE_BAD_TABLE_OPTIONS" ) != NULL) |
| 815 | ret= FALSE; |
| 816 | else |
| 817 | verbose("MySQL upgrade detected" ); |
| 818 | |
| 819 | dynstr_free(&ds_events_struct); |
| 820 | return(ret); |
| 821 | } |
| 822 | |
| 823 | static int run_mysqlcheck_views(void) |
| 824 | { |
| 825 | const char *upgrade_views="--process-views=YES" ; |
| 826 | if (upgrade_from_mysql) |
| 827 | { |
| 828 | /* |
| 829 | this has to ignore opt_systables_only, because upgrade_from_mysql |
| 830 | is determined by analyzing systables. if we honor opt_systables_only |
| 831 | here, views won't be fixed by subsequent mysql_upgrade runs |
| 832 | */ |
| 833 | upgrade_views="--process-views=UPGRADE_FROM_MYSQL" ; |
| 834 | verbose("Phase %d/%d: Fixing views from mysql" , ++phase, phases_total); |
| 835 | } |
| 836 | else if (opt_systables_only) |
| 837 | { |
| 838 | verbose("Phase %d/%d: Fixing views... Skipped" , ++phase, phases_total); |
| 839 | return 0; |
| 840 | } |
| 841 | else |
| 842 | verbose("Phase %d/%d: Fixing views" , ++phase, phases_total); |
| 843 | |
| 844 | print_conn_args("mysqlcheck" ); |
| 845 | return run_tool(mysqlcheck_path, |
| 846 | NULL, /* Send output from mysqlcheck directly to screen */ |
| 847 | defaults_file, |
| 848 | "--all-databases" , "--repair" , |
| 849 | upgrade_views, |
| 850 | "--skip-process-tables" , |
| 851 | opt_verbose ? "--verbose" : "" , |
| 852 | opt_silent ? "--silent" : "" , |
| 853 | opt_write_binlog ? "--write-binlog" : "--skip-write-binlog" , |
| 854 | "2>&1" , |
| 855 | NULL); |
| 856 | } |
| 857 | |
| 858 | static int run_mysqlcheck_fixnames(void) |
| 859 | { |
| 860 | if (opt_systables_only) |
| 861 | { |
| 862 | verbose("Phase %d/%d: Fixing table and database names ... Skipped" , |
| 863 | ++phase, phases_total); |
| 864 | return 0; |
| 865 | } |
| 866 | verbose("Phase %d/%d: Fixing table and database names" , |
| 867 | ++phase, phases_total); |
| 868 | print_conn_args("mysqlcheck" ); |
| 869 | return run_tool(mysqlcheck_path, |
| 870 | NULL, /* Send output from mysqlcheck directly to screen */ |
| 871 | defaults_file, |
| 872 | "--all-databases" , |
| 873 | "--fix-db-names" , |
| 874 | "--fix-table-names" , |
| 875 | opt_verbose >= 1 ? "--verbose" : "" , |
| 876 | opt_verbose >= 2 ? "--verbose" : "" , |
| 877 | opt_verbose >= 3 ? "--verbose" : "" , |
| 878 | opt_silent ? "--silent" : "" , |
| 879 | opt_write_binlog ? "--write-binlog" : "--skip-write-binlog" , |
| 880 | "2>&1" , |
| 881 | NULL); |
| 882 | } |
| 883 | |
| 884 | |
| 885 | static const char *expected_errors[]= |
| 886 | { |
| 887 | "ERROR 1060" , /* Duplicate column name */ |
| 888 | "ERROR 1061" , /* Duplicate key name */ |
| 889 | "ERROR 1054" , /* Unknown column */ |
| 890 | "ERROR 1290" , /* RR_OPTION_PREVENTS_STATEMENT */ |
| 891 | 0 |
| 892 | }; |
| 893 | |
| 894 | |
| 895 | static my_bool is_expected_error(const char* line) |
| 896 | { |
| 897 | const char** error= expected_errors; |
| 898 | while (*error) |
| 899 | { |
| 900 | /* |
| 901 | Check if lines starting with ERROR |
| 902 | are in the list of expected errors |
| 903 | */ |
| 904 | if (strncmp(line, "ERROR" , 5) != 0 || |
| 905 | strncmp(line, *error, strlen(*error)) == 0) |
| 906 | return 1; /* Found expected error */ |
| 907 | error++; |
| 908 | } |
| 909 | return 0; |
| 910 | } |
| 911 | |
| 912 | |
| 913 | static char* get_line(char* line) |
| 914 | { |
| 915 | while (*line && *line != '\n') |
| 916 | line++; |
| 917 | if (*line) |
| 918 | line++; |
| 919 | return line; |
| 920 | } |
| 921 | |
| 922 | |
| 923 | /* Print the current line to stderr */ |
| 924 | static void print_line(char* line) |
| 925 | { |
| 926 | while (*line && *line != '\n') |
| 927 | { |
| 928 | fputc(*line, stderr); |
| 929 | line++; |
| 930 | } |
| 931 | fputc('\n', stderr); |
| 932 | } |
| 933 | |
| 934 | static my_bool from_before_10_1() |
| 935 | { |
| 936 | my_bool ret= TRUE; |
| 937 | DYNAMIC_STRING ds_events_struct; |
| 938 | |
| 939 | if (upgrade_from_version[0]) |
| 940 | { |
| 941 | return upgrade_from_version[1] == '.' || |
| 942 | strncmp(upgrade_from_version, "10.1." , 5) < 0; |
| 943 | } |
| 944 | |
| 945 | if (init_dynamic_string(&ds_events_struct, NULL, 2048, 2048)) |
| 946 | die("Out of memory" ); |
| 947 | |
| 948 | if (run_query("show create table mysql.user" , &ds_events_struct, FALSE) || |
| 949 | strstr(ds_events_struct.str, "default_role" ) != NULL) |
| 950 | ret= FALSE; |
| 951 | else |
| 952 | verbose("Upgrading from a version before MariaDB-10.1" ); |
| 953 | |
| 954 | dynstr_free(&ds_events_struct); |
| 955 | return ret; |
| 956 | } |
| 957 | |
| 958 | |
| 959 | /* |
| 960 | Check for entries with "Unknown storage engine" in I_S.TABLES, |
| 961 | try to load plugins for these tables if available (MDEV-11942) |
| 962 | */ |
| 963 | static int install_used_engines(void) |
| 964 | { |
| 965 | char buf[512]; |
| 966 | DYNAMIC_STRING ds_result; |
| 967 | const char *query = "SELECT DISTINCT LOWER(engine) AS c1 FROM information_schema.tables" |
| 968 | " WHERE table_comment LIKE 'Unknown storage engine%'" |
| 969 | " ORDER BY c1" ; |
| 970 | |
| 971 | if (opt_systables_only || !from_before_10_1()) |
| 972 | { |
| 973 | verbose("Phase %d/%d: Installing used storage engines... Skipped" , ++phase, phases_total); |
| 974 | return 0; |
| 975 | } |
| 976 | verbose("Phase %d/%d: Installing used storage engines" , ++phase, phases_total); |
| 977 | |
| 978 | if (init_dynamic_string(&ds_result, "" , 512, 512)) |
| 979 | die("Out of memory" ); |
| 980 | |
| 981 | verbose("Checking for tables with unknown storage engine" ); |
| 982 | |
| 983 | run_query(query, &ds_result, TRUE); |
| 984 | |
| 985 | if (ds_result.length) |
| 986 | { |
| 987 | char *line= ds_result.str, *next=get_line(line); |
| 988 | do |
| 989 | { |
| 990 | if (next[-1] == '\n') |
| 991 | next[-1]=0; |
| 992 | |
| 993 | verbose("installing plugin for '%s' storage engine" , line); |
| 994 | |
| 995 | // we simply assume soname=ha_enginename |
| 996 | strxnmov(buf, sizeof(buf)-1, "install soname 'ha_" , line, "'" , NULL); |
| 997 | |
| 998 | |
| 999 | if (run_query(buf, NULL, TRUE)) |
| 1000 | fprintf(stderr, "... can't %s\n" , buf); |
| 1001 | line=next; |
| 1002 | next=get_line(line); |
| 1003 | } while (*line); |
| 1004 | } |
| 1005 | dynstr_free(&ds_result); |
| 1006 | return 0; |
| 1007 | } |
| 1008 | |
| 1009 | |
| 1010 | /* |
| 1011 | Update all system tables in MySQL Server to current |
| 1012 | version using "mysql" to execute all the SQL commands |
| 1013 | compiled into the mysql_fix_privilege_tables array |
| 1014 | */ |
| 1015 | |
| 1016 | static int run_sql_fix_privilege_tables(void) |
| 1017 | { |
| 1018 | int found_real_errors= 0; |
| 1019 | const char **query_ptr; |
| 1020 | DYNAMIC_STRING ds_script; |
| 1021 | DYNAMIC_STRING ds_result; |
| 1022 | DBUG_ENTER("run_sql_fix_privilege_tables" ); |
| 1023 | |
| 1024 | if (init_dynamic_string(&ds_script, "" , 65536, 1024)) |
| 1025 | die("Out of memory" ); |
| 1026 | |
| 1027 | if (init_dynamic_string(&ds_result, "" , 512, 512)) |
| 1028 | die("Out of memory" ); |
| 1029 | |
| 1030 | verbose("Phase %d/%d: Running 'mysql_fix_privilege_tables'" , |
| 1031 | ++phase, phases_total); |
| 1032 | |
| 1033 | /* |
| 1034 | Individual queries can not be executed independently by invoking |
| 1035 | a forked mysql client, because the script uses session variables |
| 1036 | and prepared statements. |
| 1037 | */ |
| 1038 | for ( query_ptr= &mysql_fix_privilege_tables[0]; |
| 1039 | *query_ptr != NULL; |
| 1040 | query_ptr++ |
| 1041 | ) |
| 1042 | { |
| 1043 | if (strcasecmp(*query_ptr, "flush privileges;\n" )) |
| 1044 | dynstr_append(&ds_script, *query_ptr); |
| 1045 | } |
| 1046 | |
| 1047 | run_query(ds_script.str, |
| 1048 | &ds_result, /* Collect result */ |
| 1049 | TRUE); |
| 1050 | |
| 1051 | { |
| 1052 | /* |
| 1053 | Scan each line of the result for real errors |
| 1054 | and ignore the expected one(s) like "Duplicate column name", |
| 1055 | "Unknown column" and "Duplicate key name" since they just |
| 1056 | indicate the system tables are already up to date |
| 1057 | */ |
| 1058 | char *line= ds_result.str; |
| 1059 | do |
| 1060 | { |
| 1061 | if (!is_expected_error(line)) |
| 1062 | { |
| 1063 | /* Something unexpected failed, dump error line to screen */ |
| 1064 | found_real_errors++; |
| 1065 | print_line(line); |
| 1066 | } |
| 1067 | else if (strncmp(line, "WARNING" , 7) == 0) |
| 1068 | { |
| 1069 | print_line(line); |
| 1070 | } |
| 1071 | } while ((line= get_line(line)) && *line); |
| 1072 | } |
| 1073 | |
| 1074 | dynstr_free(&ds_result); |
| 1075 | dynstr_free(&ds_script); |
| 1076 | DBUG_RETURN(found_real_errors); |
| 1077 | } |
| 1078 | |
| 1079 | |
| 1080 | static void print_error(const char *error_msg, DYNAMIC_STRING *output) |
| 1081 | { |
| 1082 | fprintf(stderr, "%s\n" , error_msg); |
| 1083 | fprintf(stderr, "%s" , output->str); |
| 1084 | } |
| 1085 | |
| 1086 | |
| 1087 | /* Convert the specified version string into the numeric format. */ |
| 1088 | static ulong STDCALL calc_server_version(char *some_version) |
| 1089 | { |
| 1090 | uint major, minor, version; |
| 1091 | char *point= some_version, *end_point; |
| 1092 | major= (uint) strtoul(point, &end_point, 10); point=end_point+1; |
| 1093 | minor= (uint) strtoul(point, &end_point, 10); point=end_point+1; |
| 1094 | version= (uint) strtoul(point, &end_point, 10); |
| 1095 | return (ulong) major * 10000L + (ulong)(minor * 100 + version); |
| 1096 | } |
| 1097 | |
| 1098 | /** |
| 1099 | Check if the server version matches with the server version mysql_upgrade |
| 1100 | was compiled with. |
| 1101 | |
| 1102 | @return 0 match successful |
| 1103 | 1 failed |
| 1104 | */ |
| 1105 | static int check_version_match(void) |
| 1106 | { |
| 1107 | DYNAMIC_STRING ds_version; |
| 1108 | char version_str[NAME_CHAR_LEN + 1]; |
| 1109 | |
| 1110 | if (init_dynamic_string(&ds_version, NULL, NAME_CHAR_LEN, NAME_CHAR_LEN)) |
| 1111 | die("Out of memory" ); |
| 1112 | |
| 1113 | if (run_query("show variables like 'version'" , |
| 1114 | &ds_version, FALSE) || |
| 1115 | extract_variable_from_show(&ds_version, version_str)) |
| 1116 | { |
| 1117 | print_error("Version check failed. Got the following error when calling " |
| 1118 | "the 'mysql' command line client" , &ds_version); |
| 1119 | dynstr_free(&ds_version); |
| 1120 | return 1; /* Query failed */ |
| 1121 | } |
| 1122 | |
| 1123 | dynstr_free(&ds_version); |
| 1124 | |
| 1125 | if (calc_server_version((char *) version_str) != MYSQL_VERSION_ID) |
| 1126 | { |
| 1127 | fprintf(stderr, "Error: Server version (%s) does not match with the " |
| 1128 | "version of\nthe server (%s) with which this program was built/" |
| 1129 | "distributed. You can\nuse --skip-version-check to skip this " |
| 1130 | "check.\n" , version_str, MYSQL_SERVER_VERSION); |
| 1131 | return 1; |
| 1132 | } |
| 1133 | else |
| 1134 | return 0; |
| 1135 | } |
| 1136 | |
| 1137 | |
| 1138 | int main(int argc, char **argv) |
| 1139 | { |
| 1140 | char self_name[FN_REFLEN + 1]; |
| 1141 | |
| 1142 | MY_INIT(argv[0]); |
| 1143 | load_defaults_or_exit("my" , load_default_groups, &argc, &argv); |
| 1144 | defaults_argv= argv; /* Must be freed by 'free_defaults' */ |
| 1145 | |
| 1146 | #if defined(__WIN__) |
| 1147 | if (GetModuleFileName(NULL, self_name, FN_REFLEN) == 0) |
| 1148 | #endif |
| 1149 | { |
| 1150 | strmake_buf(self_name, argv[0]); |
| 1151 | } |
| 1152 | |
| 1153 | if (init_dynamic_string(&ds_args, "" , 512, 256) || |
| 1154 | init_dynamic_string(&conn_args, "" , 512, 256)) |
| 1155 | die("Out of memory" ); |
| 1156 | |
| 1157 | if (handle_options(&argc, &argv, my_long_options, get_one_option)) |
| 1158 | die(NULL); |
| 1159 | if (debug_info_flag) |
| 1160 | my_end_arg= MY_CHECK_ERROR | MY_GIVE_INFO; |
| 1161 | if (debug_check_flag) |
| 1162 | my_end_arg= MY_CHECK_ERROR; |
| 1163 | |
| 1164 | if (tty_password) |
| 1165 | { |
| 1166 | opt_password= get_tty_password(NullS); |
| 1167 | /* add password to defaults file */ |
| 1168 | add_one_option_cnf_file(&ds_args, &my_long_options[PASSWORD_OPT], opt_password); |
| 1169 | DBUG_ASSERT(strcmp(my_long_options[PASSWORD_OPT].name, "password" ) == 0); |
| 1170 | } |
| 1171 | /* add user to defaults file */ |
| 1172 | add_one_option_cnf_file(&ds_args, &my_long_options[USER_OPT], opt_user); |
| 1173 | DBUG_ASSERT(strcmp(my_long_options[USER_OPT].name, "user" ) == 0); |
| 1174 | |
| 1175 | cnf_file_path= strmov(defaults_file, "--defaults-file=" ); |
| 1176 | { |
| 1177 | int fd= create_temp_file(cnf_file_path, opt_tmpdir[0] ? opt_tmpdir : NULL, |
| 1178 | "mysql_upgrade-" , 0, MYF(MY_FAE)); |
| 1179 | if (fd < 0) |
| 1180 | die(NULL); |
| 1181 | my_write(fd, USTRING_WITH_LEN( "[client]\n" ), MYF(MY_FAE)); |
| 1182 | my_write(fd, (uchar*)ds_args.str, ds_args.length, MYF(MY_FAE)); |
| 1183 | my_close(fd, MYF(0)); |
| 1184 | } |
| 1185 | |
| 1186 | /* Find mysql */ |
| 1187 | find_tool(mysql_path, IF_WIN("mysql.exe" , "mysql" ), self_name); |
| 1188 | |
| 1189 | /* Find mysqlcheck */ |
| 1190 | find_tool(mysqlcheck_path, IF_WIN("mysqlcheck.exe" , "mysqlcheck" ), self_name); |
| 1191 | |
| 1192 | if (opt_systables_only && !opt_silent) |
| 1193 | printf("The --upgrade-system-tables option was used, user tables won't be touched.\n" ); |
| 1194 | |
| 1195 | /* |
| 1196 | Read the mysql_upgrade_info file to check if mysql_upgrade |
| 1197 | already has been run for this installation of MySQL |
| 1198 | */ |
| 1199 | if (!opt_force && upgrade_already_done(0)) |
| 1200 | { |
| 1201 | printf("This installation of MySQL is already upgraded to %s, " |
| 1202 | "use --force if you still need to run mysql_upgrade\n" , |
| 1203 | MYSQL_SERVER_VERSION); |
| 1204 | goto end; |
| 1205 | } |
| 1206 | |
| 1207 | if (opt_version_check && check_version_match()) |
| 1208 | die("Upgrade failed" ); |
| 1209 | |
| 1210 | upgrade_from_mysql= is_mysql(); |
| 1211 | |
| 1212 | /* |
| 1213 | Run "mysqlcheck" and "mysql_fix_privilege_tables.sql" |
| 1214 | */ |
| 1215 | if (run_mysqlcheck_upgrade(TRUE) || |
| 1216 | install_used_engines() || |
| 1217 | run_mysqlcheck_views() || |
| 1218 | run_sql_fix_privilege_tables() || |
| 1219 | run_mysqlcheck_fixnames() || |
| 1220 | run_mysqlcheck_upgrade(FALSE)) |
| 1221 | die("Upgrade failed" ); |
| 1222 | |
| 1223 | verbose("Phase %d/%d: Running 'FLUSH PRIVILEGES'" , ++phase, phases_total); |
| 1224 | if (run_query("FLUSH PRIVILEGES" , NULL, TRUE)) |
| 1225 | die("Upgrade failed" ); |
| 1226 | |
| 1227 | verbose("OK" ); |
| 1228 | |
| 1229 | /* Create a file indicating upgrade has been performed */ |
| 1230 | create_mysql_upgrade_info_file(); |
| 1231 | |
| 1232 | DBUG_ASSERT(phase == phases_total); |
| 1233 | |
| 1234 | end: |
| 1235 | free_used_memory(); |
| 1236 | my_end(my_end_arg); |
| 1237 | exit(0); |
| 1238 | } |
| 1239 | |