WL#7308: Refactor mysql_upgrade
Affects: Server-5.7
—
Status: Complete
Currently mysql_upgrade internally invokes a number of external tools, e.g. mysqlcheck and mysql client. This causes problems in handling options, logins, special server startup modes etc. And makes the server upgrade procedure a multi-step process. mysql_upgrade should use nothing but the C API to talk to the server. And we should also have a version of it that's bundled with the embedded server and provide for a single-step upgrade process.
User Documentation
1: Fix bugs BUG#65288 and BUG#71579.
2: Use cases should be the same.
3: Any existing usage should be working on new version in the same way.
4: Connecting to service parameters should look like in other tools.
5: Exit codes from mysql_upgrade should be following:
- Initialization error.
- Server already upgraded.
- Bad server version.
- Error during calling mysql_check functionality.
- Error during execution of upgrading queries.
6: Verbosity option prints more progress messages and no notes or warnings from updating queries execution (in contrast to old version).
7: -t --tmpdir option is not needed anymore, and is removed.
This tool does queries to MySQL server and uses mysqlcheck tool. First should be done using C API. Second should be done by calling mysqlcheck functionality linked into mysql_upgrade executable.
This task is divided into following work items:
1) To limit excessive copy&paste of command line options list, variables and
functions making use of these parameters refactoring will be done.
Main idea is to divide these options into functional groups and encapsulate as
much of parameters in separate objects.
Interface I_options_provider with functions to:
- acquire parameters list: vector<my_option> generate_options(),
- do action per option parsed: void option_parsed(int optid, char *argument),
- process data on parsing done: void options_parsed(),
- set option changed event listener: void
set_option_changed_listener(I_option_changed_listener* listener) - by extending
I_option_changed_listener.
It also contains implementation of function for aggregating several instances of
implementations of Options_provider to one list:
static void aggregate_options(vector<my_option> *options,
vector<Options_provider*> options_providers).
Abstract class Abstract_options_provider will be created to hide my_option
structure internally.
It will implement generate_options() and option_parsed(int optid, char
*argument).
It will expose new set of overloaded protected methods to create instances that
implement Abstract_option:
- ...* create_new(_*)_option(...) - they will differ in input parameter types
and will generate expected type of option object.
It will define pure virtual method void create_options(). It's task will be to
create all options using new methods.
It will implement interface I_option_changed_listener with functions:
- void notify_option_optid_changed(I_option* source, uint32 old_optid) - this
function is called by option or contained options provider to allow containers
to check for optid collision.
- void notify_option_name_changed(I_option* source, string old_name) - this
function is called by option or contained options provider to allow containers
to check for name collision.
Class Composite_options_provider will be created to contain other instances of
Abstract_options_provider and aggregate their options - it should register for
option_changed event in each one.
One instance can be contained in at most one another. This will also be assumed
by set_optid_changed_listener, as it can store only one listener in state (for
now, in future when needed this restriction can be removed by making state store
list of listeners).
Class Abstract_Option will be OO wrapper of my_option, which it will contain and
expose only to Abstract_options_provider.
There are type-specific classes extending Abstract_option (base class -> class):
I_option: non-template interface with protected functions for use in
Abstract_options_provider:
void call_callbacks(char* argument)
my_option get_my_option()
void set_option_changed_listener(I_option_changed_listener* listener)
I_option -> Abstract_option: abstract option class to implement all common code,
that is: wrapping my_option, handling option IDs, callback functors, implement
all I_option functions.
Abstract_option -> Bool_option: on/off switch type option.
Abstract_option -> Disabled_option: Represents disabled option. When used in
command line it issues message to user.
Abstract_option -> Abstract_value_option: abstract option to cover options
accepting value.
Abstract_option -> Simple_option: option that does not get any values. Main role
is to execute callbacks when it is used.
Abstract_value_option -> Char_array_option: option for handling string values to
char* variable. Should not be used in new code.
Abstract_value_option -> Abstract_string_option: abstract option for handling
character string argument.
Abstract_string_option -> String_option: option with simple character string
argument.
Abstract_string_option -> Password_option: option to handle password parameter.
Should implement getting password from TTY, warning user when he specified it in
command line and masking it in Linux command line.
Abstract_value_option -> Abstract_number_option: abstract option to cover
numeric values. Allows to set number argument restrictions: minimum and maximum
value boundaries.
Abstract_number_option -> Abstract_integer_number_option: abstract option to
cover integer numeric values. Allows to set additional number argument
restriction: value step.
Abstract_number_option -> Abstract_floating_number_option: abstract option to
cover floating point numeric values.
Abstract_integer_number_option -> Number_option<>: option to cover specific type
integer numeric values: int32, uint32, int64 and uint64.
Abstract_floating_number_option -> Number_option<>: option to cover specific
type floating point numeric values: float and double.
Abstract_string_option -> Choice_option: option with argument value from defined
set. Will cover old GET_ENUM type. This one will not be implemented, as it will
have no usages for a while.
Abstract_value_option -> Flag_option: option which allows to provide comma-
separated string values contained in set. It covers old GET_FLAGSET type. This
one will not be implemented, as it will have no usages for a while.
Abstract_value_option -> Set_option: option which allows to provide comma-
separated string values. It has no replacement in old GET_ types. This one will
not be implemented, as it will have no usages for a while.
Number_option will have additional template parameter TValue stating type of
value: int32, uint32, int64, uint64, float, double.
Constructors to option classes should have at least following parameters:
- Option name.
- Option description.
And for classes implementing Abstract_value_option also:
- Pointer to data where the value will be stored. Also we assume it contains
default value at the constructor call.
We expect client tools not to call these constructors explicitly, instead they
should use methods from a list of Abstract_options_provider::create_new_option()
methods (all '...' stands for option name and description parameters):
Simple_option* create_new_option(...)
Password_option* create_new_password_option(std::string* value, ...)
String_option* create_new_option(std::string* value, ...)
Number_option<TNumber>* create_new_option(<TNumber>* value, ...) - for TNumber
in {int32, uint32, int64, uint64, float, double}.
Bool_option* create_new_option(bool* value, ...)
Choice_option* create_new_option(std::string* value, const
std::vector<std::string> possible_values, ...)
Set_option* create_new_option(std::vector<std::string>* value, ...)
Flag_option* create_new_option(uint64* value, const std::vector<std::string>
possible_values, ...)
All values that receives argument value pointed to as parameter are expected to
be members of calling class (that is extending Abstract_options_provider) to
preserve encapsulation, and to have default value from the moment of creation
method call. Also member value SHOULD NOT be changed afterwards as it will lead
to undefined behaviour.
My_option IDs should be assigned basing on some global counter starting with 256
to allow usage of short codes.
Abstract_options_provider should keep track of used options short IDs and names
to detect collisions. For that it will collect all optids and names, and it will
register itself as I_getopt_change_listener in all created I_option objects.
In implementation of Abstract_string_option and Abstract_floating_number_option
there should be additional private member that will get value assigned from
handle_options() function before conversion to destination type.
They also may expose chain-able methods to use more specific behaviour of
option:
- Abstract_value_option::value_optional() - sets arg_type to OPT_ARG,
- Abstract_option::add_callback(I_callable<void, char*> callback) - adds new
callback for this option for option_parsed() event to callback chain,
- Abstract_option::set_short_character(char code) - sets optid to given
character to make possible usage of short option alternative,
- Abstract_number_option::set_minimum_value(TValue minimum) - sets minimum valid
value,
- Abstract_number_option::set_maximum_value(TValue maximum) - sets maximum valid
value,
- Abstract_integer_number_option::set_value_step(TValue step) - will round value
to value divisible by step that is not further to zero than original value.
For this chain-able methods we need to introduce template parameter TType to
abstract option classes so all these methods can use properly typed pointer to
it as return value.
The my_option.app_type in wrappers should contain reference to wrapping object.
This will enable us to make static callback,
that then can dispatch execution to proper object without using additional
static state. Also we require all options in application to implement I_option.
I_callable is an interface to allow creating functors with one argument, as we
don't have standard ones for now.
I_callable<type_of_return_value, type_of_parameter> exposes type_of_return_value
operator() (type_of_parameter parameter).
I have one implementation for now, Instance_callback that allows usage of
instance method to be used as functor.
I_callable and all implementations should be removed once we have
boost::function<> or std::function<>.
We introduce interface I_connection_factory capable of creating new connections
to use with MYSQL C API. It will consist of MYSQL* create_connection() function.
Common MySQL tools options groups implementing Abstract_options_provider will be
created:
- Help_options (for handling -?),
- Debug_options,
- Mysql_connection_options (also will be able to provide MYSQL connection
according to input parameters by implementing I_connection_factory).
- Mysql_connection_options::Ssl_options used by Mysql_connection_options to
encapsulate SSL-related options.
Abstract class Abstract_program will be created to handle all common sequential
work, that is:
- gathering and parsing options providers parameters,
- calling main tool routine (which is to be overridden by extending classes,
- free resources at the end and call at-exit debug routines.
It will extend Composite_options_provider to allow adding multiple options
providers easily to program.
Also, it allows to get program name (got from command line): const char*
get_name().
It has 4 pure virtual members:
- char* get_version() - Returns string describing current version of this
program.
- string get_description() - Returns string describing shortly current program.
- void error(int error_code) - Handles general errors.
- void execute(vector<string> arguments) - Runs main program code. Supplies all
command line arguments that weren't consumed by handle_options.
Also abstract class Abstract_connection_program extending Abstract_program will
handle Mysql_connection_options and will implement I_connection_factory.
As there are a lot of objects in global namespace and some of tools would like
to share functionality with others there will be a dedicated namespace for each
tool in My::Tools namespace.
All common code for clients should be in Mysql::Tools::Base namespace, and all
options-related (derived from I_option and I_options_provider) should be in
Mysql::Tools::Base::Options namespace.
Alternatively names of objects could be prefixed, but that would cause more
boilerplate and would not make any deeper division.
Also using namespaces will make it easier for community developers to integrate
with tools and extend them (mysqldump seems to have such possibility in future).
This is main reason to use Mysql namespace instead of global one for tools.
The main() function should then look like:
static ::Mysql::Tools::Upgrade::Program program;
int main(int argc, char **argv)
{
program.run(argc, argv);
}
It should be global variable to make it being destructed on exit(), which wouldn't if it was on heap or stack. This is required to Debug_options work properly.
Here are two remaining work items in this task (continuation from first lines):
2) Use MYSQL C API to run queries in run_query routine.
3) Make mysqlcheck code available for use from mysql_upgrade and call required
routines. main() in mysqlcheck need to be divided, so that part with actual work
is separated from options parsing, connection creation etc.
Subsequent calls will be parametrized using explicit option variables assignment
in call to function extracted from main().
This function will be in mysqlcheck_core library, which should be the kind of
API for whole functionality.
No other objects should be accessed from mysqlcheck source within mysql_upgrade.
Copyright (c) 2000, 2025, Oracle Corporation and/or its affiliates. All rights reserved.