diff options
Diffstat (limited to 'server-tools/instance-manager/commands.cc')
-rw-r--r-- | server-tools/instance-manager/commands.cc | 1855 |
1 files changed, 1369 insertions, 486 deletions
diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc index 7b999f61503..83ed9cf6536 100644 --- a/server-tools/instance-manager/commands.cc +++ b/server-tools/instance-manager/commands.cc @@ -14,36 +14,54 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#if defined(__GNUC__) && defined(USE_PRAGMA_IMPLEMENTATION) +#pragma implementation +#endif + #include "commands.h" +#include <my_global.h> +#include <m_string.h> +#include <m_ctype.h> +#include <mysql.h> +#include <my_dir.h> + +#include "buffer.h" +#include "guardian.h" #include "instance_map.h" +#include "log.h" +#include "manager.h" #include "messages.h" #include "mysqld_error.h" #include "mysql_manager_error.h" -#include "protocol.h" -#include "buffer.h" #include "options.h" +#include "priv.h" +#include "protocol.h" -#include <m_string.h> -#include <m_ctype.h> -#include <mysql.h> -#include <my_dir.h> + +/* + modify_defaults_to_im_error -- a map of error codes of + mysys::modify_defaults_file() into Instance Manager error codes. +*/ + +static const int modify_defaults_to_im_error[]= { 0, ER_OUT_OF_RESOURCES, + ER_ACCESS_OPTION_FILE }; /* - Add a string to a buffer + Add a string to a buffer. SYNOPSYS put_to_buff() buff buffer to add the string str string to add - uint offset in the buff to add a string + position offset in the buff to add a string DESCRIPTION Function to add a string to the buffer. It is different from - store_to_protocol_packet, which is used in the protocol.cc. The last - one also stores the length of the string in a special way. + store_to_protocol_packet, which is used in the protocol.cc. + The last one also stores the length of the string in a special way. This is required for MySQL client/server protocol support only. RETURN @@ -51,7 +69,6 @@ 1 - error occured */ - static inline int put_to_buff(Buffer *buff, const char *str, uint *position) { uint len= strlen(str); @@ -88,749 +105,1615 @@ static int parse_version_number(const char *version_str, char *version, } -/* implementation for Show_instances: */ +/************************************************************************** + Implementation of Instance_name. +**************************************************************************/ +Instance_name::Instance_name(const LEX_STRING *name) +{ + str.str= str_buffer; + str.length= name->length; -/* - The method sends a list of instances in the instance map to the client. + if (str.length > MAX_INSTANCE_NAME_SIZE - 1) + str.length= MAX_INSTANCE_NAME_SIZE - 1; - SYNOPSYS - Show_instances::execute() - net The network connection to the client. - connection_id Client connection ID + strmake(str.str, name->str, str.length); +} - RETURN - 0 - ok - 1 - error occured +/************************************************************************** + Implementation of Show_instances. +**************************************************************************/ + +/* + Implementation of SHOW INSTANCES statement. + + Possible error codes: + ER_OUT_OF_RESOURCES Not enough resources to complete the operation */ -int Show_instances::execute(struct st_net *net, ulong connection_id) +int Show_instances::execute(st_net *net, ulong connection_id) { - Buffer send_buff; /* buffer for packets */ - LIST name, status; - NAME_WITH_LENGTH name_field, status_field; + int err_code; + + if ((err_code= write_header(net)) || + (err_code= write_data(net))) + return err_code; + + if (send_eof(net) || net_flush(net)) + return ER_OUT_OF_RESOURCES; + + return 0; +} + + +int Show_instances::write_header(st_net *net) +{ + LIST name, state; + LEX_STRING name_field, state_field; LIST *field_list; - uint position=0; - name_field.name= (char*) "instance_name"; + name_field.str= (char *) "instance_name"; name_field.length= DEFAULT_FIELD_LENGTH; name.data= &name_field; - status_field.name= (char*) "status"; - status_field.length= DEFAULT_FIELD_LENGTH; - status.data= &status_field; - field_list= list_add(NULL, &status); + + state_field.str= (char *) "state"; + state_field.length= DEFAULT_FIELD_LENGTH; + state.data= &state_field; + + field_list= list_add(NULL, &state); field_list= list_add(field_list, &name); - send_fields(net, field_list); + return send_fields(net, field_list) ? ER_OUT_OF_RESOURCES : 0; +} + + +int Show_instances::write_data(st_net *net) +{ + my_bool err_status= FALSE; + + Instance *instance; + Instance_map::Iterator iterator(instance_map); + instance_map->guardian->lock(); + instance_map->lock(); + + while ((instance= iterator.next())) { - Instance *instance; - Instance_map::Iterator iterator(instance_map); + Buffer send_buf; /* buffer for packets */ + uint pos= 0; + + const char *instance_name= instance->options.instance_name.str; + const char *state_name= instance_map->get_instance_state_name(instance); - instance_map->lock(); - while ((instance= iterator.next())) + if (store_to_protocol_packet(&send_buf, instance_name, &pos) || + store_to_protocol_packet(&send_buf, state_name, &pos) || + my_net_write(net, send_buf.buffer, pos)) { - position= 0; - store_to_protocol_packet(&send_buff, instance->options.instance_name, - &position); - if (instance->is_running()) - store_to_protocol_packet(&send_buff, (char*) "online", &position); - else - store_to_protocol_packet(&send_buff, (char*) "offline", &position); - if (my_net_write(net, send_buff.buffer, (uint) position)) - goto err; + err_status= TRUE; + break; } - instance_map->unlock(); } - if (send_eof(net)) - goto err; - if (net_flush(net)) - goto err; - return 0; -err: - return ER_OUT_OF_RESOURCES; + instance_map->unlock(); + instance_map->guardian->unlock(); + + return err_status ? ER_OUT_OF_RESOURCES : 0; } -/* implementation for Flush_instances: */ +/************************************************************************** + Implementation of Flush_instances. +**************************************************************************/ + +/* + Implementation of FLUSH INSTANCES statement. + + Possible error codes: + ER_OUT_OF_RESOURCES Not enough resources to complete the operation + ER_THERE_IS_ACTIVE_INSTACE If there is an active instance +*/ -int Flush_instances::execute(struct st_net *net, ulong connection_id) +int Flush_instances::execute(st_net *net, ulong connection_id) { - if (instance_map->flush_instances() || - net_send_ok(net, connection_id, NULL)) + instance_map->guardian->lock(); + instance_map->lock(); + + if (instance_map->is_there_active_instance()) + { + instance_map->unlock(); + instance_map->guardian->unlock(); + return ER_THERE_IS_ACTIVE_INSTACE; + } + + if (instance_map->flush_instances()) + { + instance_map->unlock(); + instance_map->guardian->unlock(); return ER_OUT_OF_RESOURCES; + } - return 0; + instance_map->unlock(); + instance_map->guardian->unlock(); + + return net_send_ok(net, connection_id, NULL) ? ER_OUT_OF_RESOURCES : 0; } -/* implementation for Show_instance_status: */ +/************************************************************************** + Implementation of Abstract_instance_cmd. +**************************************************************************/ -Show_instance_status::Show_instance_status(Instance_map *instance_map_arg, - const char *name, uint len) - :Command(instance_map_arg) +Abstract_instance_cmd::Abstract_instance_cmd( + Instance_map *instance_map_arg, const LEX_STRING *instance_name_arg) + :Command(instance_map_arg), + instance_name(instance_name_arg) { - Instance *instance; + /* + MT-NOTE: we can not make a search for Instance object here, + because it can dissappear after releasing the lock. + */ +} + + +int Abstract_instance_cmd::execute(st_net *net, ulong connection_id) +{ + int err_code; + + instance_map->lock(); + + { + Instance *instance= instance_map->find(get_instance_name()); + + if (!instance) + { + instance_map->unlock(); + return ER_BAD_INSTANCE_NAME; + } + + err_code= execute_impl(net, instance); + } - /* we make a search here, since we don't want to store the name */ - if ((instance= instance_map->find(name, len))) - instance_name= instance->options.instance_name; - else - instance_name= NULL; + instance_map->unlock(); + + if (!err_code) + err_code= send_ok_response(net, connection_id); + + return err_code; } -/* - The method sends a table with a status of requested instance to the client. +/************************************************************************** + Implementation of Show_instance_status. +**************************************************************************/ - SYNOPSYS - Show_instance_status::do_command() - net The network connection to the client. - instance_name The name of the instance. +Show_instance_status::Show_instance_status(Instance_map *instance_map_arg, + const LEX_STRING *instance_name_arg) + :Abstract_instance_cmd(instance_map_arg, instance_name_arg) +{ +} - RETURN - 0 - ok - 1 - error occured + +/* + Implementation of SHOW INSTANCE STATUS statement. + + Possible error codes: + ER_BAD_INSTANCE_NAME The instance with the given name does not exist + ER_OUT_OF_RESOURCES Not enough resources to complete the operation */ +int Show_instance_status::execute_impl(st_net *net, Instance *instance) +{ + int err_code; + + if ((err_code= write_header(net)) || + (err_code= write_data(net, instance))) + return err_code; -int Show_instance_status::execute(struct st_net *net, - ulong connection_id) + return 0; +} + + +int Show_instance_status::send_ok_response(st_net *net, ulong connection_id) { - enum { MAX_VERSION_LENGTH= 40 }; - Buffer send_buff; /* buffer for packets */ - LIST name, status, version, version_number; + if (send_eof(net) || net_flush(net)) + return ER_OUT_OF_RESOURCES; + + return 0; +} + + +int Show_instance_status::write_header(st_net *net) +{ + LIST name, state, version, version_number, mysqld_compatible; LIST *field_list; - NAME_WITH_LENGTH name_field, status_field, version_field, - version_number_field; - uint position=0; + LEX_STRING name_field, state_field, version_field, + version_number_field, mysqld_compatible_field; - if (!instance_name) - return ER_BAD_INSTANCE_NAME; + /* Create list of the fileds to be passed to send_fields(). */ - /* create list of the fileds to be passed to send_fields */ - name_field.name= (char*) "instance_name"; + name_field.str= (char *) "instance_name"; name_field.length= DEFAULT_FIELD_LENGTH; name.data= &name_field; - status_field.name= (char*) "status"; - status_field.length= DEFAULT_FIELD_LENGTH; - status.data= &status_field; - version_field.name= (char*) "version"; + + state_field.str= (char *) "state"; + state_field.length= DEFAULT_FIELD_LENGTH; + state.data= &state_field; + + version_field.str= (char *) "version"; version_field.length= MAX_VERSION_LENGTH; version.data= &version_field; - version_number_field.name= (char*) "version_number"; + + version_number_field.str= (char *) "version_number"; version_number_field.length= MAX_VERSION_LENGTH; version_number.data= &version_number_field; - field_list= list_add(NULL, &version); + + mysqld_compatible_field.str= (char *) "mysqld_compatible"; + mysqld_compatible_field.length= DEFAULT_FIELD_LENGTH; + mysqld_compatible.data= &mysqld_compatible_field; + + field_list= list_add(NULL, &mysqld_compatible); + field_list= list_add(field_list, &version); field_list= list_add(field_list, &version_number); - field_list= list_add(field_list, &status); + field_list= list_add(field_list, &state); field_list= list_add(field_list, &name); - send_fields(net, field_list); + return send_fields(net, field_list) ? ER_OUT_OF_RESOURCES : 0; +} - { - Instance *instance; - store_to_protocol_packet(&send_buff, (char*) instance_name, &position); - if (!(instance= instance_map->find(instance_name, strlen(instance_name)))) - goto err; - if (instance->is_running()) - store_to_protocol_packet(&send_buff, (char*) "online", &position); - else - store_to_protocol_packet(&send_buff, (char*) "offline", &position); +int Show_instance_status::write_data(st_net *net, Instance *instance) +{ + Buffer send_buf; /* buffer for packets */ + char version_num_buf[MAX_VERSION_LENGTH]; + uint pos= 0; - if (instance->options.mysqld_version) - { - char parsed_version[MAX_VERSION_LENGTH]; + const char *state_name; + const char *version_tag= "unknown"; + const char *version_num= "unknown"; + const char *mysqld_compatible_status; - parse_version_number(instance->options.mysqld_version, parsed_version, - sizeof(parsed_version)); - store_to_protocol_packet(&send_buff, parsed_version, &position); + instance_map->guardian->lock(); + state_name= instance_map->get_instance_state_name(instance); + mysqld_compatible_status= instance->is_mysqld_compatible() ? "yes" : "no"; + instance_map->guardian->unlock(); - store_to_protocol_packet(&send_buff, instance->options.mysqld_version, - &position); - } - else - { - store_to_protocol_packet(&send_buff, (char*) "unknown", &position); - store_to_protocol_packet(&send_buff, (char*) "unknown", &position); - } + if (instance->options.mysqld_version) + { + if (parse_version_number(instance->options.mysqld_version, version_num_buf, + sizeof(version_num_buf))) + return ER_OUT_OF_RESOURCES; - if (send_buff.is_error() || - my_net_write(net, send_buff.buffer, (uint) position)) - goto err; + version_num= version_num_buf; + version_tag= instance->options.mysqld_version; } - if (send_eof(net) || net_flush(net)) - goto err; + if (store_to_protocol_packet(&send_buf, get_instance_name()->str, &pos) || + store_to_protocol_packet(&send_buf, state_name, &pos) || + store_to_protocol_packet(&send_buf, version_num, &pos) || + store_to_protocol_packet(&send_buf, version_tag, &pos) || + store_to_protocol_packet(&send_buf, mysqld_compatible_status, &pos) || + my_net_write(net, send_buf.buffer, (uint) pos)) + { + return ER_OUT_OF_RESOURCES; + } return 0; +} + + +/************************************************************************** + Implementation of Show_instance_options. +**************************************************************************/ -err: - return ER_OUT_OF_RESOURCES; +Show_instance_options::Show_instance_options( + Instance_map *instance_map_arg, const LEX_STRING *instance_name_arg) + :Abstract_instance_cmd(instance_map_arg, instance_name_arg) +{ } -/* Implementation for Show_instance_options */ +/* + Implementation of SHOW INSTANCE OPTIONS statement. -Show_instance_options::Show_instance_options(Instance_map *instance_map_arg, - const char *name, uint len): - Command(instance_map_arg) + Possible error codes: + ER_BAD_INSTANCE_NAME The instance with the given name does not exist + ER_OUT_OF_RESOURCES Not enough resources to complete the operation +*/ + +int Show_instance_options::execute_impl(st_net *net, Instance *instance) { - Instance *instance; + int err_code; - /* we make a search here, since we don't want to store the name */ - if ((instance= instance_map->find(name, len))) - instance_name= instance->options.instance_name; - else - instance_name= NULL; + if ((err_code= write_header(net)) || + (err_code= write_data(net, instance))) + return err_code; + + return 0; } -int Show_instance_options::execute(struct st_net *net, ulong connection_id) +int Show_instance_options::send_ok_response(st_net *net, ulong connection_id) +{ + if (send_eof(net) || net_flush(net)) + return ER_OUT_OF_RESOURCES; + + return 0; +} + + +int Show_instance_options::write_header(st_net *net) { - Buffer send_buff; /* buffer for packets */ LIST name, option; LIST *field_list; - NAME_WITH_LENGTH name_field, option_field; - uint position=0; + LEX_STRING name_field, option_field; - if (!instance_name) - return ER_BAD_INSTANCE_NAME; + /* Create list of the fileds to be passed to send_fields(). */ - /* create list of the fileds to be passed to send_fields */ - name_field.name= (char*) "option_name"; + name_field.str= (char *) "option_name"; name_field.length= DEFAULT_FIELD_LENGTH; name.data= &name_field; - option_field.name= (char*) "value"; + + option_field.str= (char *) "value"; option_field.length= DEFAULT_FIELD_LENGTH; option.data= &option_field; + field_list= list_add(NULL, &option); field_list= list_add(field_list, &name); - send_fields(net, field_list); + return send_fields(net, field_list) ? ER_OUT_OF_RESOURCES : 0; +} + +int Show_instance_options::write_data(st_net *net, Instance *instance) +{ + Buffer send_buff; /* buffer for packets */ + uint pos= 0; + + if (store_to_protocol_packet(&send_buff, "instance_name", &pos) || + store_to_protocol_packet(&send_buff, get_instance_name()->str, &pos) || + my_net_write(net, send_buff.buffer, pos)) { - Instance *instance; - - if (!(instance= instance_map->find(instance_name, strlen(instance_name)))) - goto err; - store_to_protocol_packet(&send_buff, (char*) "instance_name", &position); - store_to_protocol_packet(&send_buff, (char*) instance_name, &position); - if (my_net_write(net, send_buff.buffer, (uint) position)) - goto err; - if ((instance->options.mysqld_path)) - { - position= 0; - store_to_protocol_packet(&send_buff, (char*) "mysqld-path", &position); - store_to_protocol_packet(&send_buff, - (char*) instance->options.mysqld_path, - &position); - if (send_buff.is_error() || - my_net_write(net, send_buff.buffer, (uint) position)) - goto err; - } + return ER_OUT_OF_RESOURCES; + } + + /* Loop through the options. */ + + for (int i= 0; i < instance->options.get_num_options(); i++) + { + Named_value option= instance->options.get_option(i); + const char *option_value= option.get_value()[0] ? option.get_value() : ""; + + pos= 0; - if ((instance->options.nonguarded)) + if (store_to_protocol_packet(&send_buff, option.get_name(), &pos) || + store_to_protocol_packet(&send_buff, option_value, &pos) || + my_net_write(net, send_buff.buffer, pos)) { - position= 0; - store_to_protocol_packet(&send_buff, (char*) "nonguarded", &position); - store_to_protocol_packet(&send_buff, "", &position); - if (send_buff.is_error() || - my_net_write(net, send_buff.buffer, (uint) position)) - goto err; + return ER_OUT_OF_RESOURCES; } + } + + return 0; +} + - /* loop through the options stored in DYNAMIC_ARRAY */ - for (uint i= 0; i < instance->options.options_array.elements; i++) +/************************************************************************** + Implementation of Start_instance. +**************************************************************************/ + +Start_instance::Start_instance(Instance_map *instance_map_arg, + const LEX_STRING *instance_name_arg) + :Abstract_instance_cmd(instance_map_arg, instance_name_arg) +{ +} + + +/* + Implementation of START INSTANCE statement. + + Possible error codes: + ER_BAD_INSTANCE_NAME The instance with the given name does not exist + ER_OUT_OF_RESOURCES Not enough resources to complete the operation +*/ + +int Start_instance::execute_impl(st_net *net, Instance *instance) +{ + int err_code; + + if ((err_code= instance->start())) + return err_code; + + if (!(instance->options.nonguarded)) + instance_map->guardian->guard(instance); + + return 0; +} + + +int Start_instance::send_ok_response(st_net *net, ulong connection_id) +{ + if (net_send_ok(net, connection_id, "Instance started")) + return ER_OUT_OF_RESOURCES; + + return 0; +} + + +/************************************************************************** + Implementation of Stop_instance. +**************************************************************************/ + +Stop_instance::Stop_instance(Instance_map *instance_map_arg, + const LEX_STRING *instance_name_arg) + :Abstract_instance_cmd(instance_map_arg, instance_name_arg) +{ +} + + +/* + Implementation of STOP INSTANCE statement. + + Possible error codes: + ER_BAD_INSTANCE_NAME The instance with the given name does not exist + ER_OUT_OF_RESOURCES Not enough resources to complete the operation +*/ + +int Stop_instance::execute_impl(st_net *net, Instance *instance) +{ + int err_code; + + if (!(instance->options.nonguarded)) + instance_map->guardian->stop_guard(instance); + + if ((err_code= instance->stop())) + return err_code; + + return 0; +} + + +int Stop_instance::send_ok_response(st_net *net, ulong connection_id) +{ + if (net_send_ok(net, connection_id, NULL)) + return ER_OUT_OF_RESOURCES; + + return 0; +} + + +/************************************************************************** + Implementation for Create_instance. +**************************************************************************/ + +Create_instance::Create_instance(Instance_map *instance_map_arg, + const LEX_STRING *instance_name_arg) + :Command(instance_map_arg), + instance_name(instance_name_arg) +{ +} + + +/* + This operation initializes Create_instance object. + + SYNOPSYS + text [IN/OUT] a pointer to the text containing instance options. + + RETURN + FALSE On success. + TRUE On error. +*/ + +bool Create_instance::init(const char **text) +{ + return options.init() || parse_args(text); +} + + +/* + This operation parses CREATE INSTANCE options. + + SYNOPSYS + text [IN/OUT] a pointer to the text containing instance options. + + RETURN + FALSE On success. + TRUE On syntax error. +*/ + +bool Create_instance::parse_args(const char **text) +{ + uint len; + + /* Check if we have something (and trim leading spaces). */ + + get_word(text, &len, NONSPACE); + + if (len == 0) + return FALSE; /* OK: no option. */ + + /* Main parsing loop. */ + + while (TRUE) + { + LEX_STRING option_name; + char *option_name_str; + char *option_value_str= NULL; + + /* Looking for option name. */ + + get_word(text, &option_name.length, OPTION_NAME); + + if (option_name.length == 0) + return TRUE; /* Syntax error: option name expected. */ + + option_name.str= (char *) *text; + *text+= option_name.length; + + /* Looking for equal sign. */ + + skip_spaces(text); + + if (**text == '=') { - char *tmp_option, *option_value; - get_dynamic(&(instance->options.options_array), (gptr) &tmp_option, i); - option_value= strchr(tmp_option, '='); - /* split the option string into two parts if it has a value */ + ++(*text); /* Skip an equal sign. */ - position= 0; - if (option_value != NULL) + /* Looking for option value. */ + + skip_spaces(text); + + if (!**text) + return TRUE; /* Syntax error: EOS when option value expected. */ + + if (**text != '\'' && **text != '"') { - *option_value= 0; - store_to_protocol_packet(&send_buff, tmp_option + 2, &position); - store_to_protocol_packet(&send_buff, option_value + 1, &position); - /* join name and the value into the same option again */ - *option_value= '='; + /* Option value is a simple token. */ + + LEX_STRING option_value; + + get_word(text, &option_value.length, ALPHANUM); + + if (option_value.length == 0) + return TRUE; /* internal parser error. */ + + option_value.str= (char *) *text; + *text+= option_value.length; + + if (!(option_value_str= Named_value::alloc_str(&option_value))) + return TRUE; /* out of memory during parsing. */ } else { - store_to_protocol_packet(&send_buff, tmp_option + 2, &position); - store_to_protocol_packet(&send_buff, "", &position); + /* Option value is a string. */ + + if (parse_option_value(*text, &len, &option_value_str)) + return TRUE; /* Syntax error: invalid string specification. */ + + *text+= len; } + } + + if (!option_value_str) + { + LEX_STRING empty_str= { C_STRING_WITH_SIZE("") }; + + if (!(option_value_str= Named_value::alloc_str(&empty_str))) + return TRUE; /* out of memory during parsing. */ + } + + if (!(option_name_str= Named_value::alloc_str(&option_name))) + { + Named_value::free_str(&option_value_str); + return TRUE; /* out of memory during parsing. */ + } + + { + Named_value option(option_name_str, option_value_str); - if (send_buff.is_error() || - my_net_write(net, send_buff.buffer, (uint) position)) - goto err; + if (options.add_element(&option)) + { + option.free(); + return TRUE; /* out of memory during parsing. */ + } } + + skip_spaces(text); + + if (!**text) + return FALSE; /* OK: end of options. */ + + if (**text != ',') + return TRUE; /* Syntax error: comma expected. */ + + ++(*text); } +} - if (send_eof(net) || net_flush(net)) - goto err; - return 0; +/* + Implementation of CREATE INSTANCE statement. -err: - return ER_OUT_OF_RESOURCES; + Possible error codes: + ER_MALFORMED_INSTANCE_NAME Instance name is malformed + ER_CREATE_EXISTING_INSTANCE There is an instance with the given name + ER_OUT_OF_RESOURCES Not enough resources to complete the operation +*/ + +int Create_instance::execute(st_net *net, ulong connection_id) +{ + int err_code; + + /* Check that the name is valid and there is no instance with such name. */ + + if (!Instance::is_name_valid(get_instance_name())) + return ER_MALFORMED_INSTANCE_NAME; + + /* + NOTE: In order to prevent race condition, we should perform all operations + on under acquired lock. + */ + + instance_map->lock(); + + if (instance_map->find(get_instance_name())) + { + instance_map->unlock(); + return ER_CREATE_EXISTING_INSTANCE; + } + + if ((err_code= instance_map->create_instance(get_instance_name(), &options))) + { + instance_map->unlock(); + return err_code; + } + + if ((err_code= create_instance_in_file(get_instance_name(), &options))) + { + Instance *instance= instance_map->find(get_instance_name()); + + if (instance) + instance_map->remove_instance(instance); /* instance is deleted here. */ + + instance_map->unlock(); + return err_code; + } + + /* That's all. */ + + instance_map->unlock(); + + /* Send the result. */ + + if (net_send_ok(net, connection_id, NULL)) + return ER_OUT_OF_RESOURCES; + + return 0; } -/* Implementation for Start_instance */ +/************************************************************************** + Implementation for Drop_instance. +**************************************************************************/ -Start_instance::Start_instance(Instance_map *instance_map_arg, - const char *name, uint len) - :Command(instance_map_arg) +Drop_instance::Drop_instance(Instance_map *instance_map_arg, + const LEX_STRING *instance_name_arg) + :Abstract_instance_cmd(instance_map_arg, instance_name_arg) { - /* we make a search here, since we don't want to store the name */ - if ((instance= instance_map->find(name, len))) - instance_name= instance->options.instance_name; } -int Start_instance::execute(struct st_net *net, ulong connection_id) +/* + Implementation of DROP INSTANCE statement. + + Possible error codes: + ER_BAD_INSTANCE_NAME The instance with the given name does not exist + ER_DROP_ACTIVE_INSTANCE The specified instance is active + ER_OUT_OF_RESOURCES Not enough resources to complete the operation +*/ + +int Drop_instance::execute_impl(st_net *net, Instance *instance) { - uint err_code; - if (instance == 0) - return ER_BAD_INSTANCE_NAME; /* haven't found an instance */ - else - { - if ((err_code= instance->start())) - return err_code; + int err_code; + + /* Check that the instance is offline. */ - if (!(instance->options.nonguarded)) - instance_map->guardian->guard(instance); + if (instance_map->guardian->is_active(instance)) + return ER_DROP_ACTIVE_INSTANCE; - net_send_ok(net, connection_id, "Instance started"); - return 0; + err_code= modify_defaults_file(Options::Main::config_file, NULL, NULL, + get_instance_name()->str, MY_REMOVE_SECTION); + DBUG_ASSERT(err_code >= 0 && err_code <= 2); + + if (err_code) + { + log_error("Can not remove instance '%s' from defaults file (%s). " + "Original error code: %d.", + (const char *) get_instance_name()->str, + (const char *) Options::Main::config_file, + (int) err_code); } + + if (err_code) + return modify_defaults_to_im_error[err_code]; + + /* Remove instance from the instance map hash and Guardian's list. */ + + if (!instance->options.nonguarded) + instance_map->guardian->stop_guard(instance); + + if ((err_code= instance->stop())) + return err_code; + + instance_map->remove_instance(instance); + + return 0; } -/* implementation for Show_instance_log: */ +int Drop_instance::send_ok_response(st_net *net, ulong connection_id) +{ + if (net_send_ok(net, connection_id, "Instance dropped")) + return ER_OUT_OF_RESOURCES; + + return 0; +} + + +/************************************************************************** + Implementation for Show_instance_log. +**************************************************************************/ Show_instance_log::Show_instance_log(Instance_map *instance_map_arg, - const char *name, uint len, + const LEX_STRING *instance_name_arg, Log_type log_type_arg, - const char *size_arg, - const char *offset_arg) - :Command(instance_map_arg) + uint size_arg, uint offset_arg) + :Abstract_instance_cmd(instance_map_arg, instance_name_arg), + log_type(log_type_arg), + size(size_arg), + offset(offset_arg) { - Instance *instance; +} + + +/* + Implementation of SHOW INSTANCE LOG statement. + + Possible error codes: + ER_BAD_INSTANCE_NAME The instance with the given name does not exist + ER_OFFSET_ERROR We were requested to read negative number of + bytes from the log + ER_NO_SUCH_LOG The specified type of log is not available for + the given instance + ER_GUESS_LOGFILE IM wasn't able to figure out the log + placement, while it is enabled. Probably user + should specify the path to the logfile + explicitly. + ER_OPEN_LOGFILE Cannot open the logfile + ER_READ_FILE Cannot read the logfile + ER_OUT_OF_RESOURCES Not enough resources to complete the operation +*/ + +int Show_instance_log::execute_impl(st_net *net, Instance *instance) +{ + int err_code; + + if ((err_code= check_params(instance))) + return err_code; + + if ((err_code= write_header(net)) || + (err_code= write_data(net, instance))) + return err_code; + + return 0; +} - if (offset_arg != NULL) - offset= atoi(offset_arg); - else - offset= 0; - size= atoi(size_arg); - log_type= log_type_arg; - /* we make a search here, since we don't want to store the name */ - if ((instance= instance_map->find(name, len))) - instance_name= instance->options.instance_name; - else - instance_name= NULL; +int Show_instance_log::send_ok_response(st_net *net, ulong connection_id) +{ + if (send_eof(net) || net_flush(net)) + return ER_OUT_OF_RESOURCES; + + return 0; } +int Show_instance_log::check_params(Instance *instance) +{ + const char *logpath= instance->options.logs[log_type]; -/* - Open the logfile, read requested part of the log and send the info - to the client. + /* Cannot read negative number of bytes. */ - SYNOPSYS - Show_instance_log::execute() - net The network connection to the client. - connection_id Client connection ID + if (offset > size) + return ER_OFFSET_ERROR; - DESCRIPTION + /* Instance has no such log. */ - Send a table with the content of the log requested. The function also - deals with errro handling, to be verbose. + if (logpath == NULL) + return ER_NO_SUCH_LOG; + + if (*logpath == '\0') + return ER_GUESS_LOGFILE; + + return 0; +} - RETURN - ER_OFFSET_ERROR We were requested to read negative number of bytes - from the log - ER_NO_SUCH_LOG The kind log being read is not enabled in the instance - ER_GUESS_LOGFILE IM wasn't able to figure out the log placement, while - it is enabled. Probably user should specify the path - to the logfile explicitly. - ER_OPEN_LOGFILE Cannot open the logfile - ER_READ_FILE Cannot read the logfile - ER_OUT_OF_RESOURCES We weren't able to allocate some resources -*/ -int Show_instance_log::execute(struct st_net *net, ulong connection_id) +int Show_instance_log::write_header(st_net *net) { - Buffer send_buff; /* buffer for packets */ LIST name; LIST *field_list; - NAME_WITH_LENGTH name_field; - uint position= 0; + LEX_STRING name_field; + + /* Create list of the fields to be passed to send_fields(). */ - /* create list of the fileds to be passed to send_fields */ - name_field.name= (char*) "Log"; + name_field.str= (char *) "Log"; name_field.length= DEFAULT_FIELD_LENGTH; - name.data= &name_field; - field_list= list_add(NULL, &name); - if (!instance_name) - return ER_BAD_INSTANCE_NAME; + name.data= &name_field; - /* cannot read negative number of bytes */ - if (offset > size) - return ER_OFFSET_ERROR; + field_list= list_add(NULL, &name); - send_fields(net, field_list); + return send_fields(net, field_list) ? ER_OUT_OF_RESOURCES : 0; +} - { - Instance *instance; - const char *logpath; - File fd; - if ((instance= instance_map->find(instance_name, - strlen(instance_name))) == NULL) - goto err; +int Show_instance_log::write_data(st_net *net, Instance *instance) +{ + Buffer send_buff; /* buffer for packets */ + uint pos= 0; - logpath= instance->options.logs[log_type]; + const char *logpath= instance->options.logs[log_type]; + File fd; - /* Instance has no such log */ - if (logpath == NULL) - return ER_NO_SUCH_LOG; + size_t buff_size; + int read_len; - if (*logpath == '\0') - return ER_GUESS_LOGFILE; + MY_STAT file_stat; + Buffer read_buff; + if ((fd= my_open(logpath, O_RDONLY | O_BINARY, MYF(MY_WME))) <= 0) + return ER_OPEN_LOGFILE; - if ((fd= my_open(logpath, O_RDONLY | O_BINARY, MYF(MY_WME))) >= 0) - { - size_t buff_size; - int read_len; - /* calculate buffer size */ - MY_STAT file_stat; - Buffer read_buff; + /* my_fstat doesn't use the flag parameter */ + if (my_fstat(fd, &file_stat, MYF(0))) + { + close(fd); + return ER_OUT_OF_RESOURCES; + } - /* my_fstat doesn't use the flag parameter */ - if (my_fstat(fd, &file_stat, MYF(0))) - goto err; + /* calculate buffer size */ + buff_size= (size - offset); - buff_size= (size - offset); + read_buff.reserve(0, buff_size); - read_buff.reserve(0, buff_size); + /* read in one chunk */ + read_len= (int)my_seek(fd, file_stat.st_size - size, MY_SEEK_SET, MYF(0)); - /* read in one chunk */ - read_len= (int)my_seek(fd, file_stat.st_size - size, MY_SEEK_SET, MYF(0)); + if ((read_len= my_read(fd, (byte*) read_buff.buffer, + buff_size, MYF(0))) < 0) + { + close(fd); + return ER_READ_FILE; + } - if ((read_len= my_read(fd, (byte*) read_buff.buffer, - buff_size, MYF(0))) < 0) - return ER_READ_FILE; - store_to_protocol_packet(&send_buff, read_buff.buffer, - &position, read_len); - close(fd); - } - else - return ER_OPEN_LOGFILE; + close(fd); - if (my_net_write(net, send_buff.buffer, (uint) position)) - goto err; + if (store_to_protocol_packet(&send_buff, read_buff.buffer, &pos, read_len) || + my_net_write(net, send_buff.buffer, pos)) + { + return ER_OUT_OF_RESOURCES; } - if (send_eof(net) || net_flush(net)) - goto err; - return 0; - -err: - return ER_OUT_OF_RESOURCES; } -/* implementation for Show_instance_log_files: */ +/************************************************************************** + Implementation of Show_instance_log_files. +**************************************************************************/ Show_instance_log_files::Show_instance_log_files - (Instance_map *instance_map_arg, const char *name, uint len) - :Command(instance_map_arg) + (Instance_map *instance_map_arg, + const LEX_STRING *instance_name_arg) + :Abstract_instance_cmd(instance_map_arg, instance_name_arg) { - Instance *instance; - - /* we make a search here, since we don't want to store the name */ - if ((instance= instance_map->find(name, len))) - instance_name= instance->options.instance_name; - else - instance_name= NULL; } /* - The method sends a table with a list of log files - used by the instance. - - SYNOPSYS - Show_instance_log_files::execute() - net The network connection to the client. - connection_id The ID of the client connection + Implementation of SHOW INSTANCE LOG FILES statement. - RETURN - ER_BAD_INSTANCE_NAME The instance name specified is not valid - ER_OUT_OF_RESOURCES some error occured - 0 - ok + Possible error codes: + ER_BAD_INSTANCE_NAME The instance with the given name does not exist + ER_OUT_OF_RESOURCES Not enough resources to complete the operation */ -int Show_instance_log_files::execute(struct st_net *net, ulong connection_id) +int Show_instance_log_files::execute_impl(st_net *net, Instance *instance) +{ + int err_code; + + if ((err_code= write_header(net)) || + (err_code= write_data(net, instance))) + return err_code; + + return 0; +} + + +int Show_instance_log_files::send_ok_response(st_net *net, ulong connection_id) +{ + if (send_eof(net) || net_flush(net)) + return ER_OUT_OF_RESOURCES; + + return 0; +} + + +int Show_instance_log_files::write_header(st_net *net) { - Buffer send_buff; /* buffer for packets */ LIST name, path, size; LIST *field_list; - NAME_WITH_LENGTH name_field, path_field, size_field; - uint position= 0; + LEX_STRING name_field, path_field, size_field; - if (!instance_name) - return ER_BAD_INSTANCE_NAME; + /* Create list of the fileds to be passed to send_fields(). */ - /* create list of the fileds to be passed to send_fields */ - name_field.name= (char*) "Logfile"; + name_field.str= (char *) "Logfile"; name_field.length= DEFAULT_FIELD_LENGTH; name.data= &name_field; - path_field.name= (char*) "Path"; + + path_field.str= (char *) "Path"; path_field.length= DEFAULT_FIELD_LENGTH; path.data= &path_field; - size_field.name= (char*) "File size"; + + size_field.str= (char *) "File size"; size_field.length= DEFAULT_FIELD_LENGTH; size.data= &size_field; + field_list= list_add(NULL, &size); field_list= list_add(field_list, &path); field_list= list_add(field_list, &name); - send_fields(net, field_list); + return send_fields(net, field_list) ? ER_OUT_OF_RESOURCES : 0; +} - Instance *instance; - if ((instance= instance_map-> - find(instance_name, strlen(instance_name))) == NULL) - goto err; +int Show_instance_log_files::write_data(st_net *net, Instance *instance) +{ + Buffer send_buff; /* buffer for packets */ + /* + We have alike structure in instance_options.cc. We use such to be able + to loop through the options, which we need to handle in some common way. + */ + struct log_files_st + { + const char *name; + const char *value; + } logs[]= { + {"ERROR LOG", instance->options.logs[IM_LOG_ERROR]}, + {"GENERAL LOG", instance->options.logs[IM_LOG_GENERAL]}, + {"SLOW LOG", instance->options.logs[IM_LOG_SLOW]}, + {NULL, NULL} + }; + struct log_files_st *log_files; + + for (log_files= logs; log_files->name; log_files++) + { + if (!log_files->value) + continue; + + struct stat file_stat; /* - We have alike structure in instance_options.cc. We use such to be able - to loop through the options, which we need to handle in some common way. + Save some more space for the log file names. In fact all + we need is strlen("GENERAL_LOG") + 1 */ - struct log_files_st - { - const char *name; - const char *value; - } logs[]= - { - {"ERROR LOG", instance->options.logs[IM_LOG_ERROR]}, - {"GENERAL LOG", instance->options.logs[IM_LOG_GENERAL]}, - {"SLOW LOG", instance->options.logs[IM_LOG_SLOW]}, - {NULL, NULL} - }; - struct log_files_st *log_files; - - for (log_files= logs; log_files->name; log_files++) + enum { LOG_NAME_BUFFER_SIZE= 20 }; + char buff[LOG_NAME_BUFFER_SIZE]; + + uint pos= 0; + + const char *log_path= ""; + const char *log_size= "0"; + + if (!stat(log_files->value, &file_stat) && + MY_S_ISREG(file_stat.st_mode)) { - if (log_files->value != NULL) - { - struct stat file_stat; - /* - Save some more space for the log file names. In fact all - we need is srtlen("GENERAL_LOG") + 1 - */ - enum { LOG_NAME_BUFFER_SIZE= 20 }; - char buff[LOG_NAME_BUFFER_SIZE]; - - position= 0; - /* store the type of the log in the send buffer */ - store_to_protocol_packet(&send_buff, log_files->name, &position); - if (stat(log_files->value, &file_stat)) - { - store_to_protocol_packet(&send_buff, "", &position); - store_to_protocol_packet(&send_buff, (char*) "0", &position); - } - else if (MY_S_ISREG(file_stat.st_mode)) - { - store_to_protocol_packet(&send_buff, - (char*) log_files->value, - &position); - int10_to_str(file_stat.st_size, buff, 10); - store_to_protocol_packet(&send_buff, (char*) buff, &position); - } - - if (my_net_write(net, send_buff.buffer, (uint) position)) - goto err; - } + int10_to_str(file_stat.st_size, buff, 10); + + log_path= log_files->value; + log_size= buff; } - } - if (send_eof(net) || net_flush(net)) - goto err; + if (store_to_protocol_packet(&send_buff, log_files->name, &pos) || + store_to_protocol_packet(&send_buff, log_path, &pos) || + store_to_protocol_packet(&send_buff, log_size, &pos) || + my_net_write(net, send_buff.buffer, pos)) + return ER_OUT_OF_RESOURCES; + } return 0; - -err: - return ER_OUT_OF_RESOURCES; } -/* implementation for SET instance_name.option=option_value: */ +/************************************************************************** + Implementation of Abstract_option_cmd. +**************************************************************************/ + +/* + Instance_options_list -- a data class representing a list of options for + some instance. +*/ -Set_option::Set_option(Instance_map *instance_map_arg, - const char *name, uint len, - const char *option_arg, uint option_len_arg, - const char *option_value_arg, uint option_value_len_arg) - :Command(instance_map_arg) +class Instance_options_list { - Instance *instance; +public: + Instance_options_list(const LEX_STRING *instance_name_arg); - /* we make a search here, since we don't want to store the name */ - if ((instance= instance_map->find(name, len))) - { - instance_name= instance->options.instance_name; +public: + bool init(); - /* add prefix for add_option */ - if ((option_len_arg < MAX_OPTION_LEN - 1) || - (option_value_len_arg < MAX_OPTION_LEN - 1)) - { - strmake(option, option_arg, option_len_arg); - strmake(option_value, option_value_arg, option_value_len_arg); - } - else - { - option[0]= 0; - option_value[0]= 0; - } - instance_name_len= len; - } - else + const LEX_STRING *get_instance_name() const { - instance_name= NULL; - instance_name_len= 0; + return instance_name.get_str(); } + +public: + /* + This member is set and used only in Abstract_option_cmd::execute_impl(). + Normally it is not used (and should not). + + The problem is that construction and execution of commands are made not + in one transaction (not under one lock session). So, we can not initialize + instance in constructor and use it in execution. + */ + Instance *instance; + + Named_value_arr options; + +private: + Instance_name instance_name; +}; + + +/**************************************************************************/ + +Instance_options_list::Instance_options_list( + const LEX_STRING *instance_name_arg) + :instance(NULL), + instance_name(instance_name_arg) +{ } -/* - The method sends a table with a list of log files - used by the instance. +bool Instance_options_list::init() +{ + return options.init(); +} - SYNOPSYS - Set_option::correct_file() - skip Skip the option, being searched while writing the result file. - That is, to delete it. - DESCRIPTION +/**************************************************************************/ + +C_MODE_START + +static byte* get_item_key(const byte* item, uint* len, + my_bool __attribute__((unused)) t) +{ + const Instance_options_list *lst= (const Instance_options_list *) item; + *len= lst->get_instance_name()->length; + return (byte *) lst->get_instance_name()->str; +} + +static void delete_item(void *item) +{ + delete (Instance_options_list *) item; +} + +C_MODE_END + + +/**************************************************************************/ + +Abstract_option_cmd::Abstract_option_cmd(Instance_map *instance_map_arg) + :Command(instance_map_arg), + initialized(FALSE) +{ +} + + +Abstract_option_cmd::~Abstract_option_cmd() +{ + if (initialized) + hash_free(&instance_options_map); +} + + +bool Abstract_option_cmd::add_option(const LEX_STRING *instance_name, + Named_value *option) +{ + Instance_options_list *lst= get_instance_options_list(instance_name); + if (!lst) + return TRUE; + + lst->options.add_element(option); + + return FALSE; +} + + +bool Abstract_option_cmd::init(const char **text) +{ + static const int INITIAL_HASH_SIZE= 16; + + if (hash_init(&instance_options_map, default_charset_info, + INITIAL_HASH_SIZE, 0, 0, get_item_key, delete_item, 0)) + return TRUE; + + if (parse_args(text)) + return TRUE; + + initialized= TRUE; + + return FALSE; +} + + +/* Correct the option file. The "skip" option is used to remove the found option. + SYNOPSYS + Abstract_option_cmd::correct_file() + skip Skip the option, being searched while writing the result file. + That is, to delete it. + RETURN - ER_OUT_OF_RESOURCES out of resources + 0 Success + ER_OUT_OF_RESOURCES Not enough resources to complete the operation ER_ACCESS_OPTION_FILE Cannot access the option file - 0 - ok */ -int Set_option::correct_file(int skip) +int Abstract_option_cmd::correct_file(Instance *instance, Named_value *option, + bool skip) +{ + int err_code= modify_defaults_file(Options::Main::config_file, + option->get_name(), + option->get_value(), + instance->get_name()->str, + skip); + + DBUG_ASSERT(err_code >= 0 && err_code <= 2); + + if (err_code) + { + log_error("Can not modify option (%s) in defaults file (%s). " + "Original error code: %d.", + (const char *) option->get_name(), + (const char *) Options::Main::config_file, + (int) err_code); + } + + return modify_defaults_to_im_error[err_code]; +} + + +/* + Implementation of SET statement. + + Possible error codes: + ER_BAD_INSTANCE_NAME The instance with the given name does not exist + ER_INCOMPATIBLE_OPTION The specified option can not be set for + mysqld-compatible instance + ER_INSTANCE_IS_ACTIVE The specified instance is active + ER_OUT_OF_RESOURCES Not enough resources to complete the operation +*/ + +int Abstract_option_cmd::execute(st_net *net, ulong connection_id) +{ + int err_code; + + instance_map->lock(); + + err_code= execute_impl(net, connection_id); + + instance_map->unlock(); + + return err_code; +} + + +Instance_options_list * +Abstract_option_cmd::get_instance_options_list(const LEX_STRING *instance_name) { - static const int mysys_to_im_error[]= { 0, ER_OUT_OF_RESOURCES, - ER_ACCESS_OPTION_FILE }; - int error; + Instance_options_list *lst= + (Instance_options_list *) hash_search(&instance_options_map, + (byte *) instance_name->str, + instance_name->length); + + if (!lst) + { + lst= new Instance_options_list(instance_name); - error= modify_defaults_file(Options::config_file, option, - option_value, instance_name, skip); - DBUG_ASSERT(error >= 0 && error <= 2); + if (!lst) + return NULL; - return mysys_to_im_error[error]; + if (lst->init() || my_hash_insert(&instance_options_map, (byte *) lst)) + { + delete lst; + return NULL; + } + } + + return lst; +} + + +int Abstract_option_cmd::execute_impl(st_net *net, ulong connection_id) +{ + int err_code; + + /* Check that all the specified instances exist and are offline. */ + + for (uint i= 0; i < instance_options_map.records; ++i) + { + Instance_options_list *lst= + (Instance_options_list *) hash_element(&instance_options_map, i); + + lst->instance= instance_map->find(lst->get_instance_name()); + + if (!lst->instance) + return ER_BAD_INSTANCE_NAME; + + if (instance_map->guardian->is_active(lst->instance)) + return ER_INSTANCE_IS_ACTIVE; + } + + /* Perform command-specific (SET/UNSET) actions. */ + + for (uint i= 0; i < instance_options_map.records; ++i) + { + Instance_options_list *lst= + (Instance_options_list *) hash_element(&instance_options_map, i); + + for (int j= 0; j < lst->options.get_size(); ++j) + { + Named_value option= lst->options.get_element(j); + err_code= process_option(lst->instance, &option); + + if (err_code) + break; + } + + if (err_code) + break; + } + + if (err_code == 0) + net_send_ok(net, connection_id, NULL); + + return err_code; +} + + +/************************************************************************** + Implementation of Set_option. +**************************************************************************/ + +Set_option::Set_option(Instance_map *instance_map_arg) + :Abstract_option_cmd(instance_map_arg) +{ } /* - The method sets an option in the the default config file (/etc/my.cnf). + This operation parses SET options. SYNOPSYS - Set_option::do_command() - net The network connection to the client. + text [IN/OUT] a pointer to the text containing options. RETURN - 0 - ok - 1 - error occured + FALSE On success. + TRUE On syntax error. */ -int Set_option::do_command(struct st_net *net) +bool Set_option::parse_args(const char **text) { - int error; + uint len; - /* we must hold the instance_map mutex while changing config file */ - instance_map->lock(); - error= correct_file(FALSE); - instance_map->unlock(); + /* Check if we have something (and trim leading spaces). */ + + get_word(text, &len, NONSPACE); + + if (len == 0) + return TRUE; /* Syntax error: no option. */ + + /* Main parsing loop. */ + + while (TRUE) + { + LEX_STRING instance_name; + LEX_STRING option_name; + char *option_name_str; + char *option_value_str= NULL; + + /* Looking for instance name. */ + + get_word(text, &instance_name.length, ALPHANUM); + + if (instance_name.length == 0) + return TRUE; /* Syntax error: instance name expected. */ + + instance_name.str= (char *) *text; + *text+= instance_name.length; + + skip_spaces(text); + + /* Check the the delimiter is a dot. */ + + if (**text != '.') + return TRUE; /* Syntax error: dot expected. */ + + ++(*text); + + /* Looking for option name. */ + + get_word(text, &option_name.length, OPTION_NAME); + + if (option_name.length == 0) + return TRUE; /* Syntax error: option name expected. */ + + option_name.str= (char *) *text; + *text+= option_name.length; + + /* Looking for equal sign. */ + + skip_spaces(text); + + if (**text == '=') + { + ++(*text); /* Skip an equal sign. */ + + /* Looking for option value. */ + + skip_spaces(text); + + if (!**text) + return TRUE; /* Syntax error: EOS when option value expected. */ + + if (**text != '\'' && **text != '"') + { + /* Option value is a simple token. */ + + LEX_STRING option_value; + + get_word(text, &option_value.length, ALPHANUM); + + if (option_value.length == 0) + return TRUE; /* internal parser error. */ + + option_value.str= (char *) *text; + *text+= option_value.length; - return error; + if (!(option_value_str= Named_value::alloc_str(&option_value))) + return TRUE; /* out of memory during parsing. */ + } + else + { + /* Option value is a string. */ + + if (parse_option_value(*text, &len, &option_value_str)) + return TRUE; /* Syntax error: invalid string specification. */ + + *text+= len; + } + } + + if (!option_value_str) + { + LEX_STRING empty_str= { C_STRING_WITH_SIZE("") }; + + if (!(option_value_str= Named_value::alloc_str(&empty_str))) + return TRUE; /* out of memory during parsing. */ + } + + if (!(option_name_str= Named_value::alloc_str(&option_name))) + { + Named_value::free_str(&option_name_str); + return TRUE; /* out of memory during parsing. */ + } + + { + Named_value option(option_name_str, option_value_str); + + if (add_option(&instance_name, &option)) + { + option.free(); + return TRUE; /* out of memory during parsing. */ + } + } + + skip_spaces(text); + + if (!**text) + return FALSE; /* OK: end of options. */ + + if (**text != ',') + return TRUE; /* Syntax error: comma expected. */ + + ++(*text); /* Skip a comma. */ + } } -int Set_option::execute(struct st_net *net, ulong connection_id) +int Set_option::process_option(Instance *instance, Named_value *option) { - if (instance_name != NULL) + /* Check that the option is valid. */ + + if (instance->is_mysqld_compatible() && + Instance_options::is_option_im_specific(option->get_name())) { - int val; + log_error("Error: IM-option (%s) can not be used " + "in the configuration of mysqld-compatible instance (%s).", + (const char *) option->get_name(), + (const char *) instance->get_name()->str); + return ER_INCOMPATIBLE_OPTION; + } - val= do_command(net); + /* Update the configuration file. */ - if (val == 0) - net_send_ok(net, connection_id, NULL); + int err_code= correct_file(instance, option, FALSE); - return val; - } + if (err_code) + return err_code; + + /* Update the internal cache. */ - return ER_BAD_INSTANCE_NAME; + if (instance->options.set_option(option)) + return ER_OUT_OF_RESOURCES; + + return 0; } -/* the only function from Unset_option we need to Implement */ +/************************************************************************** + Implementation of Unset_option. +**************************************************************************/ -int Unset_option::do_command(struct st_net *net) +Unset_option::Unset_option(Instance_map *instance_map_arg) + :Abstract_option_cmd(instance_map_arg) { - return correct_file(TRUE); } -/* Implementation for Stop_instance: */ +/* + This operation parses UNSET options. -Stop_instance::Stop_instance(Instance_map *instance_map_arg, - const char *name, uint len) - :Command(instance_map_arg) + SYNOPSYS + text [IN/OUT] a pointer to the text containing options. + + RETURN + FALSE On success. + TRUE On syntax error. +*/ + +bool Unset_option::parse_args(const char **text) { - /* we make a search here, since we don't want to store the name */ - if ((instance= instance_map->find(name, len))) - instance_name= instance->options.instance_name; + uint len; + + /* Check if we have something (and trim leading spaces). */ + + get_word(text, &len, NONSPACE); + + if (len == 0) + return TRUE; /* Syntax error: no option. */ + + /* Main parsing loop. */ + + while (TRUE) + { + LEX_STRING instance_name; + LEX_STRING option_name; + char *option_name_str; + char *option_value_str; + + /* Looking for instance name. */ + + get_word(text, &instance_name.length, ALPHANUM); + + if (instance_name.length == 0) + return TRUE; /* Syntax error: instance name expected. */ + + instance_name.str= (char *) *text; + *text+= instance_name.length; + + skip_spaces(text); + + /* Check the the delimiter is a dot. */ + + if (**text != '.') + return TRUE; /* Syntax error: dot expected. */ + + ++(*text); /* Skip a dot. */ + + /* Looking for option name. */ + + get_word(text, &option_name.length, OPTION_NAME); + + if (option_name.length == 0) + return TRUE; /* Syntax error: option name expected. */ + + option_name.str= (char *) *text; + *text+= option_name.length; + + if (!(option_name_str= Named_value::alloc_str(&option_name))) + return TRUE; /* out of memory during parsing. */ + + { + LEX_STRING empty_str= { C_STRING_WITH_SIZE("") }; + + if (!(option_value_str= Named_value::alloc_str(&empty_str))) + { + Named_value::free_str(&option_name_str); + return TRUE; + } + } + + { + Named_value option(option_name_str, option_value_str); + + if (add_option(&instance_name, &option)) + { + option.free(); + return TRUE; /* out of memory during parsing. */ + } + } + + skip_spaces(text); + + if (!**text) + return FALSE; /* OK: end of options. */ + + if (**text != ',') + return TRUE; /* Syntax error: comma expected. */ + + ++(*text); /* Skip a comma. */ + } } -int Stop_instance::execute(struct st_net *net, ulong connection_id) -{ - uint err_code; +/* + Implementation of UNSET statement. - if (instance == 0) - return ER_BAD_INSTANCE_NAME; /* haven't found an instance */ + Possible error codes: + ER_BAD_INSTANCE_NAME The instance name specified is not valid + ER_INSTANCE_IS_ACTIVE The specified instance is active + ER_OUT_OF_RESOURCES Not enough resources to complete the operation +*/ - if (!(instance->options.nonguarded)) - instance_map->guardian->stop_guard(instance); +int Unset_option::process_option(Instance *instance, Named_value *option) +{ + /* Update the configuration file. */ - if ((err_code= instance->stop())) + int err_code= correct_file(instance, option, TRUE); + + if (err_code) return err_code; - net_send_ok(net, connection_id, NULL); + /* Update the internal cache. */ + + instance->options.unset_option(option->get_name()); + return 0; } -int Syntax_error::execute(struct st_net *net, ulong connection_id) +/************************************************************************** + Implementation of Syntax_error. +**************************************************************************/ + +int Syntax_error::execute(st_net *net, ulong connection_id) { return ER_SYNTAX_ERROR; } |