diff options
Diffstat (limited to 'client/mysql_upgrade.c')
-rw-r--r-- | client/mysql_upgrade.c | 322 |
1 files changed, 224 insertions, 98 deletions
diff --git a/client/mysql_upgrade.c b/client/mysql_upgrade.c index a13365d8dbf..1cc1edd9e9e 100644 --- a/client/mysql_upgrade.c +++ b/client/mysql_upgrade.c @@ -22,7 +22,7 @@ #include <welcome_copyright_notice.h> /* ORACLE_WELCOME_COPYRIGHT_NOTICE */ -#define VER "1.4" +#define VER "2.0" #ifdef HAVE_SYS_WAIT_H #include <sys/wait.h> @@ -37,13 +37,15 @@ #endif static int phase = 0; +static int info_file= -1; static const int phases_total = 7; static char mysql_path[FN_REFLEN]; static char mysqlcheck_path[FN_REFLEN]; -static my_bool opt_force, opt_verbose, debug_info_flag, debug_check_flag, +static my_bool debug_info_flag, debug_check_flag, opt_systables_only, opt_version_check; -static my_bool opt_not_used, opt_silent; +static my_bool opt_not_used, opt_silent, opt_check_upgrade; +static uint opt_force, opt_verbose; static uint my_end_arg= 0; static char *opt_user= (char*)"root"; @@ -69,7 +71,7 @@ static char **defaults_argv; static my_bool not_used; /* Can't use GET_BOOL without a value pointer */ -char upgrade_from_version[sizeof("10.20.456-MariaDB")+1]; +char upgrade_from_version[sizeof("10.20.456-MariaDB")+30]; static my_bool opt_write_binlog; @@ -95,8 +97,8 @@ static struct my_option my_long_options[]= {"debug", '#', "This is a non-debug version. Catch this and exit.", 0, 0, 0, GET_DISABLED, OPT_ARG, 0, 0, 0, 0, 0, 0}, #else - {"debug", '#', "Output debug log.", &default_dbug_option, - &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, + {"debug", '#', "Output debug log.", + 0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, #endif {"debug-check", OPT_DEBUG_CHECK, "Check memory and open file usage at exit.", &debug_check_flag, &debug_check_flag, @@ -110,9 +112,13 @@ static struct my_option my_long_options[]= "Default authentication client-side plugin to use.", &opt_default_auth, &opt_default_auth, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"check-if-upgrade-is-needed", OPT_CHECK_IF_UPGRADE_NEEDED, + "Exits with status 0 if an upgrades is required, 1 otherwise.", + &opt_check_upgrade, &opt_check_upgrade, + 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"force", 'f', "Force execution of mysqlcheck even if mysql_upgrade " - "has already been executed for the current version of MySQL.", - &opt_force, &opt_force, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + "has already been executed for the current version of MariaDB.", + &opt_not_used, &opt_not_used, 0 , GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, {"host", 'h', "Connect to host.", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, #define PASSWORD_OPT 12 @@ -193,6 +199,12 @@ static void free_used_memory(void) dynstr_free(&conn_args); if (cnf_file_path) my_delete(cnf_file_path, MYF(MY_WME)); + if (info_file >= 0) + { + (void) my_lock(info_file, F_UNLCK, 0, 1, MYF(0)); + my_close(info_file, MYF(MY_WME)); + info_file= -1; + } } @@ -238,6 +250,13 @@ static void verbose(const char *fmt, ...) } +static void print_error(const char *error_msg, DYNAMIC_STRING *output) +{ + fprintf(stderr, "%s\n", error_msg); + fprintf(stderr, "%s", output->str); +} + + /* Add one option - passed to mysql_upgrade on command line or by defaults file(my.cnf) - to a dynamic string, in @@ -271,6 +290,7 @@ static void add_one_option_cnf_file(DYNAMIC_STRING *ds, dynstr_append(ds, "\n"); } + static my_bool get_one_option(int optid, const struct my_option *opt, char *argument) @@ -339,11 +359,17 @@ get_one_option(int optid, const struct my_option *opt, my_progname, VER, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE); die(0); break; + case 'f': /* --force */ + opt_force++; + if (argument == disabled_my_option) + opt_force= 0; + add_option= 0; + break; case OPT_SILENT: opt_verbose= 0; add_option= 0; break; - case 'f': /* --force */ + case OPT_CHECK_IF_UPGRADE_NEEDED: /* --check-if-upgrade-needed */ case 's': /* --upgrade-system-tables */ case OPT_WRITE_BINLOG: /* --write-binlog */ add_option= FALSE; @@ -374,6 +400,18 @@ get_one_option(int optid, const struct my_option *opt, } +/* Convert the specified version string into the numeric format. */ + +static ulong STDCALL calc_server_version(char *some_version) +{ + uint major, minor, version; + char *point= some_version, *end_point; + major= (uint) strtoul(point, &end_point, 10); point=end_point+1; + minor= (uint) strtoul(point, &end_point, 10); point=end_point+1; + version= (uint) strtoul(point, &end_point, 10); + return (ulong) major * 10000L + (ulong)(minor * 100 + version); +} + /** Run a command using the shell, storing its output in the supplied dynamic string. @@ -571,7 +609,7 @@ static int run_query(const char *query, DYNAMIC_STRING *ds_res, if (my_write(fd, sql_log_bin, sizeof(sql_log_bin)-1, MYF(MY_FNABP | MY_WME))) { - my_close(fd, MYF(0)); + my_close(fd, MYF(MY_WME)); my_delete(query_file_path, MYF(0)); die("Failed to write to '%s'", query_file_path); } @@ -580,7 +618,7 @@ static int run_query(const char *query, DYNAMIC_STRING *ds_res, if (my_write(fd, (uchar*) query, strlen(query), MYF(MY_FNABP | MY_WME))) { - my_close(fd, MYF(0)); + my_close(fd, MYF(MY_WME)); my_delete(query_file_path, MYF(0)); die("Failed to write to '%s'", query_file_path); } @@ -597,7 +635,7 @@ static int run_query(const char *query, DYNAMIC_STRING *ds_res, "2>&1", NULL); - my_close(fd, MYF(0)); + my_close(fd, MYF(MY_WME)); my_delete(query_file_path, MYF(0)); DBUG_RETURN(ret); @@ -644,6 +682,9 @@ static int get_upgrade_info_file_name(char* name) &ds_datadir, FALSE) || extract_variable_from_show(&ds_datadir, name)) { + print_error("Reading datadir from the MariaDB server failed. Got the " + "following error when executing the 'mysql' command line client", + &ds_datadir); dynstr_free(&ds_datadir); DBUG_RETURN(1); /* Query failed */ } @@ -655,6 +696,83 @@ static int get_upgrade_info_file_name(char* name) DBUG_RETURN(0); } +static char upgrade_info_file[FN_REFLEN]= {0}; + + +/* + Open or create mysql_upgrade_info file in servers data dir. + + Take a lock to ensure there cannot be any other mysql_upgrades + runninc concurrently +*/ + +const char *create_error_message= + "%sCould not open or create the upgrade info file '%s' in " + "the MariaDB Servers data directory, errno: %d (%s)\n"; + + + +static void open_mysql_upgrade_file() +{ + char errbuff[80]; + if (get_upgrade_info_file_name(upgrade_info_file)) + { + die("Upgrade failed"); + } + if ((info_file= my_create(upgrade_info_file, 0, + O_RDWR | O_NOFOLLOW, + MYF(0))) < 0) + { + if (opt_force >= 2) + { + fprintf(stdout, create_error_message, + "", upgrade_info_file, errno, + my_strerror(errbuff, sizeof(errbuff)-1, errno)); + fprintf(stdout, + "--force --force used, continuing without using the %s file.\n" + "Note that this means that there is no protection against " + "concurrent mysql_upgrade executions and next mysql_upgrade run " + "will do a full upgrade again!\n", + upgrade_info_file); + return; + } + fprintf(stdout, create_error_message, + "FATAL ERROR: ", + upgrade_info_file, errno, + my_strerror(errbuff, sizeof(errbuff)-1, errno)); + if (errno == EACCES) + { + fprintf(stderr, + "Note that mysql_upgrade should be run as the same user as the " + "MariaDB server binary, normally 'mysql' or 'root'.\n" + "Alternatively you can use mysql_upgrade --force --force. " + "Please check the documentation if you decide to use the force " + "option!\n"); + } + fflush(stderr); + die(0); + } + if (my_lock(info_file, F_WRLCK, 0, 1, MYF(0))) + { + die("Could not exclusively lock on file '%s'. Error %d: %s\n", + upgrade_info_file, my_errno, + my_strerror(errbuff, sizeof(errbuff)-1, my_errno)); + } +} + + +/** + Place holder for versions that require a major upgrade + + @return 0 upgrade has alredy been run on this version + @return 1 upgrade has to be run + +*/ + +static int faulty_server_versions(const char *version) +{ + return 0; +} /* Read the content of mysql_upgrade_info file and @@ -664,86 +782,111 @@ static int get_upgrade_info_file_name(char* name) NOTE This is an optimization to avoid running mysql_upgrade when it's already been performed for the particular - version of MySQL. + version of MariaDB. - In case the MySQL server can't return the upgrade info + In case the MariaDBL server can't return the upgrade info file it's always better to report that the upgrade hasn't been performed. + @return 0 Upgrade has alredy been run on this version + @return > 0 Upgrade has to be run */ -static int upgrade_already_done(myf flags) +static int upgrade_already_done(int silent) { - FILE *in; - char upgrade_info_file[FN_REFLEN]= {0}; - - if (get_upgrade_info_file_name(upgrade_info_file)) - return 0; /* Could not get filename => not sure */ - - if (!(in= my_fopen(upgrade_info_file, O_RDONLY, flags))) - return 0; /* Could not open file => not sure */ + const char *version = MYSQL_SERVER_VERSION; + const char *s; + char *pos; + my_off_t length; - bzero(upgrade_from_version, sizeof(upgrade_from_version)); - if (!fgets(upgrade_from_version, sizeof(upgrade_from_version), in)) + if (info_file < 0) { - /* Preserve errno for caller */ - int save_errno= errno; - (void) my_fclose(in, flags); - errno= save_errno; - return 0; + DBUG_ASSERT(opt_force > 1); + return 1; /* No info file and --force */ } - if (my_fclose(in, flags)) - return 0; - - errno= 0; - return (strncmp(upgrade_from_version, MYSQL_SERVER_VERSION, - sizeof(MYSQL_SERVER_VERSION)-1)==0); -} + bzero(upgrade_from_version, sizeof(upgrade_from_version)); + (void) my_seek(info_file, 0, SEEK_SET, MYF(0)); + /* We have -3 here to make calc_server_version() safe */ + length= my_read(info_file, (uchar*) upgrade_from_version, + sizeof(upgrade_from_version)-3, + MYF(MY_WME)); -/* - Write mysql_upgrade_info file in servers data dir indicating that - upgrade has been done for this version + if (!length) + { + if (opt_verbose) + verbose("Empty or non existent %s. Assuming mysql_upgrade has to be run!", + upgrade_info_file); + return 1; + } - NOTE - This might very well fail but since it's just an optimization - to run mysql_upgrade only when necessary the error can be - ignored. + /* Remove possible \ŋ that may end in output */ + if ((pos= strchr(upgrade_from_version, '\n'))) + *pos= 0; -*/ - -static void create_mysql_upgrade_info_file(void) -{ - FILE *out; - char upgrade_info_file[FN_REFLEN]= {0}; + if (faulty_server_versions(upgrade_from_version)) + { + if (opt_verbose) + verbose("Upgrading from version %s requires mysql_upgrade to be run!", + upgrade_from_version); + return 2; + } - if (get_upgrade_info_file_name(upgrade_info_file)) - return; /* Could not get filename => skip */ + s= strchr(version, '.'); + s= strchr(s + 1, '.'); - if (!(out= my_fopen(upgrade_info_file, O_TRUNC | O_WRONLY, MYF(0)))) + if (strncmp(upgrade_from_version, version, + (size_t)(s - version + 1))) { - fprintf(stderr, - "Could not create the upgrade info file '%s' in " - "the MariaDB Servers datadir, errno: %d\n", - upgrade_info_file, errno); - return; + if (calc_server_version(upgrade_from_version) <= MYSQL_VERSION_ID) + { + verbose("Major version upgrade detected from %s to %s. Check required!", + upgrade_from_version, version); + return 3; + } + die("Version mismatch (%s -> %s): Trying to downgrade from a higher to " + "lower version is not supported!", + upgrade_from_version, version); } + if (!silent) + { + verbose("This installation of MariaDB is already upgraded to %s.\n" + "There is no need to run mysql_upgrade again for %s.", + upgrade_from_version, version); + if (!opt_check_upgrade) + verbose("You can use --force if you still want to run mysql_upgrade", + upgrade_from_version, version); + } + return 0; +} + +static void finish_mysql_upgrade_info_file(void) +{ + if (info_file < 0) + return; /* Write new version to file */ - my_fwrite(out, (uchar*) MYSQL_SERVER_VERSION, - sizeof(MYSQL_SERVER_VERSION), MY_WME); - my_fclose(out, MYF(MY_WME)); + (void) my_seek(info_file, 0, SEEK_CUR, MYF(0)); + (void) my_chsize(info_file, 0, 0, MYF(0)); + (void) my_seek(info_file, 0, 0, MYF(0)); + (void) my_write(info_file, (uchar*) MYSQL_SERVER_VERSION, + sizeof(MYSQL_SERVER_VERSION)-1, MYF(MY_WME)); + (void) my_write(info_file, (uchar*) "\n", 1, MYF(MY_WME)); + (void) my_lock(info_file, F_UNLCK, 0, 1, MYF(0)); /* - Check if the upgrad_info_file was properly created/updated + Check if the upgrade_info_file was properly created/updated It's not a fatal error -> just print a message if it fails */ - if (!upgrade_already_done(MY_WME)) + if (upgrade_already_done(1)) fprintf(stderr, - "Upgrade file '%s' was not properly created. " - "Got error errno while checking file content: %d\n", + "Could not write to the upgrade info file '%s' in " + "the MariaDB Servers datadir, errno: %d\n", upgrade_info_file, errno); + + my_close(info_file, MYF(MY_WME)); + info_file= -1; return; } @@ -814,7 +957,7 @@ static my_bool is_mysql() strstr(ds_events_struct.str, "IGNORE_BAD_TABLE_OPTIONS") != NULL) ret= FALSE; else - verbose("MySQL upgrade detected"); + verbose("MariaDB upgrade detected"); dynstr_free(&ds_events_struct); return(ret); @@ -1066,7 +1209,7 @@ static int check_slave_repositories(void) } /* - Update all system tables in MySQL Server to current + Update all system tables in MariaDB Server to current version using "mysql" to execute all the SQL commands compiled into the mysql_fix_privilege_tables array */ @@ -1135,24 +1278,6 @@ static int run_sql_fix_privilege_tables(void) } -static void print_error(const char *error_msg, DYNAMIC_STRING *output) -{ - fprintf(stderr, "%s\n", error_msg); - fprintf(stderr, "%s", output->str); -} - - -/* Convert the specified version string into the numeric format. */ -static ulong STDCALL calc_server_version(char *some_version) -{ - uint major, minor, version; - char *point= some_version, *end_point; - major= (uint) strtoul(point, &end_point, 10); point=end_point+1; - minor= (uint) strtoul(point, &end_point, 10); point=end_point+1; - version= (uint) strtoul(point, &end_point, 10); - return (ulong) major * 10000L + (ulong)(minor * 100 + version); -} - /** Check if the server version matches with the server version mysql_upgrade was compiled with. @@ -1188,8 +1313,7 @@ static int check_version_match(void) "check.\n", version_str, MYSQL_SERVER_VERSION); return 1; } - else - return 0; + return 0; } @@ -1198,6 +1322,8 @@ int main(int argc, char **argv) char self_name[FN_REFLEN + 1]; MY_INIT(argv[0]); + DBUG_PROCESS(argv[0]); + load_defaults_or_exit("my", load_default_groups, &argc, &argv); defaults_argv= argv; /* Must be freed by 'free_defaults' */ @@ -1238,12 +1364,17 @@ int main(int argc, char **argv) die(NULL); my_write(fd, USTRING_WITH_LEN( "[client]\n"), MYF(MY_FAE)); my_write(fd, (uchar*)ds_args.str, ds_args.length, MYF(MY_FAE)); - my_close(fd, MYF(0)); + my_close(fd, MYF(MY_WME)); } /* Find mysql */ find_tool(mysql_path, IF_WIN("mysql.exe", "mysql"), self_name); + open_mysql_upgrade_file(); + + if (opt_check_upgrade) + exit(upgrade_already_done(0) == 0); + /* Find mysqlcheck */ find_tool(mysqlcheck_path, IF_WIN("mysqlcheck.exe", "mysqlcheck"), self_name); @@ -1252,15 +1383,10 @@ int main(int argc, char **argv) /* Read the mysql_upgrade_info file to check if mysql_upgrade - already has been run for this installation of MySQL + already has been run for this installation of MariaDB */ - if (!opt_force && upgrade_already_done(0)) - { - printf("This installation of MySQL is already upgraded to %s, " - "use --force if you still need to run mysql_upgrade\n", - MYSQL_SERVER_VERSION); - goto end; - } + if (!opt_force && !upgrade_already_done(0)) + goto end; /* Upgrade already done */ if (opt_version_check && check_version_match()) die("Upgrade failed"); @@ -1285,8 +1411,8 @@ int main(int argc, char **argv) verbose("OK"); - /* Create a file indicating upgrade has been performed */ - create_mysql_upgrade_info_file(); + /* Finish writing indicating upgrade has been performed */ + finish_mysql_upgrade_info_file(); DBUG_ASSERT(phase == phases_total); |