From f6834a5de08024c795a0506bbe906d4e6756c131 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 2 Sep 2004 16:20:30 +0500 Subject: [PATCH 01/42] A fix (bug #5056: defaults-extra-file throws no error when file is inaccessible). --- mysys/default.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/mysys/default.c b/mysys/default.c index 81290322223..e8195893ad8 100644 --- a/mysys/default.c +++ b/mysys/default.c @@ -164,8 +164,11 @@ int load_defaults(const char *conf_file, const char **groups, if (forced_default_file) { if ((error= search_default_file(&args, &alloc, "", - forced_default_file, "", &group)) < 0) + forced_default_file, "", &group))) + { + fprintf(stderr, "Error reading '%s' file.\n", forced_default_file); goto err; + } } else if (dirname_length(conf_file)) { @@ -199,8 +202,11 @@ int load_defaults(const char *conf_file, const char **groups, else if (defaults_extra_file) { if (search_default_file(&args, &alloc, NullS, defaults_extra_file, - default_ext, &group) < 0) + default_ext, &group)) + { + fprintf(stderr, "Error reading '%s' file.\n", defaults_extra_file); goto err; /* Fatal error */ + } } } } From 47f8a473bd1179c7622deab59b491f5a9d91b211 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 11 Oct 2004 18:38:48 +0500 Subject: [PATCH 02/42] A fix (bug #5615 type of aggregate function column wrong when using group by). mysql-test/r/func_group.result: test case (bug #5615 type of aggregate function column wrong when using group by) mysql-test/t/func_group.test: test case (bug #5615 type of aggregate function column wrong when using group by) sql/sql_select.cc: A fix (bug #5615 type of aggregate function column wrong when using group by): should create a temporary field of the proper type in case of MIN|MAX(field). --- mysql-test/r/func_group.result | 9 +++++++++ mysql-test/t/func_group.test | 10 ++++++++++ sql/sql_select.cc | 9 +++++++++ 3 files changed, 28 insertions(+) diff --git a/mysql-test/r/func_group.result b/mysql-test/r/func_group.result index c25f89d4df3..ecf6422261f 100644 --- a/mysql-test/r/func_group.result +++ b/mysql-test/r/func_group.result @@ -719,3 +719,12 @@ one 2 two 2 three 1 drop table t1; +create table t1(a int, b datetime); +insert into t1 values (1, NOW()), (2, NOW()); +create table t2 select MAX(b) from t1 group by a; +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `MAX(b)` datetime default NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +drop table t1, t2; diff --git a/mysql-test/t/func_group.test b/mysql-test/t/func_group.test index 3e001961f90..ecd3bf97f0b 100644 --- a/mysql-test/t/func_group.test +++ b/mysql-test/t/func_group.test @@ -458,3 +458,13 @@ INSERT INTO t1 VALUES select val, count(*) from t1 group by val; drop table t1; + +# +# Bug #5615: type of aggregate function column wrong when using group by +# + +create table t1(a int, b datetime); +insert into t1 values (1, NOW()), (2, NOW()); +create table t2 select MAX(b) from t1 group by a; +show create table t2; +drop table t1, t2; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 638ed229a70..7ffef151457 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -4744,6 +4744,15 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, item->name,table,item_sum->decimals); case Item_sum::UNIQUE_USERS_FUNC: return new Field_long(9,maybe_null,item->name,table,1); + case Item_sum::MIN_FUNC: + case Item_sum::MAX_FUNC: + if (item_sum->args[0]->type() == Item::FIELD_ITEM) + { + *from_field= ((Item_field*) item_sum->args[0])->field; + return create_tmp_field_from_field(thd, *from_field, item, table, + modify_item, convert_blob_length); + } + /* fall through */ default: switch (item_sum->result_type()) { case REAL_RESULT: From 556950da46e61a1a04f0531f6d96ec60c7ede79c Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 11 Oct 2004 19:17:13 +0500 Subject: [PATCH 03/42] An addition (see ChangeSet 1.2082 04/10/11 18:38:48 ram@gw.mysql.r18.ru +3 -0 A fix (bug #5615 type of aggregate function column wrong when using group by).) Shouldn't it be 'int(11) not null' == the same as in the t1? --- mysql-test/r/show_check.result | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result index 8256c8d692a..5ea17c93f48 100644 --- a/mysql-test/r/show_check.result +++ b/mysql-test/r/show_check.result @@ -250,9 +250,11 @@ type_bool type_tiny type_short type_mediumint type_bigint type_decimal type_nume drop table t1; create table t1 (a int not null); create table t2 select max(a) from t1; +Warnings: +Warning 1263 Data truncated; NULL supplied to NOT NULL column 'max(a)' at row 1 show columns from t2; Field Type Null Key Default Extra -max(a) bigint(20) YES NULL +max(a) int(11) 0 drop table t1,t2; create table t1 (c decimal, d double, f float, r real); show columns from t1; From 70fe41d2ec0fccdd54f643d832b67274a8f76f16 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 21 Oct 2004 17:02:24 +0500 Subject: [PATCH 04/42] A fix (bug #4802 prompt in mysql client shows wrong database after dropping default db). client/mysql.cc: A fix (bug #4802 prompt in mysql client shows wrong database after dropping default db). Introduced new get_current_db() function which is called from the com_use() and the com_go() if we get SERVER_STATUS_DB_DROPPED. include/mysql_com.h: A fix (bug #4802 prompt in mysql client shows wrong database after dropping default db). SERVER_STATUS_DB_DROPPED flag added. Note: it is set to 256 to don't conflict with 5.0 ver. sql/sql_db.cc: A fix (bug #4802 prompt in mysql client shows wrong database after dropping default db). SERVER_STATUS_DB_DROPPED flag is set/unset. --- client/mysql.cc | 38 ++++++++++++++++++++------------------ include/mysql_com.h | 1 + sql/sql_db.cc | 2 ++ 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/client/mysql.cc b/client/mysql.cc index 05d5d1355ad..18dc4dd76cb 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -1574,6 +1574,22 @@ static int reconnect(void) return 0; } +static void get_current_db() +{ + MYSQL_RES *res; + + my_free(current_db, MYF(MY_ALLOW_ZERO_PTR)); + current_db= NULL; + /* In case of error below current_db will be NULL */ + if (!mysql_query(&mysql, "SELECT DATABASE()") && + (res= mysql_use_result(&mysql))) + { + MYSQL_ROW row= mysql_fetch_row(res); + if (row[0]) + current_db= my_strdup(row[0], MYF(MY_WME)); + mysql_free_result(res); + } +} /*************************************************************************** The different commands @@ -1899,6 +1915,9 @@ com_go(String *buffer,char *line __attribute__((unused))) if (err >= 1) error= put_error(&mysql); + if (!status.batch && (mysql.server_status & SERVER_STATUS_DB_DROPPED)) + get_current_db(); + return error; /* New command follows */ } @@ -2614,24 +2633,7 @@ com_use(String *buffer __attribute__((unused)), char *line) under our feet, for example if DROP DATABASE or RENAME DATABASE (latter one not yet available by the time the comment was written) */ - /* Let's reset current_db, assume it's gone */ - my_free(current_db, MYF(MY_ALLOW_ZERO_PTR)); - current_db= 0; - /* - We don't care about in case of an error below because current_db - was just set to 0. - */ - if (!mysql_query(&mysql, "SELECT DATABASE()") && - (res= mysql_use_result(&mysql))) - { - row= mysql_fetch_row(res); - if (row[0]) - { - current_db= my_strdup(row[0], MYF(MY_WME)); - } - (void) mysql_fetch_row(res); // Read eof - mysql_free_result(res); - } + get_current_db(); if (!current_db || cmp_database(charset_info, current_db,tmp)) { diff --git a/include/mysql_com.h b/include/mysql_com.h index 449cd0ba396..6fc1d106197 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -127,6 +127,7 @@ enum enum_server_command #define SERVER_MORE_RESULTS_EXISTS 8 /* Multi query - next query exists */ #define SERVER_QUERY_NO_GOOD_INDEX_USED 16 #define SERVER_QUERY_NO_INDEX_USED 32 +#define SERVER_STATUS_DB_DROPPED 256 /* A database was dropped */ #define MYSQL_ERRMSG_SIZE 512 #define NET_READ_TIMEOUT 30 /* Timeout on read */ diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 426f7d36633..e5b426ef508 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -634,7 +634,9 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) thd->clear_error(); mysql_bin_log.write(&qinfo); } + thd->server_status|= SERVER_STATUS_DB_DROPPED; send_ok(thd, (ulong) deleted); + thd->server_status&= !SERVER_STATUS_DB_DROPPED; } exit: From 3bffc522317ad18102f8a2e5079e3242b6866dcf Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 27 Oct 2004 14:51:17 +0500 Subject: [PATCH 05/42] A fix (bug #6089: FOUND_ROWS returns wrong values when no table/view is used). mysql-test/r/ps.result: A fix (bug #6089: FOUND_ROWS returns wrong values when no table/view is used). The second FOUND_ROWS() should return 1. sql/sql_class.cc: A fix (bug #6089: FOUND_ROWS returns wrong values when no table/view is used). limit_found_rows initialization added. sql/sql_select.cc: A fix (bug #6089: FOUND_ROWS returns wrong values when no table/view is used). thd->limit_found_rows is set to 1 when no table is used. --- mysql-test/r/ps.result | 2 +- mysql-test/r/select_found.result | 25 +++++++++++++++++++++++++ mysql-test/t/select_found.test | 15 +++++++++++++++ sql/sql_class.cc | 1 + sql/sql_select.cc | 4 +++- 5 files changed, 45 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 6cad58282a2..8c55bb08249 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -135,7 +135,7 @@ FOUND_ROWS() 1 execute stmt1; FOUND_ROWS() -0 +1 deallocate prepare stmt1; drop table t1; create table t1 diff --git a/mysql-test/r/select_found.result b/mysql-test/r/select_found.result index 00dbcb54d93..1eaa7033c99 100644 --- a/mysql-test/r/select_found.result +++ b/mysql-test/r/select_found.result @@ -246,3 +246,28 @@ SELECT FOUND_ROWS(); FOUND_ROWS() 0 DROP TABLE t1; +SELECT 'foo'; +foo +foo +SELECT FOUND_ROWS(); +FOUND_ROWS() +1 +SELECT SQL_CALC_FOUND_ROWS 'foo'; +foo +foo +SELECT FOUND_ROWS(); +FOUND_ROWS() +1 +SELECT SQL_CALC_FOUND_ROWS 'foo' limit 0; +foo +SELECT FOUND_ROWS(); +FOUND_ROWS() +1 +SELECT FOUND_ROWS(); +FOUND_ROWS() +1 +SELECT SQL_CALC_FOUND_ROWS 'foo' UNION SELECT 'bar' LIMIT 0; +foo +SELECT FOUND_ROWS(); +FOUND_ROWS() +2 diff --git a/mysql-test/t/select_found.test b/mysql-test/t/select_found.test index 943174462e3..e51dd2442b9 100644 --- a/mysql-test/t/select_found.test +++ b/mysql-test/t/select_found.test @@ -166,3 +166,18 @@ INSERT INTO t1 VALUES (0), (0), (1), (2); SELECT SQL_CALC_FOUND_ROWS * FROM t1 WHERE a = 0 GROUP BY a HAVING a > 10; SELECT FOUND_ROWS(); DROP TABLE t1; + +# +# Bug #6089: queries which don't use any tables +# + +SELECT 'foo'; +SELECT FOUND_ROWS(); +SELECT SQL_CALC_FOUND_ROWS 'foo'; +SELECT FOUND_ROWS(); +SELECT SQL_CALC_FOUND_ROWS 'foo' limit 0; +SELECT FOUND_ROWS(); +SELECT FOUND_ROWS(); + +SELECT SQL_CALC_FOUND_ROWS 'foo' UNION SELECT 'bar' LIMIT 0; +SELECT FOUND_ROWS(); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index abe00027b07..b6fdac03526 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -177,6 +177,7 @@ THD::THD() lock=locked_tables=0; used_tables=0; cuted_fields= sent_row_count= 0L; + limit_found_rows= 0; statement_id_counter= 0UL; // Must be reset to handle error with THD's created for init of mysqld lex->current_select= 0; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index df74a946b5c..1610057f877 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1067,7 +1067,9 @@ JOIN::exec() else error=(int) result->send_eof(); } - thd->limit_found_rows= thd->examined_row_count= 0; + /* Single select (without union and limit) always returns 1 row */ + thd->limit_found_rows= 1; + thd->examined_row_count= 0; DBUG_VOID_RETURN; } thd->limit_found_rows= thd->examined_row_count= 0; From fb3d6c39a0dc128f5310aac2906cc1e6c6b7db59 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 25 Jan 2005 13:54:56 +0300 Subject: [PATCH 06/42] IM mostly fixed according to Brian's directions. Will need to do some additional option handling and cleanups server-tools/instance-manager/Makefile.am: New file added server-tools/instance-manager/client_func.c: typo fixed server-tools/instance-manager/commands.cc: there are no admin-user snd admin-password fields anymore, so no need to show their values server-tools/instance-manager/guardian.cc: Syncronization added -- now guardian wakes up whenever SIGCLD has been catched server-tools/instance-manager/guardian.h: Condition variable declared server-tools/instance-manager/instance.cc: Persistent connection to the instance removed. Now we use SIGTERM instead of com_shutdown for STOP. We also manage SIGCHLD ourselves now (therefore no double fork). server-tools/instance-manager/instance.h: Pointer to the instance_map added, MySQL connection structures removed server-tools/instance-manager/instance_map.cc: More syncronization added (to make proper STOP) server-tools/instance-manager/instance_map.h: added condition variable and mutex for connection threads to wait for SIGCHLD server-tools/instance-manager/instance_options.cc: defaults-handling methods have been added. server-tools/instance-manager/instance_options.h: New functions and constants declared server-tools/instance-manager/listener.cc: No changes here (bk bug?) server-tools/instance-manager/manager.cc: SIGCHLD handling added --- server-tools/instance-manager/Makefile.am | 1 + server-tools/instance-manager/client_func.c | 2 +- server-tools/instance-manager/commands.cc | 24 +-- server-tools/instance-manager/guardian.cc | 15 +- server-tools/instance-manager/guardian.h | 3 + server-tools/instance-manager/instance.cc | 138 ++++++++++++------ server-tools/instance-manager/instance.h | 11 +- server-tools/instance-manager/instance_map.cc | 9 +- server-tools/instance-manager/instance_map.h | 8 + .../instance-manager/instance_options.cc | 135 ++++++++++++++--- .../instance-manager/instance_options.h | 18 ++- server-tools/instance-manager/manager.cc | 22 ++- server-tools/instance-manager/parse_output.cc | 82 +++++++++++ server-tools/instance-manager/parse_output.h | 19 +++ 14 files changed, 380 insertions(+), 107 deletions(-) create mode 100644 server-tools/instance-manager/parse_output.cc create mode 100644 server-tools/instance-manager/parse_output.h diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index c2bf501eca7..fcc4a60c7a8 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -75,6 +75,7 @@ mysqlmanager_SOURCES= command.cc command.h mysqlmanager.cc \ instance_options.h instance_options.cc \ buffer.h buffer.cc parse.cc parse.h \ guardian.cc guardian.h \ + parse_output.cc parse_output.h \ mysql_manager_error.h client_func.c mysqlmanager_LDADD= liboptions.a \ diff --git a/server-tools/instance-manager/client_func.c b/server-tools/instance-manager/client_func.c index a7ff1d27a8f..92c106e5172 100644 --- a/server-tools/instance-manager/client_func.c +++ b/server-tools/instance-manager/client_func.c @@ -3,7 +3,7 @@ #include /* - Currently we cannot use libmysqlclient directly becouse of the linking + Currently we cannot use libmysqlclient directly because of the linking issues. Here we provide needed libmysqlclient functions. TODO: to think how to use libmysqlclient code instead of copy&paste. The other possible solution is to use simple_command directly. diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc index dacace1af1e..4922aba4a06 100644 --- a/server-tools/instance-manager/commands.cc +++ b/server-tools/instance-manager/commands.cc @@ -175,7 +175,7 @@ int Show_instance_status::do_command(struct st_net *net, if (instance->is_running()) { store_to_string(&send_buff, (char *) "online", &position); - store_to_string(&send_buff, mysql_get_server_info(&(instance->mysql)), &position); + store_to_string(&send_buff, "unknown", &position); } else { @@ -283,28 +283,6 @@ int Show_instance_options::do_command(struct st_net *net, goto err; } - if (instance->options.mysqld_user != NULL) - { - position= 0; - store_to_string(&send_buff, (char *) "admin-user", &position); - store_to_string(&send_buff, - (char *) instance->options.mysqld_user, - &position); - if (my_net_write(net, send_buff.buffer, (uint) position)) - goto err; - } - - if (instance->options.mysqld_password != NULL) - { - position= 0; - store_to_string(&send_buff, (char *) "admin-password", &position); - store_to_string(&send_buff, - (char *) instance->options.mysqld_password, - &position); - if (my_net_write(net, send_buff.buffer, (uint) position)) - goto err; - } - /* loop through the options stored in DYNAMIC_ARRAY */ for (uint i= 0; i < instance->options.options_array.elements; i++) { diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc index 7375453673b..f68ef6575a0 100644 --- a/server-tools/instance-manager/guardian.cc +++ b/server-tools/instance-manager/guardian.cc @@ -45,6 +45,7 @@ Guardian_thread::Guardian_thread(Thread_registry &thread_registry_arg, thread_info(pthread_self()) { pthread_mutex_init(&LOCK_guardian, 0); + pthread_cond_init(&COND_guardian, 0); thread_registry.register_thread(&thread_info); init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0); guarded_instances= NULL; @@ -60,6 +61,7 @@ Guardian_thread::~Guardian_thread() thread_registry.unregister_thread(&thread_info); pthread_mutex_unlock(&LOCK_guardian); pthread_mutex_destroy(&LOCK_guardian); + pthread_cond_destroy(&COND_guardian); } @@ -79,27 +81,32 @@ void Guardian_thread::run() { Instance *instance; LIST *loop; + struct timespec timeout; my_thread_init(); + pthread_mutex_lock(&LOCK_guardian); + while (!thread_registry.is_shutdown()) { - pthread_mutex_lock(&LOCK_guardian); loop= guarded_instances; while (loop != NULL) { instance= (Instance *) loop->data; - /* instance-> start already checks whether instance is running */ + /* instance-> start already checks whether the instance is running */ if (instance->start() != ER_INSTANCE_ALREADY_STARTED) log_info("guardian attempted to restart instance %s", instance->options.instance_name); loop= loop->next; } move_to_list(&starting_instances, &guarded_instances); - pthread_mutex_unlock(&LOCK_guardian); - sleep(monitoring_interval); + timeout.tv_sec= time(NULL) + monitoring_interval; + timeout.tv_nsec= 0; + + pthread_cond_timedwait(&COND_guardian, &LOCK_guardian, &timeout); } + pthread_mutex_unlock(&LOCK_guardian); my_thread_end(); } diff --git a/server-tools/instance-manager/guardian.h b/server-tools/instance-manager/guardian.h index 0ae2161f1dc..bcc249ed7d3 100644 --- a/server-tools/instance-manager/guardian.h +++ b/server-tools/instance-manager/guardian.h @@ -70,6 +70,9 @@ public: int guard(Instance *instance); int stop_guard(Instance *instance); +public: + pthread_cond_t COND_guardian; + private: int add_instance_to_list(Instance *instance, LIST **list); void move_to_list(LIST **from, LIST **to); diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc index 58bc5b85dd8..edb9ea61ad3 100644 --- a/server-tools/instance-manager/instance.cc +++ b/server-tools/instance-manager/instance.cc @@ -21,6 +21,7 @@ #include "instance.h" #include "mysql_manager_error.h" #include "log.h" +#include "instance_map.h" #include #include #include @@ -41,24 +42,18 @@ int Instance::start() { - pid_t pid; - + /* echk for the pidfile and remove it */ if (!is_running()) { - log_info("trying to start instance %s", options.instance_name); + stop(); + log_info("starting instance %s", options.instance_name); switch (pid= fork()) { case 0: - if (fork()) /* zombie protection */ - exit(0); /* parent goes bye-bye */ - else - { - execv(options.mysqld_path, options.argv); - exit(1); - } + execv(options.mysqld_path, options.argv); + exit(1); case -1: return ER_CANNOT_START_INSTANCE; default: - waitpid(pid, NULL, 0); return 0; } } @@ -67,15 +62,9 @@ int Instance::start() return ER_INSTANCE_ALREADY_STARTED; } + int Instance::cleanup() { - /* - We cannot close connection in destructor, as mysql_close needs alarm - services which are definitely unavailaible at the time of destructor - call. - */ - if (is_connected) - mysql_close(&mysql); return 0; } @@ -88,8 +77,13 @@ Instance::~Instance() bool Instance::is_running() { + MYSQL mysql; uint port= 0; const char *socket= NULL; + const char *password= "321rarepassword213"; + const char *username= "645rareusername945"; + const char *access_denied_message= "Access denied for user"; + bool return_val; if (options.mysqld_port) port= atoi(strchr(options.mysqld_port, '=') + 1); @@ -98,30 +92,40 @@ bool Instance::is_running() socket= strchr(options.mysqld_socket, '=') + 1; pthread_mutex_lock(&LOCK_instance); - if (!is_connected) + + mysql_init(&mysql); + /* try to connect to a server with the fake username/password pair */ + if (mysql_real_connect(&mysql, LOCAL_HOST, username, + password, + NullS, port, + socket, 0)) { - mysql_init(&mysql); - if (mysql_real_connect(&mysql, LOCAL_HOST, options.mysqld_user, - options.mysqld_password, - NullS, port, - socket, 0)) - { - mysql.reconnect= 1; - is_connected= TRUE; - pthread_mutex_unlock(&LOCK_instance); - return TRUE; - } + /* + Very strange. We have successfully connected to the server using + bullshit as username/password. Write a warning to the logfile. + */ + log_info("The Instance Manager was able to log into you server \ + with faked compiled-in password while checking server status. \ + Looks like something is wrong."); mysql_close(&mysql); pthread_mutex_unlock(&LOCK_instance); - return FALSE; + return_val= TRUE; /* server is alive */ } - else if (!mysql_ping(&mysql)) + else { - pthread_mutex_unlock(&LOCK_instance); - return TRUE; + if (!strncmp(access_denied_message, mysql_error(&mysql), + sizeof(access_denied_message)-1)) + { + return_val= TRUE; + } + else + return_val= FALSE; } + + mysql_close(&mysql); pthread_mutex_unlock(&LOCK_instance); - return FALSE; + + return return_val; } @@ -139,13 +143,58 @@ bool Instance::is_running() int Instance::stop() { - if (is_running()) - { - if (mysql_shutdown(&mysql, SHUTDOWN_DEFAULT)) - goto err; + pid_t pid; + struct timespec timeout; + time_t waitchild= 35; /* */ + + if ((pid= options.get_pid()) != 0) /* get pid from pidfile */ + { + /* + If we cannot kill mysqld, then it has propably crashed. + Let us try to remove staled pidfile and return succes as mysqld + is stopped + */ + if (kill(pid, SIGTERM)) + { + if (options.unlink_pidfile()) + log_error("cannot remove pidfile for instance %i, this might be \ + since IM lacks permmissions or hasn't found the pidifle", + options.instance_name); + + log_error("The instance %s has probably crashed or IM lacks permissions \ + to kill it. in either case something seems to be wrong. \ + Check your setup", options.instance_name); + return 0; + } + + /* sleep on condition to wait for SIGCHLD */ + + timeout.tv_sec= time(NULL) + waitchild; + timeout.tv_nsec= 0; + if (pthread_mutex_lock(&instance_map->pid_cond.LOCK_pid)) + goto err; /* perhaps this should be procecced differently */ + + while (options.get_pid() != 0) + { + int status; + + status= pthread_cond_timedwait(&instance_map->pid_cond.COND_pid, + &instance_map->pid_cond.LOCK_pid, + &timeout); + if (status == ETIMEDOUT) + break; + } + + pthread_mutex_unlock(&instance_map->pid_cond.LOCK_pid); + + if (!kill(pid, SIGKILL)) + { + log_error("The instance %s has been stopped forsibly. Normally \ + it should not happed. Probably the instance has been \ + hanging. You should also check your IM setup", + options.instance_name); + } - mysql_close(&mysql); - is_connected= FALSE; return 0; } @@ -166,3 +215,10 @@ int Instance::init(const char *name_arg) return options.init(name_arg); } + + +int Instance::complete_initialization(Instance_map *instance_map_arg) +{ + instance_map= instance_map_arg; + return 0; +} diff --git a/server-tools/instance-manager/instance.h b/server-tools/instance-manager/instance.h index 6733985116a..78b7768c121 100644 --- a/server-tools/instance-manager/instance.h +++ b/server-tools/instance-manager/instance.h @@ -25,14 +25,14 @@ #pragma interface #endif +class Instance_map; + class Instance { public: - Instance(): is_connected(FALSE) - {} ~Instance(); - int init(const char *name); + int complete_initialization(Instance_map *instance_map_arg); /* check if the instance is running and set up mysql connection if yes */ bool is_running(); @@ -44,7 +44,7 @@ public: Instance_options options; /* connection to the instance */ - MYSQL mysql; + pid_t pid; private: /* @@ -53,8 +53,7 @@ private: and we issue the start command once more. */ pthread_mutex_t LOCK_instance; - /* Here we store the state of the following connection */ - bool is_connected; + Instance_map *instance_map; }; #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_INSTANCE_H */ diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index 9399d6e2563..2573729841b 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -123,6 +123,9 @@ Instance_map::Instance_map(const char *default_mysqld_path_arg, int Instance_map::init() { + pthread_mutex_init(&pid_cond.LOCK_pid, 0); + pthread_cond_init(&pid_cond.COND_pid, 0); + if (hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0, get_instance_key, delete_instance, 0)) return 1; @@ -135,6 +138,8 @@ Instance_map::~Instance_map() hash_free(&hash); pthread_mutex_unlock(&LOCK_instance_map); pthread_mutex_destroy(&LOCK_instance_map); + pthread_mutex_destroy(&pid_cond.LOCK_pid); + pthread_cond_destroy(&pid_cond.COND_pid); } @@ -189,6 +194,7 @@ void Instance_map::complete_initialization() while (i < hash.records) { instance= (Instance *) hash_element(&hash, i); + instance->complete_initialization(this); instance->options.complete_initialization(mysqld_path, user, password); i++; } @@ -218,7 +224,8 @@ Instance_map::find(uint instance_number) Instance *instance; char name[80]; - sprintf(name, "mysqld%i", instance_number); + snprintf(name, sizeof(name) - 1, "mysqld%i", instance_number); + name[sizeof(name) - 1]= 0; /* safety */ pthread_mutex_lock(&LOCK_instance_map); instance= (Instance *) hash_search(&hash, (byte *) name, strlen(name)); pthread_mutex_unlock(&LOCK_instance_map); diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h index 522785ce9b9..1f6072f2582 100644 --- a/server-tools/instance-manager/instance_map.h +++ b/server-tools/instance-manager/instance_map.h @@ -27,6 +27,12 @@ #include "protocol.h" #include "guardian.h" +typedef struct st_instance_cond +{ + pthread_mutex_t LOCK_pid; + pthread_cond_t COND_pid; +} CHILD_COND; + class Instance; extern int load_all_groups(char ***groups, const char *filename); extern void free_groups(char **groups); @@ -83,6 +89,8 @@ public: const char *user; const char *password; Guardian_thread *guardian; + /* structure used for syncronization reasons in the stop command */ + CHILD_COND pid_cond; private: enum { START_HASH_SIZE = 16 }; diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc index fab69865c85..c29f136955e 100644 --- a/server-tools/instance-manager/instance_options.cc +++ b/server-tools/instance-manager/instance_options.cc @@ -19,20 +19,105 @@ #endif #include "instance_options.h" +#include "parse_output.h" +#include "buffer.h" #include #include #include #include + +/* option_name should be prefixed with "--" */ +int Instance_options::get_default_option(char *result, const char *option_name, + size_t result_len) +{ + int position= 0; + char verbose_option[]= " --no-defaults --verbose --help"; + Buffer cmd; + + cmd.append(position, mysqld_path, strlen(mysqld_path)); + position+= strlen(mysqld_path); + cmd.append(position, verbose_option, sizeof(verbose_option) - 1); + position+= sizeof(verbose_option) - 1; + cmd.append(position, "\0", 1); + /* get the value from "mysqld --help --verbose" */ + if (parse_output_and_get_value(cmd.buffer, option_name + 2, + result, result_len)) + return 1; + + return 0; +} + + +void Instance_options::get_pid_filename(char *result) +{ + const char *pid_file= mysqld_pid_file; + char datadir[MAX_PATH_LEN]; + + if (mysqld_datadir == NULL) + { + get_default_option(datadir, "--datadir", MAX_PATH_LEN); + } + else + strxnmov(datadir, MAX_PATH_LEN - 1, strchr(mysqld_datadir, '=') + 1, + "/", NullS); + + /* well, we should never get it */ + if (mysqld_pid_file != NULL) + pid_file= strchr(pid_file, '=') + 1; + else + DBUG_ASSERT(0); + + /* get the full path to the pidfile */ + my_load_path(result, pid_file, datadir); + +} + + +int Instance_options::unlink_pidfile() +{ + char pid_file_path[MAX_PATH_LEN]; + + /* + This works as we know that pid_file_path is of + MAX_PATH_LEN == FN_REFLEN length + */ + get_pid_filename((char *)&pid_file_path); + + return unlink(pid_file_path); +} + + +pid_t Instance_options::get_pid() +{ + char pid_file_path[MAX_PATH_LEN]; + + /* + This works as we know that pid_file_path is of + MAX_PATH_LEN == FN_REFLEN length + */ + get_pid_filename((char *)&pid_file_path); + + /* get the pid */ + if (FILE *pid_file_stream= my_fopen(pid_file_path, + O_RDONLY | O_BINARY, MYF(0))) + { + pid_t pid; + + fscanf(pid_file_stream, "%i", &pid); + my_fclose(pid_file_stream, MYF(0)); + return pid; + } + else + return 0; +} + + int Instance_options::complete_initialization(const char *default_path, const char *default_user, const char *default_password) { - /* we need to reserve space for the final zero + possible default options */ - if (!(argv= (char**) alloc_root(&alloc, (options_array.elements + 1 - + MAX_NUMBER_OF_DEFAULT_OPTIONS) * sizeof(char*)))) - goto err; - + const char *tmp; if (mysqld_path == NULL) { @@ -40,22 +125,34 @@ int Instance_options::complete_initialization(const char *default_path, goto err; } - /* this option must be first in the argv */ + if (!(tmp= strdup_root(&alloc, "--no-defaults"))) + goto err; + + if (mysqld_pid_file == NULL) + { + char pidfilename[MAX_PATH_LEN]; + char hostname[MAX_PATH_LEN]; + if (!gethostname(hostname, sizeof(hostname) - 1)) + strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", hostname, "-", + instance_name, ".pid", NullS); + else + strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", instance_name, + ".pid", NullS); + + add_option(pidfilename); + } + + /* we need to reserve space for the final zero + possible default options */ + if (!(argv= (char**) alloc_root(&alloc, (options_array.elements + 1 + + MAX_NUMBER_OF_DEFAULT_OPTIONS) * sizeof(char*)))) + goto err; + + /* the path must be first in the argv */ if (add_to_argv(mysqld_path)) goto err; - /* the following options are not for argv */ - if (mysqld_user == NULL) - { - if (!(mysqld_user= strdup_root(&alloc, default_user))) - goto err; - } - - if (mysqld_password == NULL) - { - if (!(mysqld_password= strdup_root(&alloc, default_password))) - goto err; - } + if (add_to_argv(tmp)) + goto err; memcpy((gptr) (argv + filled_default_options), options_array.buffer, options_array.elements*sizeof(char*)); @@ -102,8 +199,6 @@ int Instance_options::add_option(const char* option) {"--bind-address=", 15, &mysqld_bind_address, SAVE_WHOLE_AND_ADD}, {"--pid-file=", 11, &mysqld_pid_file, SAVE_WHOLE_AND_ADD}, {"--mysqld-path=", 14, &mysqld_path, SAVE_VALUE}, - {"--admin-user=", 13, &mysqld_user, SAVE_VALUE}, - {"--admin-password=", 17, &mysqld_password, SAVE_VALUE}, {"--guarded", 9, &is_guarded, SAVE_WHOLE}, {NULL, 0, NULL, 0} }; diff --git a/server-tools/instance-manager/instance_options.h b/server-tools/instance-manager/instance_options.h index 5bc46497d2a..26448c0c2b1 100644 --- a/server-tools/instance-manager/instance_options.h +++ b/server-tools/instance-manager/instance_options.h @@ -38,8 +38,8 @@ class Instance_options public: Instance_options() : mysqld_socket(0), mysqld_datadir(0), mysqld_bind_address(0), - mysqld_pid_file(0), mysqld_port(0), mysqld_path(0), mysqld_user(0), - mysqld_password(0), is_guarded(0), filled_default_options(0) + mysqld_pid_file(0), mysqld_port(0), mysqld_path(0), is_guarded(0), + filled_default_options(0) {} ~Instance_options(); /* fills in argv */ @@ -49,9 +49,17 @@ public: int add_option(const char* option); int init(const char *instance_name_arg); + pid_t get_pid(); + void get_pid_filename(char *result); + int unlink_pidfile(); public: - enum { MAX_NUMBER_OF_DEFAULT_OPTIONS= 1 }; + /* + We need this value to be greater or equal then FN_REFLEN found in + my_global.h to use my_load_path() + */ + enum { MAX_PATH_LEN= 512 }; + enum { MAX_NUMBER_OF_DEFAULT_OPTIONS= 2 }; enum { MEM_ROOT_BLOCK_SIZE= 512 }; char **argv; /* We need the some options, so we store them as a separate pointers */ @@ -63,12 +71,12 @@ public: uint instance_name_len; const char *instance_name; const char *mysqld_path; - const char *mysqld_user; - const char *mysqld_password; const char *is_guarded; DYNAMIC_ARRAY options_array; private: int add_to_argv(const char *option); + int get_default_option(char *result, const char *option_name, + size_t result_len); private: uint filled_default_options; MEM_ROOT alloc; diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index 07d4f1ed33e..a8c6406fb83 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -16,12 +16,6 @@ #include "manager.h" -#include -#include -#include -#include -#include - #include "thread_registry.h" #include "listener.h" #include "instance_map.h" @@ -30,6 +24,14 @@ #include "log.h" #include "guardian.h" +#include +#include +#include +#include +#include +#include + + static int create_pid_file(const char *pid_file_name) { if (FILE *pid_file= my_fopen(pid_file_name, @@ -90,6 +92,7 @@ void manager(const Options &options) sigemptyset(&mask); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGTERM); + sigaddset(&mask, SIGCHLD); sigaddset(&mask, SIGPIPE); sigaddset(&mask, SIGHUP); /* @@ -170,6 +173,13 @@ void manager(const Options &options) case THR_SERVER_ALARM: process_alarm(signo); break; + case SIGCHLD: + wait(NULL); + /* wake threads waiting for an instance to shutdown */ + pthread_cond_broadcast(&instance_map.pid_cond.COND_pid); + /* wake guardian */ + pthread_cond_broadcast(&guardian_thread.COND_guardian); + break; default: thread_registry.deliver_shutdown(); shutdown_complete= TRUE; diff --git a/server-tools/instance-manager/parse_output.cc b/server-tools/instance-manager/parse_output.cc new file mode 100644 index 00000000000..2bc51922e69 --- /dev/null +++ b/server-tools/instance-manager/parse_output.cc @@ -0,0 +1,82 @@ +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include +#include +#include +#include + +/* buf should be of appropriate size. Otherwise the word will be truncated */ +static int get_word(FILE *file, char *buf, size_t size) +{ + int currchar; + + currchar= getc(file); + + /* skip space */ + while (my_isspace(default_charset_info, (char) currchar) && + currchar != EOF && size > 1) + { + currchar= getc(file); + } + + while (!my_isspace(default_charset_info, (char) currchar) && + currchar != EOF && size > 1) + { + *buf++= (char) currchar; + currchar= getc(file); + size--; + } + + *buf= '\0'; + return 0; +} + + +int parse_output_and_get_value(const char *command, const char *word, + char *result, size_t result_len) +{ + FILE *output; + int wordlen; + + wordlen= strlen(word); + + output= popen(command, "r"); + + /* + We want fully buffered stream. We also want system to + allocate appropriate buffer. + */ + setvbuf(output, NULL, _IOFBF, 0); + + get_word(output, result, result_len); + while (strncmp(word, result, wordlen) && *result != '\0') + { + get_word(output, result, result_len); + } + + /* + If we have found the word, return the next one. This is usually + an option value. + */ + if (*result != '\0') + get_word(output, result, result_len); + + if (pclose(output)) + return 1; + + return 0; +} diff --git a/server-tools/instance-manager/parse_output.h b/server-tools/instance-manager/parse_output.h new file mode 100644 index 00000000000..20503a74629 --- /dev/null +++ b/server-tools/instance-manager/parse_output.h @@ -0,0 +1,19 @@ +/* Copyright (C) 2004 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +int parse_output_and_get_value(const char *command, const char *word, + char *result, size_t result_len); + From 92a52cccf6fe4f41f2b234b162daaae3d2e3ab26 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 31 Jan 2005 23:54:08 +0300 Subject: [PATCH 07/42] more fixes for IM to substitude mysqld_safe in startup scripts BitKeeper/deleted/.del-thread_repository.cc~bba09f64f8cb4037: Delete: server-tools/instance-manager/thread_repository.cc BitKeeper/deleted/.del-thread_repository.h~e6a3b9cab7a6612a: Delete: server-tools/instance-manager/thread_repository.h server-tools/instance-manager/commands.cc: All instances are guarded by default now, so we need to perform check on whether the instance is nonguarded, rather then guarded when adding it to the list of guarded instnces. server-tools/instance-manager/guardian.cc: Guardian rewritten to start instances by default, and shut them down, when exiting. Behaviour of the guardian in case of the instance crash has changed. Now it tries to restart an instance constantly in the first 2 seconds after the crash was noticed, and then it tries restart an instance once in the MONITORING_INTERVAL. If it failed to restart instance for "restart_retry" (compiled-in value) times, guardian stops trying to restart it. server-tools/instance-manager/guardian.h: Several new functions and variables declared. server-tools/instance-manager/instance.cc: now start doesn't call stop(), but rather tries to remove the pidfile itself server-tools/instance-manager/instance.h: cleanup server-tools/instance-manager/instance_map.cc: no more "admin" options server-tools/instance-manager/instance_map.h: User and password purged from instance_map options, as IM shouldn't know them server-tools/instance-manager/instance_options.cc: new option added -- shutdown_delay, guarded option now called nonguaded and has the opposite meaning server-tools/instance-manager/instance_options.h: appropriate changes, reflecting options addition/removal server-tools/instance-manager/manager.cc: shutdown process is complicated a bit -- at first signal thread should stop guardian, and only then the IM itself server-tools/instance-manager/messages.cc: update error message server-tools/instance-manager/options.cc: admin user/password purged from mysqlmanager options --- server-tools/instance-manager/commands.cc | 8 +- server-tools/instance-manager/guardian.cc | 107 ++++++++-- server-tools/instance-manager/guardian.h | 19 ++ server-tools/instance-manager/instance.cc | 22 ++- server-tools/instance-manager/instance.h | 4 +- server-tools/instance-manager/instance_map.cc | 11 +- server-tools/instance-manager/instance_map.h | 7 +- .../instance-manager/instance_options.cc | 7 +- .../instance-manager/instance_options.h | 9 +- server-tools/instance-manager/manager.cc | 17 +- server-tools/instance-manager/messages.cc | 4 +- server-tools/instance-manager/options.cc | 16 -- .../instance-manager/thread_repository.cc | 185 ------------------ .../instance-manager/thread_repository.h | 113 ----------- 14 files changed, 160 insertions(+), 369 deletions(-) delete mode 100644 server-tools/instance-manager/thread_repository.cc delete mode 100644 server-tools/instance-manager/thread_repository.h diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc index 4922aba4a06..70774115429 100644 --- a/server-tools/instance-manager/commands.cc +++ b/server-tools/instance-manager/commands.cc @@ -274,10 +274,10 @@ int Show_instance_options::do_command(struct st_net *net, goto err; } - if (instance->options.is_guarded != NULL) + if (instance->options.nonguarded == NULL) { position= 0; - store_to_string(&send_buff, (char *) "guarded", &position); + store_to_string(&send_buff, (char *) "nonguarded", &position); store_to_string(&send_buff, "", &position); if (my_net_write(net, send_buff.buffer, (uint) position)) goto err; @@ -350,7 +350,7 @@ int Start_instance::execute(struct st_net *net, ulong connection_id) if (err_code= instance->start()) return err_code; - if (instance->options.is_guarded != NULL) + if (instance->options.nonguarded == NULL) instance_map->guardian->guard(instance); net_send_ok(net, connection_id); @@ -381,7 +381,7 @@ int Stop_instance::execute(struct st_net *net, ulong connection_id) } else { - if (instance->options.is_guarded != NULL) + if (instance->options.nonguarded == NULL) instance_map->guardian-> stop_guard(instance); if ((err_code= instance->stop())) diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc index f68ef6575a0..e8f9068dbb9 100644 --- a/server-tools/instance-manager/guardian.cc +++ b/server-tools/instance-manager/guardian.cc @@ -46,6 +46,8 @@ Guardian_thread::Guardian_thread(Thread_registry &thread_registry_arg, { pthread_mutex_init(&LOCK_guardian, 0); pthread_cond_init(&COND_guardian, 0); + shutdown_guardian= FALSE; + is_stopped= FALSE; thread_registry.register_thread(&thread_info); init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0); guarded_instances= NULL; @@ -65,6 +67,22 @@ Guardian_thread::~Guardian_thread() } +void Guardian_thread::shutdown() +{ + pthread_mutex_lock(&LOCK_guardian); + shutdown_guardian= TRUE; + pthread_mutex_unlock(&LOCK_guardian); +} + + +void Guardian_thread::request_stop_instances() +{ + pthread_mutex_lock(&LOCK_guardian); + request_stop= TRUE; + pthread_mutex_unlock(&LOCK_guardian); +} + + /* Run guardian thread @@ -80,6 +98,7 @@ Guardian_thread::~Guardian_thread() void Guardian_thread::run() { Instance *instance; + int restart_retry= 100; LIST *loop; struct timespec timeout; @@ -87,26 +106,68 @@ void Guardian_thread::run() pthread_mutex_lock(&LOCK_guardian); - while (!thread_registry.is_shutdown()) + while (!shutdown_guardian) { + int status= 0; loop= guarded_instances; while (loop != NULL) { - instance= (Instance *) loop->data; - /* instance-> start already checks whether the instance is running */ - if (instance->start() != ER_INSTANCE_ALREADY_STARTED) - log_info("guardian attempted to restart instance %s", - instance->options.instance_name); + instance= ((GUARD_NODE *) loop->data)->instance; + if (!instance->is_running()) + { + int state= 0; /* state of guardian */ + + if ((((GUARD_NODE *) loop->data)->crash_moment == 0)) + state= 1; /* an instance just crashed */ + else + if (time(NULL) - ((GUARD_NODE *) loop->data)->crash_moment <= 2) + /* try to restart an instance immediately */ + state= 2; + else + state= 3; /* try to restart it */ + + if (state == 1) + ((GUARD_NODE *) loop->data)->crash_moment= time(NULL); + + if ((state == 1) || (state == 2)) + { + instance->start(); + ((GUARD_NODE *) loop->data)->restart_counter++; + log_info("guardian: starting instance %s", + instance->options.instance_name); + } + else + { + if ((status == ETIMEDOUT) && + (((GUARD_NODE *) loop->data)->restart_counter < restart_retry)) + { + instance->start(); + ((GUARD_NODE *) loop->data)->restart_counter++; + log_info("guardian: starting instance %s", + instance->options.instance_name); + } + } + } + else /* clear status fields */ + { + ((GUARD_NODE *) loop->data)->restart_counter= 0; + ((GUARD_NODE *) loop->data)->crash_moment= 0; + } loop= loop->next; } move_to_list(&starting_instances, &guarded_instances); timeout.tv_sec= time(NULL) + monitoring_interval; timeout.tv_nsec= 0; - pthread_cond_timedwait(&COND_guardian, &LOCK_guardian, &timeout); + status= pthread_cond_timedwait(&COND_guardian, &LOCK_guardian, &timeout); } pthread_mutex_unlock(&LOCK_guardian); + if (request_stop) + stop_instances(); + is_stopped= TRUE; + /* now, when the Guardian is stopped we can stop the IM */ + thread_registry.request_shutdown(); my_thread_end(); } @@ -119,7 +180,7 @@ int Guardian_thread::start() instance_map->lock(); while ((instance= iterator.next())) { - if ((instance->options.is_guarded != NULL) && (instance->is_running())) + if ((instance->options.nonguarded == NULL)) if (guard(instance)) return 1; } @@ -170,12 +231,18 @@ void Guardian_thread::move_to_list(LIST **from, LIST **to) int Guardian_thread::add_instance_to_list(Instance *instance, LIST **list) { LIST *node; + GUARD_NODE *content; node= (LIST *) alloc_root(&alloc, sizeof(LIST)); - if (node == NULL) + content= (GUARD_NODE *) alloc_root(&alloc, sizeof(GUARD_NODE)); + + if ((node == NULL) || (content == NULL)) return 1; /* we store the pointers to instances from the instance_map's MEM_ROOT */ - node->data= (void *) instance; + content->instance= instance; + content->restart_counter= 0; + content->crash_moment= 0; + node->data= (void *) content; pthread_mutex_lock(&LOCK_guardian); *list= list_add(*list, node); @@ -205,7 +272,7 @@ int Guardian_thread::stop_guard(Instance *instance) We compare only pointers, as we always use pointers from the instance_map's MEM_ROOT. */ - if ((Instance *) node->data == instance) + if (((GUARD_NODE *) node->data)->instance == instance) { guarded_instances= list_delete(guarded_instances, node); pthread_mutex_unlock(&LOCK_guardian); @@ -219,3 +286,21 @@ int Guardian_thread::stop_guard(Instance *instance) return 0; } +int Guardian_thread::stop_instances() +{ + Instance *instance; + Instance_map::Iterator iterator(instance_map); + + while ((instance= iterator.next())) + { + if ((instance->options.nonguarded == NULL)) + { + if (stop_guard(instance)) + return 1; + /* let us try to stop the server */ + instance->stop(); + } + } + + return 0; +} diff --git a/server-tools/instance-manager/guardian.h b/server-tools/instance-manager/guardian.h index bcc249ed7d3..d96208247ae 100644 --- a/server-tools/instance-manager/guardian.h +++ b/server-tools/instance-manager/guardian.h @@ -24,6 +24,7 @@ #pragma interface #endif +class Instance; class Instance_map; #include "thread_registry.h" @@ -35,6 +36,13 @@ pthread_handler_decl(guardian, arg); C_MODE_END +typedef struct st_guard_node +{ + Instance *instance; + uint restart_counter; + time_t crash_moment; +} GUARD_NODE; + struct Guardian_thread_args { @@ -67,13 +75,17 @@ public: void run(); int init(); int start(); + void shutdown(); + void request_stop_instances(); int guard(Instance *instance); int stop_guard(Instance *instance); + bool is_stopped; public: pthread_cond_t COND_guardian; private: + int stop_instances(); int add_instance_to_list(Instance *instance, LIST **list); void move_to_list(LIST **from, LIST **to); @@ -84,6 +96,13 @@ private: LIST *starting_instances; MEM_ROOT alloc; enum { MEM_ROOT_BLOCK_SIZE= 512 }; + /* this variable is set to TRUE when we want to stop Guardian thread */ + bool shutdown_guardian; + /* + This var is usually set together with shutdown_guardian. this way we + request guardian to shut down all instances before termination + */ + bool request_stop; }; #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_GUARDIAN_H */ diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc index edb9ea61ad3..6c32aa1d815 100644 --- a/server-tools/instance-manager/instance.cc +++ b/server-tools/instance-manager/instance.cc @@ -42,10 +42,16 @@ int Instance::start() { - /* echk for the pidfile and remove it */ + pid_t pid; + if (!is_running()) { - stop(); + if ((pid= options.get_pid()) != 0) /* check the pidfile */ + if (options.unlink_pidfile()) /* remove stalled pidfile */ + log_error("cannot remove pidfile for instance %i, this might be \ + since IM lacks permmissions or hasn't found the pidifle", + options.instance_name); + log_info("starting instance %s", options.instance_name); switch (pid= fork()) { case 0: @@ -145,14 +151,17 @@ int Instance::stop() { pid_t pid; struct timespec timeout; - time_t waitchild= 35; /* */ + int waitchild= DEFAULT_SHUTDOWN_DELAY; + + if (options.shutdown_delay != NULL) + waitchild= atoi(options.shutdown_delay); if ((pid= options.get_pid()) != 0) /* get pid from pidfile */ { /* If we cannot kill mysqld, then it has propably crashed. Let us try to remove staled pidfile and return succes as mysqld - is stopped + is probably stopped */ if (kill(pid, SIGTERM)) { @@ -161,9 +170,6 @@ int Instance::stop() since IM lacks permmissions or hasn't found the pidifle", options.instance_name); - log_error("The instance %s has probably crashed or IM lacks permissions \ - to kill it. in either case something seems to be wrong. \ - Check your setup", options.instance_name); return 0; } @@ -174,7 +180,7 @@ int Instance::stop() if (pthread_mutex_lock(&instance_map->pid_cond.LOCK_pid)) goto err; /* perhaps this should be procecced differently */ - while (options.get_pid() != 0) + while (options.get_pid() != 0) /* while server isn't stopped */ { int status; diff --git a/server-tools/instance-manager/instance.h b/server-tools/instance-manager/instance.h index 78b7768c121..29137c749c7 100644 --- a/server-tools/instance-manager/instance.h +++ b/server-tools/instance-manager/instance.h @@ -41,11 +41,9 @@ public: int cleanup(); public: + enum { DEFAULT_SHUTDOWN_DELAY= 35 }; Instance_options options; - /* connection to the instance */ - pid_t pid; - private: /* Mutex protecting the instance. Currently we use it to avoid the diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index 2573729841b..26181ec56e0 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -82,7 +82,8 @@ static int process_option(void * ctx, const char *group, const char *option) map = (Instance_map*) ctx; if (strncmp(group, prefix, sizeof prefix) == 0 && - (my_isdigit(default_charset_info, group[sizeof prefix]))) + ((my_isdigit(default_charset_info, group[sizeof prefix])) + || group[sizeof(prefix)] == '\0')) { if ((instance= map->find(group, strlen(group))) == NULL) { @@ -109,13 +110,9 @@ err_new_instance: C_MODE_END -Instance_map::Instance_map(const char *default_mysqld_path_arg, - const char *default_admin_user_arg, - const char *default_admin_password_arg) +Instance_map::Instance_map(const char *default_mysqld_path_arg) { mysqld_path= default_mysqld_path_arg; - user= default_admin_user_arg; - password= default_admin_password_arg; pthread_mutex_init(&LOCK_instance_map, 0); } @@ -195,7 +192,7 @@ void Instance_map::complete_initialization() { instance= (Instance *) hash_element(&hash, i); instance->complete_initialization(this); - instance->options.complete_initialization(mysqld_path, user, password); + instance->options.complete_initialization(mysqld_path); i++; } } diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h index 1f6072f2582..05bc4d94908 100644 --- a/server-tools/instance-manager/instance_map.h +++ b/server-tools/instance-manager/instance_map.h @@ -71,9 +71,7 @@ public: int unlock(); int init(); - Instance_map(const char *default_mysqld_path_arg, - const char *default_admin_user_arg, - const char *default_admin_password_arg); + Instance_map(const char *default_mysqld_path_arg); ~Instance_map(); /* loads options from config files */ @@ -85,9 +83,6 @@ public: public: const char *mysqld_path; - /* user an password to shutdown MySQL */ - const char *user; - const char *password; Guardian_thread *guardian; /* structure used for syncronization reasons in the stop command */ CHILD_COND pid_cond; diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc index c29f136955e..0e29dca2422 100644 --- a/server-tools/instance-manager/instance_options.cc +++ b/server-tools/instance-manager/instance_options.cc @@ -113,9 +113,7 @@ pid_t Instance_options::get_pid() } -int Instance_options::complete_initialization(const char *default_path, - const char *default_user, - const char *default_password) +int Instance_options::complete_initialization(const char *default_path) { const char *tmp; @@ -199,7 +197,8 @@ int Instance_options::add_option(const char* option) {"--bind-address=", 15, &mysqld_bind_address, SAVE_WHOLE_AND_ADD}, {"--pid-file=", 11, &mysqld_pid_file, SAVE_WHOLE_AND_ADD}, {"--mysqld-path=", 14, &mysqld_path, SAVE_VALUE}, - {"--guarded", 9, &is_guarded, SAVE_WHOLE}, + {"--nonguarded", 9, &nonguarded, SAVE_WHOLE}, + {"--shutdown_delay", 9, &shutdown_delay, SAVE_VALUE}, {NULL, 0, NULL, 0} }; struct selected_options_st *selected_options; diff --git a/server-tools/instance-manager/instance_options.h b/server-tools/instance-manager/instance_options.h index 26448c0c2b1..eae22f840aa 100644 --- a/server-tools/instance-manager/instance_options.h +++ b/server-tools/instance-manager/instance_options.h @@ -38,14 +38,12 @@ class Instance_options public: Instance_options() : mysqld_socket(0), mysqld_datadir(0), mysqld_bind_address(0), - mysqld_pid_file(0), mysqld_port(0), mysqld_path(0), is_guarded(0), + mysqld_pid_file(0), mysqld_port(0), mysqld_path(0), nonguarded(0), filled_default_options(0) {} ~Instance_options(); /* fills in argv */ - int complete_initialization(const char *default_path, - const char *default_user, - const char *default_password); + int complete_initialization(const char *default_path); int add_option(const char* option); int init(const char *instance_name_arg); @@ -71,7 +69,8 @@ public: uint instance_name_len; const char *instance_name; const char *mysqld_path; - const char *is_guarded; + const char *nonguarded; + const char *shutdown_delay; DYNAMIC_ARRAY options_array; private: int add_to_argv(const char *option); diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index a8c6406fb83..6d9ee569d04 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -67,9 +67,7 @@ void manager(const Options &options) */ User_map user_map; - Instance_map instance_map(options.default_mysqld_path, - options.default_admin_user, - options.default_admin_password); + Instance_map instance_map(options.default_mysqld_path); Guardian_thread guardian_thread(thread_registry, &instance_map, options.monitoring_interval); @@ -181,8 +179,17 @@ void manager(const Options &options) pthread_cond_broadcast(&guardian_thread.COND_guardian); break; default: - thread_registry.deliver_shutdown(); - shutdown_complete= TRUE; + if (!guardian_thread.is_stopped) + { + guardian_thread.request_stop_instances(); + guardian_thread.shutdown(); + pthread_cond_broadcast(&guardian_thread.COND_guardian); + } + else + { + thread_registry.deliver_shutdown(); + shutdown_complete= TRUE; + } break; } } diff --git a/server-tools/instance-manager/messages.cc b/server-tools/instance-manager/messages.cc index cc07352f58a..e3c2423a47e 100644 --- a/server-tools/instance-manager/messages.cc +++ b/server-tools/instance-manager/messages.cc @@ -45,8 +45,8 @@ static const char *mysqld_error_message(unsigned sql_errno) case ER_BAD_INSTANCE_NAME: return "Bad instance name. Check that the instance with such a name exists"; case ER_INSTANCE_IS_NOT_STARTED: - return "Cannot stop instance. Perhaps the instance is not started or you" - " have specified wrong username/password in the config file"; + return "Cannot stop instance. Perhaps the instance is not started, or was started" + "manually, so IM cannot find the pidfile."; case ER_INSTANCE_ALREADY_STARTED: return "The instance is already started"; case ER_CANNOT_START_INSTANCE: diff --git a/server-tools/instance-manager/options.cc b/server-tools/instance-manager/options.cc index 05493e10ad8..828a5d7b4d0 100644 --- a/server-tools/instance-manager/options.cc +++ b/server-tools/instance-manager/options.cc @@ -35,8 +35,6 @@ const char *Options::pid_file_name= QUOTE(DEFAULT_PID_FILE_NAME); const char *Options::socket_file_name= QUOTE(DEFAULT_SOCKET_FILE_NAME); const char *Options::password_file_name= QUOTE(DEFAULT_PASSWORD_FILE_NAME); const char *Options::default_mysqld_path= QUOTE(DEFAULT_MYSQLD_PATH); -const char *Options::default_admin_user= QUOTE(DEFAULT_USER); -const char *Options::default_admin_password= QUOTE(DEFAULT_PASSWORD); const char *Options::bind_address= 0; /* No default value */ uint Options::monitoring_interval= DEFAULT_MONITORING_INTERVAL; uint Options::port_number= DEFAULT_PORT; @@ -55,8 +53,6 @@ enum options { OPT_RUN_AS_SERVICE, OPT_USER, OPT_PASSWORD, - OPT_DEFAULT_ADMIN_USER, - OPT_DEFAULT_ADMIN_PASSWORD, OPT_MONITORING_INTERVAL, OPT_PORT, OPT_BIND_ADDRESS @@ -98,18 +94,6 @@ static struct my_option my_long_options[] = (gptr *) &Options::default_mysqld_path, (gptr *) &Options::default_mysqld_path, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, - { "default-admin-user", OPT_DEFAULT_ADMIN_USER, "Username to shutdown MySQL" - " instances.", - (gptr *) &Options::default_admin_user, - (gptr *) &Options::default_admin_user, - 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, - - { "default-admin-password", OPT_DEFAULT_ADMIN_PASSWORD, "Password to" - "shutdown MySQL instances.", - (gptr *) &Options::default_admin_password, - (gptr *) &Options::default_admin_password, - 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, - { "monitoring-interval", OPT_MONITORING_INTERVAL, "Interval to monitor instances" " in seconds.", (gptr *) &Options::monitoring_interval, diff --git a/server-tools/instance-manager/thread_repository.cc b/server-tools/instance-manager/thread_repository.cc deleted file mode 100644 index d0b302d29fb..00000000000 --- a/server-tools/instance-manager/thread_repository.cc +++ /dev/null @@ -1,185 +0,0 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#ifdef __GNUC__ -#pragma implementation -#endif - -#include "thread_repository.h" -#include -#include -#include "log.h" - - -/* Kick-off signal handler */ - -enum { THREAD_KICK_OFF_SIGNAL= SIGUSR2 }; - -static void handle_signal(int __attribute__((unused)) sig_no) -{ -} - - -/* - TODO: think about moving signal information (now it's shutdown_in_progress) - to Thread_info. It will reduce contention and allow signal deliverence to - a particular thread, not to the whole worker crew -*/ - -Thread_repository::Thread_repository() : - shutdown_in_progress(false) -{ - pthread_mutex_init(&LOCK_thread_repository, 0); - pthread_cond_init(&COND_thread_repository_is_empty, 0); - - /* head is used by-value to simplify nodes inserting */ - head.next= head.prev= &head; -} - - -Thread_repository::~Thread_repository() -{ - /* Check that no one uses the repository. */ - pthread_mutex_lock(&LOCK_thread_repository); - - /* All threads must unregister */ - DBUG_ASSERT(head.next == &head); - - pthread_mutex_unlock(&LOCK_thread_repository); - pthread_cond_destroy(&COND_thread_repository_is_empty); - pthread_mutex_destroy(&LOCK_thread_repository); -} - - -/* - - Set signal handler for kick-off thread, and insert a thread info to the - repository. New node is appended to the end of the list; head.prev always - points to the last node. -*/ - -void Thread_repository::register_thread(Thread_info *info) -{ - struct sigaction sa; - sa.sa_handler= handle_signal; - sa.sa_flags= 0; - sigemptyset(&sa.sa_mask); - sigaction(THREAD_KICK_OFF_SIGNAL, &sa, 0); - - info->current_cond= 0; - - pthread_mutex_lock(&LOCK_thread_repository); - info->next= &head; - info->prev= head.prev; - head.prev->next= info; - head.prev= info; - pthread_mutex_unlock(&LOCK_thread_repository); -} - - -/* - Unregister a thread from the repository and free Thread_info structure. - Every registered thread must unregister. Unregistering should be the last - thing a thread is doing, otherwise it could have no time to finalize. -*/ - -void Thread_repository::unregister_thread(Thread_info *info) -{ - pthread_mutex_lock(&LOCK_thread_repository); - info->prev->next= info->next; - info->next->prev= info->prev; - if (head.next == &head) - pthread_cond_signal(&COND_thread_repository_is_empty); - pthread_mutex_unlock(&LOCK_thread_repository); -} - - -/* - Check whether shutdown is in progress, and if yes, return immidiately. - Else set info->current_cond and call pthread_cond_wait. When - pthread_cond_wait returns, unregister current cond and check the shutdown - status again. - RETURN VALUE - return value from pthread_cond_wait -*/ - -int Thread_repository::cond_wait(Thread_info *info, pthread_cond_t *cond, - pthread_mutex_t *mutex, bool *is_shutdown) -{ - pthread_mutex_lock(&LOCK_thread_repository); - *is_shutdown= shutdown_in_progress; - if (*is_shutdown) - { - pthread_mutex_unlock(&LOCK_thread_repository); - return 0; - } - info->current_cond= cond; - pthread_mutex_unlock(&LOCK_thread_repository); - /* sic: race condition here, cond can be signaled in deliver_shutdown */ - int rc= pthread_cond_wait(cond, mutex); - pthread_mutex_lock(&LOCK_thread_repository); - info->current_cond= 0; - *is_shutdown= shutdown_in_progress; - pthread_mutex_unlock(&LOCK_thread_repository); - return rc; -} - - -/* - Deliver shutdown message to the workers crew. - As it's impossible to avoid all race conditions, we signal latecomers - again. -*/ - -void Thread_repository::deliver_shutdown() -{ - struct timespec shutdown_time; - set_timespec(shutdown_time, 1); - Thread_info *info; - - pthread_mutex_lock(&LOCK_thread_repository); - shutdown_in_progress= true; - for (info= head.next; info != &head; info= info->next) - { - pthread_kill(info->thread_id, THREAD_KICK_OFF_SIGNAL); - /* - sic: race condition here, the thread may not yet fall into - pthread_cond_wait. - */ - if (info->current_cond) - pthread_cond_signal(info->current_cond); - } - while (pthread_cond_timedwait(&COND_thread_repository_is_empty, - &LOCK_thread_repository, - &shutdown_time) != ETIMEDOUT && - head.next != &head) - ; - /* - If previous signals did not reach some threads, they must be sleeping - in pthread_cond_wait or a blocking syscall. Wake them up: - every thread shall check signal variables after each syscall/cond_wait, - so this time everybody should be informed (presumably each worker can - get CPU during shutdown_time.) - */ - for (info= head.next; info != &head; info= info->next) - { - pthread_kill(info->thread_id, THREAD_KICK_OFF_SIGNAL); - if (info->current_cond) - pthread_cond_signal(info->current_cond); - } - pthread_mutex_unlock(&LOCK_thread_repository); -} - diff --git a/server-tools/instance-manager/thread_repository.h b/server-tools/instance-manager/thread_repository.h deleted file mode 100644 index 7bd21d66e3d..00000000000 --- a/server-tools/instance-manager/thread_repository.h +++ /dev/null @@ -1,113 +0,0 @@ -#ifndef INCLUDES_MYSQL_INSTANCE_MANAGER_THREAD_REPOSITORY_HH -#define INCLUDES_MYSQL_INSTANCE_MANAGER_THREAD_REPOSITORY_HH -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -/* - A multi-threaded application shall nicely work with signals. - - This means it shall, first of all, shut down nicely on ``quit'' signals: - stop all running threads, cleanup and exit. - - Note, that a thread can't be shut down nicely if it doesn't want to be. - That's why to perform clean shutdown, all threads consituting a process - must observe certain rules. Here we use the rules, described in Butenhof - book 'Programming with POSIX threads', namely: - - all user signals are handled in 'signal thread' in synchronous manner - (by means of sigwait). To guarantee that the signal thread is the only who - can receive user signals, all threads block them, and signal thread is - the only who calls sigwait() with an apporpriate sigmask. - To propogate a signal to the workers the signal thread sets - a variable, corresponding to the signal. Additionally the signal thread - sends each worker an internal signal (by means of pthread_kill) to kick it - out from possible blocking syscall, and possibly pthread_cond_signal if - some thread is blocked in pthread_cond_[timed]wait. - - a worker handles only internal 'kick' signal (the handler does nothing). - In case when a syscall returns 'EINTR' the worker checks all - signal-related variables and behaves accordingly. - Also these variables shall be checked from time to time in long - CPU-bounded operations, and before/after pthread_cond_wait. (It's supposed - that a worker thread either waits in a syscall/conditional variable, or - computes something.) - - to guarantee signal deliverence, there should be some kind of feedback, - e. g. all workers shall account in the signal thread Thread Repository and - unregister from it on exit. - - Configuration reload (on SIGHUP) and thread timeouts/alarms can be handled - in manner, similar to ``quit'' signals. -*/ - -#ifdef __GNUC__ -#pragma interface -#endif - -#include -#include - - -/* - Thread_info - repository entry for each worker thread - All entries comprise double-linked list like: - 0 -- entry -- entry -- entry - 0 - Double-linked list is used to unregister threads easy. -*/ - -class Thread_info -{ - pthread_cond_t *current_cond; - Thread_info *prev, *next; - pthread_t thread_id; - Thread_info() {} - friend class Thread_repository; -public: - Thread_info(pthread_t thread_id_arg) : thread_id(thread_id_arg) {} -}; - - -/* - Thread_repository - contains handles for each worker thread to deliver - signal information to workers. -*/ - -class Thread_repository -{ -public: - Thread_repository(); - ~Thread_repository(); - - void register_thread(Thread_info *info); - void unregister_thread(Thread_info *info); - void deliver_shutdown(); - inline bool is_shutdown(); - int cond_wait(Thread_info *info, pthread_cond_t *cond, - pthread_mutex_t *mutex, bool *is_shutdown); -private: - Thread_info head; - bool shutdown_in_progress; - pthread_mutex_t LOCK_thread_repository; - pthread_cond_t COND_thread_repository_is_empty; -}; - - -inline bool Thread_repository::is_shutdown() -{ - pthread_mutex_lock(&LOCK_thread_repository); - bool res= shutdown_in_progress; - pthread_mutex_unlock(&LOCK_thread_repository); - return res; -} - -#endif // INCLUDES_MYSQL_INSTANCE_MANAGER_THREAD_REPOSITORY_HH From 935585a3369c7cf6f47edc71399c0c146178ea48 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 2 Feb 2005 15:52:19 +0400 Subject: [PATCH 08/42] After review fixes client/mysql.cc: check for "error" added sql/sql_db.cc: typo fixed --- client/mysql.cc | 3 ++- sql/sql_db.cc | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/client/mysql.cc b/client/mysql.cc index a799027542b..d48e937023b 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -1921,7 +1921,8 @@ com_go(String *buffer,char *line __attribute__((unused))) if (err >= 1) error= put_error(&mysql); - if (!status.batch && (mysql.server_status & SERVER_STATUS_DB_DROPPED)) + if (!error && !status.batch && + (mysql.server_status & SERVER_STATUS_DB_DROPPED)) get_current_db(); return error; /* New command follows */ diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 731ca1d781e..c918480812c 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -670,7 +670,7 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) } thd->server_status|= SERVER_STATUS_DB_DROPPED; send_ok(thd, (ulong) deleted); - thd->server_status&= !SERVER_STATUS_DB_DROPPED; + thd->server_status&= ~SERVER_STATUS_DB_DROPPED; } exit: From ab432d6c05d957eeaf7b311a16f2928614241f03 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 3 Feb 2005 20:48:58 +0300 Subject: [PATCH 09/42] Various fixes (cleanups, valgrind, makefiles, ...) server-tools/instance-manager/Makefile.am: increased default_monitoring interval server-tools/instance-manager/guardian.cc: some fixes for proper shutdown server-tools/instance-manager/guardian.h: removed init() prototype, as it was never used server-tools/instance-manager/instance.cc: cleanup() function removed server-tools/instance-manager/instance.h: cleanup() prototype removed server-tools/instance-manager/instance_map.cc: Instance_map::cleanup() removed, as instances have nothing to clean up server-tools/instance-manager/instance_map.h: Instance_map::cleanup() prototype removed server-tools/instance-manager/instance_options.cc: added print_argv() function for debug purposes server-tools/instance-manager/instance_options.h: declared print_argv() server-tools/instance-manager/listener.cc: some fixed in listener for proper shutdown server-tools/instance-manager/log.cc: cleanup server-tools/instance-manager/log.h: cleanup server-tools/instance-manager/manager.cc: some comments added server-tools/instance-manager/mysqlmanager.cc: we need to free memory in the very end server-tools/instance-manager/options.cc: fixed default options handling (as they were not working properly), added new method to cleanup Options server-tools/instance-manager/options.h: cleanup() declared server-tools/instance-manager/thread_registry.cc: cleanup server-tools/instance-manager/user_map.cc: missing password file is not a critical error anymore, as IM should be able to work as mysqld_safe only --- server-tools/instance-manager/Makefile.am | 4 +- server-tools/instance-manager/guardian.cc | 8 ++-- server-tools/instance-manager/guardian.h | 1 - server-tools/instance-manager/instance.cc | 7 +-- server-tools/instance-manager/instance.h | 1 - server-tools/instance-manager/instance_map.cc | 17 ------- server-tools/instance-manager/instance_map.h | 1 - .../instance-manager/instance_options.cc | 10 +++++ .../instance-manager/instance_options.h | 1 + server-tools/instance-manager/listener.cc | 45 ++++++++++++------- server-tools/instance-manager/log.cc | 4 +- server-tools/instance-manager/log.h | 4 +- server-tools/instance-manager/manager.cc | 18 +++++--- server-tools/instance-manager/mysqlmanager.cc | 2 + server-tools/instance-manager/options.cc | 13 +++++- server-tools/instance-manager/options.h | 3 ++ .../instance-manager/thread_registry.cc | 1 + server-tools/instance-manager/user_map.cc | 5 ++- 18 files changed, 85 insertions(+), 60 deletions(-) diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index fcc4a60c7a8..ff236b10ad5 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -33,7 +33,7 @@ liboptions_a_CPPFLAGS= $(CPPFLAGS) \ -DDEFAULT_MYSQLD_PATH="$(bindir)/mysqld$(EXEEXT)" \ -DDEFAULT_USER="root" \ -DDEFAULT_PASSWORD="" \ - -DDEFAULT_MONITORING_INTERVAL="5" \ + -DDEFAULT_MONITORING_INTERVAL="20" \ -DDEFAULT_PORT="2273" \ -DPROTOCOL_VERSION=@PROTOCOL_VERSION@ @@ -59,7 +59,7 @@ client_settings.h: Makefile rm -f $(srcdir)/client_settings.h @LN_CP_F@ $(top_srcdir)/sql/client_settings.h $(srcdir)/client_settings.h -bin_PROGRAMS= mysqlmanager +libexec_PROGRAMS= mysqlmanager mysqlmanager_SOURCES= command.cc command.h mysqlmanager.cc \ manager.h manager.cc log.h log.cc \ diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc index e8f9068dbb9..bc05fda1a8f 100644 --- a/server-tools/instance-manager/guardian.cc +++ b/server-tools/instance-manager/guardian.cc @@ -48,7 +48,6 @@ Guardian_thread::Guardian_thread(Thread_registry &thread_registry_arg, pthread_cond_init(&COND_guardian, 0); shutdown_guardian= FALSE; is_stopped= FALSE; - thread_registry.register_thread(&thread_info); init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0); guarded_instances= NULL; starting_instances= NULL; @@ -60,7 +59,6 @@ Guardian_thread::~Guardian_thread() /* delay guardian destruction to the moment when no one needs it */ pthread_mutex_lock(&LOCK_guardian); free_root(&alloc, MYF(0)); - thread_registry.unregister_thread(&thread_info); pthread_mutex_unlock(&LOCK_guardian); pthread_mutex_destroy(&LOCK_guardian); pthread_cond_destroy(&COND_guardian); @@ -102,6 +100,8 @@ void Guardian_thread::run() LIST *loop; struct timespec timeout; + thread_registry.register_thread(&thread_info); + my_thread_init(); pthread_mutex_lock(&LOCK_guardian); @@ -110,6 +110,7 @@ void Guardian_thread::run() { int status= 0; loop= guarded_instances; + while (loop != NULL) { instance= ((GUARD_NODE *) loop->data)->instance; @@ -167,6 +168,7 @@ void Guardian_thread::run() stop_instances(); is_stopped= TRUE; /* now, when the Guardian is stopped we can stop the IM */ + thread_registry.unregister_thread(&thread_info); thread_registry.request_shutdown(); my_thread_end(); } @@ -181,7 +183,7 @@ int Guardian_thread::start() while ((instance= iterator.next())) { if ((instance->options.nonguarded == NULL)) - if (guard(instance)) + if (add_instance_to_list(instance, &guarded_instances)) return 1; } instance_map->unlock(); diff --git a/server-tools/instance-manager/guardian.h b/server-tools/instance-manager/guardian.h index d96208247ae..bf96436a636 100644 --- a/server-tools/instance-manager/guardian.h +++ b/server-tools/instance-manager/guardian.h @@ -73,7 +73,6 @@ public: uint monitoring_interval_arg); ~Guardian_thread(); void run(); - int init(); int start(); void shutdown(); void request_stop_instances(); diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc index 6c32aa1d815..3ba943c9cd1 100644 --- a/server-tools/instance-manager/instance.cc +++ b/server-tools/instance-manager/instance.cc @@ -56,6 +56,7 @@ int Instance::start() switch (pid= fork()) { case 0: execv(options.mysqld_path, options.argv); + /* exec never returns */ exit(1); case -1: return ER_CANNOT_START_INSTANCE; @@ -69,12 +70,6 @@ int Instance::start() } -int Instance::cleanup() -{ - return 0; -} - - Instance::~Instance() { pthread_mutex_destroy(&LOCK_instance); diff --git a/server-tools/instance-manager/instance.h b/server-tools/instance-manager/instance.h index 29137c749c7..95852542fb4 100644 --- a/server-tools/instance-manager/instance.h +++ b/server-tools/instance-manager/instance.h @@ -38,7 +38,6 @@ public: bool is_running(); int start(); int stop(); - int cleanup(); public: enum { DEFAULT_SHUTDOWN_DELAY= 35 }; diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index 26181ec56e0..ba373087db4 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -198,23 +198,6 @@ void Instance_map::complete_initialization() } -int Instance_map::cleanup() -{ - Instance *instance; - uint i= 0; - - while (i < hash.records) - { - instance= (Instance *) hash_element(&hash, i); - if (instance->cleanup()) - return 1; - i++; - } - - return 0; -} - - Instance * Instance_map::find(uint instance_number) { diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h index 05bc4d94908..f2121f7141d 100644 --- a/server-tools/instance-manager/instance_map.h +++ b/server-tools/instance-manager/instance_map.h @@ -66,7 +66,6 @@ public: Instance *find(uint instance_number); int flush_instances(); - int cleanup(); int lock(); int unlock(); int init(); diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc index 0e29dca2422..31a1eafdd00 100644 --- a/server-tools/instance-manager/instance_options.cc +++ b/server-tools/instance-manager/instance_options.cc @@ -241,6 +241,16 @@ int Instance_options::add_to_argv(const char* option) return 0; } +void Instance_options::print_argv() +{ + int i; + printf("printing out an instance %s argv:\n", instance_name); + for (i=0; argv[i] != NULL; i++) + { + printf("argv: %s\n", argv[i]); + } +} + /* We execute this function to initialize some options. diff --git a/server-tools/instance-manager/instance_options.h b/server-tools/instance-manager/instance_options.h index eae22f840aa..1ed11fc9afa 100644 --- a/server-tools/instance-manager/instance_options.h +++ b/server-tools/instance-manager/instance_options.h @@ -50,6 +50,7 @@ public: pid_t get_pid(); void get_pid_filename(char *result); int unlink_pidfile(); + void print_argv(); public: /* diff --git a/server-tools/instance-manager/listener.cc b/server-tools/instance-manager/listener.cc index 15f57e7e595..8266c0dac4c 100644 --- a/server-tools/instance-manager/listener.cc +++ b/server-tools/instance-manager/listener.cc @@ -57,13 +57,11 @@ Listener_thread::Listener_thread(const Listener_thread_args &args) : ,total_connection_count(0) ,thread_info(pthread_self()) { - thread_registry.register_thread(&thread_info); } Listener_thread::~Listener_thread() { - thread_registry.unregister_thread(&thread_info); } @@ -82,6 +80,11 @@ void Listener_thread::run() enum { LISTEN_BACK_LOG_SIZE = 5 }; // standard backlog size int flags; int arg= 1; /* value to be set by setsockopt */ + int unix_socket; + uint im_port; + + thread_registry.register_thread(&thread_info); + /* I. prepare 'listen' sockets */ int ip_socket= socket(AF_INET, SOCK_STREAM, 0); @@ -89,8 +92,7 @@ void Listener_thread::run() { log_error("Listener_thead::run(): socket(AF_INET) failed, %s", strerror(errno)); - thread_registry.request_shutdown(); - return; + goto err; } struct sockaddr_in ip_socket_address; @@ -104,7 +106,7 @@ void Listener_thread::run() } else im_bind_addr= htonl(INADDR_ANY); - uint im_port= options.port_number; + im_port= options.port_number; ip_socket_address.sin_family= AF_INET; ip_socket_address.sin_addr.s_addr = im_bind_addr; @@ -119,16 +121,14 @@ void Listener_thread::run() { log_error("Listener_thread::run(): bind(ip socket) failed, '%s'", strerror(errno)); - thread_registry.request_shutdown(); - return; + goto err; } if (listen(ip_socket, LISTEN_BACK_LOG_SIZE)) { log_error("Listener_thread::run(): listen(ip socket) failed, %s", strerror(errno)); - thread_registry.request_shutdown(); - return; + goto err; } /* set the socket nonblocking */ flags= fcntl(ip_socket, F_GETFL, 0); @@ -140,13 +140,12 @@ void Listener_thread::run() log_info("accepting connections on ip socket"); /*--------------------------------------------------------------*/ - int unix_socket= socket(AF_UNIX, SOCK_STREAM, 0); + unix_socket= socket(AF_UNIX, SOCK_STREAM, 0); if (unix_socket == INVALID_SOCKET) { log_error("Listener_thead::run(): socket(AF_UNIX) failed, %s", strerror(errno)); - thread_registry.request_shutdown(); - return; + goto err; } struct sockaddr_un unix_socket_address; @@ -169,8 +168,7 @@ void Listener_thread::run() log_error("Listener_thread::run(): bind(unix socket) failed, " "socket file name is '%s', error '%s'", unix_socket_address.sun_path, strerror(errno)); - thread_registry.request_shutdown(); - return; + goto err; } umask(old_mask); @@ -178,8 +176,7 @@ void Listener_thread::run() { log_error("Listener_thread::run(): listen(unix socket) failed, %s", strerror(errno)); - thread_registry.request_shutdown(); - return; + goto err; } /* set the socket nonblocking */ @@ -205,7 +202,15 @@ void Listener_thread::run() while (thread_registry.is_shutdown() == false) { fd_set read_fds_arg= read_fds; + + /* + When using valgrind 2.0 this syscall doesn't get kicked off by a + signal during shutdown. This results in failing assert + (Thread_registry::~Thread_registry). Valgrind 2.2 works fine. + */ int rc= select(n, &read_fds_arg, 0, 0, 0); + + if (rc == -1 && errno != EINTR) log_error("Listener_thread::run(): select() failed, %s", strerror(errno)); @@ -256,6 +261,14 @@ void Listener_thread::run() close(unix_socket); close(ip_socket); unlink(unix_socket_address.sun_path); + + thread_registry.unregister_thread(&thread_info); + return; + +err: + thread_registry.unregister_thread(&thread_info); + thread_registry.request_shutdown(); + return; } diff --git a/server-tools/instance-manager/log.cc b/server-tools/instance-manager/log.cc index f89f5e425b8..dbcea3f9c3d 100644 --- a/server-tools/instance-manager/log.cc +++ b/server-tools/instance-manager/log.cc @@ -32,8 +32,8 @@ SYNOPSYS log() */ - -static inline void log(FILE *file, const char *format, va_list args) + +static inline void log(FILE *file, const char *format, va_list args) { /* log() should be thread-safe; it implies that we either call fprintf() diff --git a/server-tools/instance-manager/log.h b/server-tools/instance-manager/log.h index a1441d5bd71..825d7515513 100644 --- a/server-tools/instance-manager/log.h +++ b/server-tools/instance-manager/log.h @@ -17,14 +17,14 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* - Logging facilities. + Logging facilities. Two logging streams are supported: error log and info log. Additionally libdbug may be used for debug information output. ANSI C buffered I/O is used to perform logging. Logging is performed via stdout/stder, so one can reopen them to point to ordinary files. To initialize loggin environment log_init() must be called. - + Rationale: - no MYSQL_LOG as it has BIN mode, and not easy to fetch from sql_class.h - no constructors/desctructors to make logging available all the time diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index 6d9ee569d04..b48c020786f 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -127,6 +127,12 @@ void manager(const Options &options) pthread_attr_t guardian_thd_attr; int rc; + /* + NOTE: Guardian should be shutdowned first. Only then all other threads + need to be stopped. This should be done, as guardian is responsible for + shutting down the instances, and this is a long operation. + */ + pthread_attr_init(&guardian_thd_attr); pthread_attr_setdetachstate(&guardian_thd_attr, PTHREAD_CREATE_DETACHED); rc= pthread_create(&guardian_thd_id, &guardian_thd_attr, guardian, @@ -160,6 +166,11 @@ void manager(const Options &options) making the list. And they in their turn need alarms for timeout suppport. */ guardian_thread.start(); + /* + After the list of guarded instances have been initialized, + Guardian should start them. + */ + pthread_cond_signal(&guardian_thread.COND_guardian); signal(SIGPIPE, SIG_IGN); @@ -176,14 +187,14 @@ void manager(const Options &options) /* wake threads waiting for an instance to shutdown */ pthread_cond_broadcast(&instance_map.pid_cond.COND_pid); /* wake guardian */ - pthread_cond_broadcast(&guardian_thread.COND_guardian); + pthread_cond_signal(&guardian_thread.COND_guardian); break; default: if (!guardian_thread.is_stopped) { guardian_thread.request_stop_instances(); guardian_thread.shutdown(); - pthread_cond_broadcast(&guardian_thread.COND_guardian); + pthread_cond_signal(&guardian_thread.COND_guardian); } else { @@ -198,9 +209,6 @@ err: /* delete the pid file */ my_delete(options.pid_file_name, MYF(0)); - /* close permanent connections to the running instances */ - instance_map.cleanup(); - /* free alarm structures */ end_thr_alarm(1); /* don't pthread_exit to kill all threads who did not shut down in time */ diff --git a/server-tools/instance-manager/mysqlmanager.cc b/server-tools/instance-manager/mysqlmanager.cc index 9ad8ce2c858..bdd7c4ebe78 100644 --- a/server-tools/instance-manager/mysqlmanager.cc +++ b/server-tools/instance-manager/mysqlmanager.cc @@ -77,6 +77,8 @@ int main(int argc, char *argv[]) angel(options); } manager(options); + options.cleanup(); + my_end(0); return 0; } diff --git a/server-tools/instance-manager/options.cc b/server-tools/instance-manager/options.cc index 828a5d7b4d0..db117de03e5 100644 --- a/server-tools/instance-manager/options.cc +++ b/server-tools/instance-manager/options.cc @@ -38,6 +38,8 @@ const char *Options::default_mysqld_path= QUOTE(DEFAULT_MYSQLD_PATH); const char *Options::bind_address= 0; /* No default value */ uint Options::monitoring_interval= DEFAULT_MONITORING_INTERVAL; uint Options::port_number= DEFAULT_PORT; +/* just to declare */ +char **Options::saved_argv; /* List of options, accepted by the instance manager. @@ -81,7 +83,7 @@ static struct my_option my_long_options[] = { "port", OPT_PORT, "Port number to use for connections", (gptr *) &Options::port_number, (gptr *) &Options::port_number, - 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + 0, GET_UINT, REQUIRED_ARG, DEFAULT_PORT, 0, 0, 0, 0, 0 }, { "password-file", OPT_PASSWORD_FILE, "Look for Instane Manager users" " and passwords here.", @@ -98,7 +100,8 @@ static struct my_option my_long_options[] = " in seconds.", (gptr *) &Options::monitoring_interval, (gptr *) &Options::monitoring_interval, - 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + 0, GET_UINT, REQUIRED_ARG, DEFAULT_MONITORING_INTERVAL, + 0, 0, 0, 0, 0 }, { "run-as-service", OPT_RUN_AS_SERVICE, "Daemonize and start angel process.", (gptr *) &Options::run_as_service, @@ -171,5 +174,11 @@ void Options::load(int argc, char **argv) if (int rc= handle_options(&argc, &argv, my_long_options, get_one_option)) exit(rc); + Options::saved_argv= argv; } +void Options::cleanup() +{ + /* free_defaults returns nothing */ + free_defaults(Options::saved_argv); +} diff --git a/server-tools/instance-manager/options.h b/server-tools/instance-manager/options.h index fc2b44c8b53..8b673bd5fb1 100644 --- a/server-tools/instance-manager/options.h +++ b/server-tools/instance-manager/options.h @@ -40,7 +40,10 @@ struct Options static uint port_number; static const char *bind_address; + static char **saved_argv; + static void load(int argc, char **argv); + void cleanup(); }; #endif // INCLUDES_MYSQL_INSTANCE_MANAGER_OPTIONS_H diff --git a/server-tools/instance-manager/thread_registry.cc b/server-tools/instance-manager/thread_registry.cc index d0bf51f3d61..bf01aa6d7b2 100644 --- a/server-tools/instance-manager/thread_registry.cc +++ b/server-tools/instance-manager/thread_registry.cc @@ -197,6 +197,7 @@ void Thread_registry::deliver_shutdown() if (info->current_cond) pthread_cond_signal(info->current_cond); } + pthread_mutex_unlock(&LOCK_thread_registry); } diff --git a/server-tools/instance-manager/user_map.cc b/server-tools/instance-manager/user_map.cc index 8cbceceac7c..f145b611a8d 100644 --- a/server-tools/instance-manager/user_map.cc +++ b/server-tools/instance-manager/user_map.cc @@ -128,9 +128,10 @@ int User_map::load(const char *password_file_name) if ((file= my_fopen(password_file_name, O_RDONLY | O_BINARY, MYF(0))) == 0) { - log_error("can't open password file %s: errno=%d, %s", password_file_name, + /* Probably the password file wasn't specified. Try to leave without it */ + log_info("can't open password file %s: errno=%d, %s", password_file_name, errno, strerror(errno)); - return 1; + return 0; } while (fgets(line, sizeof(line), file)) From df1fab9d45cb7ffb3cf850d933341be13f7f416b Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 5 Feb 2005 06:23:23 +0300 Subject: [PATCH 10/42] Fix for BUG#7519: Index statistics is not displayed after ANALYZE for temporary tables: Call file->extra() with HA_STATUS_CONST in mysqld_show_keys. The fix will not be merged into 4.1/5.0 because they don't have this problem already. mysql-test/r/show_check.result: Testcase for BUG#7519 mysql-test/t/show_check.test: Testcase for BUG#7519 sql/sql_show.cc: Fix for BUG#7519: Index statistics is not displayed after ANALYZE for temporary tables: When handling "SHOW INDEX" the call file->extra(HA_STATUS_CONST | ...) is made for regular tables but bypassed for temporary tables. Call file->extra() with HA_STATUS_CONST in mysqld_show_keys to make sure we're always using the current index cardinality values. --- mysql-test/r/show_check.result | 22 ++++++++++++++++++++++ mysql-test/t/show_check.test | 16 ++++++++++++++++ sql/sql_show.cc | 3 ++- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result index f40b0693585..6d23dde1f1b 100644 --- a/mysql-test/r/show_check.result +++ b/mysql-test/r/show_check.result @@ -233,3 +233,25 @@ c decimal(4,3) YES NULL d double(4,3) YES NULL f float(4,3) YES NULL drop table t1; +CREATE TABLE t1 ( a VARCHAR(20) ); +INSERT INTO t1 VALUES ('one'),('two'),('three'),('four'),('five'); +CREATE TEMPORARY TABLE t2 ( index (a(20)) ) SELECT a FROM t1 GROUP BY a; +SHOW INDEX FROM t2; +Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment +t2 X a X X X NULL X X X BTREE +ANALYZE TABLE t2; +Table Op Msg_type Msg_text +test.t2 analyze status OK +SHOW INDEX FROM t2; +Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment +t2 X a X X X 5 X X X BTREE +SHOW CREATE TABLE t2; +Table Create Table +t2 CREATE TEMPORARY TABLE `t2` ( + `a` varchar(20) default NULL, + KEY `a` (`a`) +) TYPE=MyISAM +SHOW INDEX FROM t2; +Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment +t2 X a X X X 5 X X X BTREE +DROP TEMPORARY TABLE t2; diff --git a/mysql-test/t/show_check.test b/mysql-test/t/show_check.test index 2cd2012d109..e712ed77f04 100644 --- a/mysql-test/t/show_check.test +++ b/mysql-test/t/show_check.test @@ -131,3 +131,19 @@ drop table t1; create table t1 (c decimal(3,3), d double(3,3), f float(3,3)); show columns from t1; drop table t1; + +# Fix for BUG#7519: For temporary tables, SHOW INDEX doesn't display index +# cardinality after ANALYZE. +CREATE TABLE t1 ( a VARCHAR(20) ); +INSERT INTO t1 VALUES ('one'),('two'),('three'),('four'),('five'); +CREATE TEMPORARY TABLE t2 ( index (a(20)) ) SELECT a FROM t1 GROUP BY a; +--replace_column 2 X 4 X 5 X 6 X 8 X 9 X 10 X +SHOW INDEX FROM t2; +ANALYZE TABLE t2; +--replace_column 2 X 4 X 5 X 6 X 8 X 9 X 10 X +SHOW INDEX FROM t2; +SHOW CREATE TABLE t2; +--replace_column 2 X 4 X 5 X 6 X 8 X 9 X 10 X +SHOW INDEX FROM t2; +DROP TEMPORARY TABLE t2; + diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 2506033cda5..27246729162 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -686,7 +686,8 @@ mysqld_show_keys(THD *thd, TABLE_LIST *table_list) String *packet= &thd->packet; KEY *key_info=table->key_info; - table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK | HA_STATUS_TIME); + table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK | HA_STATUS_TIME | + HA_STATUS_CONST); for (uint i=0 ; i < table->keys ; i++,key_info++) { KEY_PART_INFO *key_part= key_info->key_part; From 87e104e44172ff4ed671d9d7c490d61e010c5fb3 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 5 Feb 2005 10:15:34 +0300 Subject: [PATCH 11/42] Fixed setup scripts to use mysqlmanager instead of mysqld_safe server-tools/instance-manager/Makefile.am: obsolete defaults removed. Also changed default binary placement. Now mysqlmanager should be installed in libexecdir (usually /usr/sbin) server-tools/instance-manager/instance_options.cc: bug, resulted in skipping non-sepcially recognized mysqld options fixed server-tools/instance-manager/user_map.cc: log message made a bit more verbose support-files/mysql.server.sh: use mysqlmanager tp start mysqld support-files/mysql.spec.sh: mysqlmanager should be placed in /usr/bin --- server-tools/instance-manager/Makefile.am | 4 +- .../instance-manager/instance_options.cc | 3 + server-tools/instance-manager/user_map.cc | 2 +- support-files/mysql.server.sh | 76 +++++++++---------- support-files/mysql.spec.sh | 1 + 5 files changed, 42 insertions(+), 44 deletions(-) diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index ff236b10ad5..4b776fecc83 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -30,9 +30,7 @@ liboptions_a_CPPFLAGS= $(CPPFLAGS) \ -DDEFAULT_LOG_FILE_NAME="$(localstatedir)/mysqlmanager.log" \ -DDEFAULT_SOCKET_FILE_NAME="$(localstatedir)/mysqlmanager.sock" \ -DDEFAULT_PASSWORD_FILE_NAME="$(sysconfdir)/mysqlmanager.passwd" \ - -DDEFAULT_MYSQLD_PATH="$(bindir)/mysqld$(EXEEXT)" \ - -DDEFAULT_USER="root" \ - -DDEFAULT_PASSWORD="" \ + -DDEFAULT_MYSQLD_PATH="$(libexecdir)/mysqld$(EXEEXT)" \ -DDEFAULT_MONITORING_INTERVAL="20" \ -DDEFAULT_PORT="2273" \ -DPROTOCOL_VERSION=@PROTOCOL_VERSION@ diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc index 31a1eafdd00..e7e17691330 100644 --- a/server-tools/instance-manager/instance_options.cc +++ b/server-tools/instance-manager/instance_options.cc @@ -225,6 +225,9 @@ int Instance_options::add_option(const char* option) } } + /* if we haven't returned earlier we should just save the option */ + insert_dynamic(&options_array,(gptr) &tmp); + return 0; err: diff --git a/server-tools/instance-manager/user_map.cc b/server-tools/instance-manager/user_map.cc index f145b611a8d..21d66912813 100644 --- a/server-tools/instance-manager/user_map.cc +++ b/server-tools/instance-manager/user_map.cc @@ -129,7 +129,7 @@ int User_map::load(const char *password_file_name) if ((file= my_fopen(password_file_name, O_RDONLY | O_BINARY, MYF(0))) == 0) { /* Probably the password file wasn't specified. Try to leave without it */ - log_info("can't open password file %s: errno=%d, %s", password_file_name, + log_info("[WARNING] can't open password file %s: errno=%d, %s", password_file_name, errno, strerror(errno)); return 0; } diff --git a/support-files/mysql.server.sh b/support-files/mysql.server.sh index 849f913bf6c..08e10c0192b 100644 --- a/support-files/mysql.server.sh +++ b/support-files/mysql.server.sh @@ -1,44 +1,29 @@ #!/bin/sh -# Copyright Abandoned 1996 TCX DataKonsult AB & Monty Program KB & Detron HB +# Copyright (C) 2005 MySQL AB # This file is public domain and comes with NO WARRANTY of any kind -# MySQL daemon start/stop script. +# MySQL server management daemon start/stop script. # Usually this is put in /etc/init.d (at least on machines SYSV R4 based -# systems) and linked to /etc/rc3.d/S99mysql and /etc/rc0.d/K01mysql. +# systems) and linked to /etc/rc3.d/S99mysqlmanager and +# /etc/rc0.d/K01mysqlmanager # When this is done the mysql server will be started when the machine is # started and shut down when the systems goes down. -# Comments to support chkconfig on RedHat Linux -# chkconfig: 2345 90 20 -# description: A very fast and reliable SQL database engine. +# description: MySQL database server Instance Manager # Comments to support LSB init script conventions ### BEGIN INIT INFO -# Provides: mysql +# Provides: mysqlmanager # Required-Start: $local_fs $network $remote_fs # Required-Stop: $local_fs $network $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 -# Short-Description: start and stop MySQL -# Description: MySQL is a very fast and reliable SQL database engine. +# Short-Description: start and stop MySQL Instance Manager +# Description: MySQL Instance Manager is used to start/stop/status/monitor +# MySQL server instances ### END INIT INFO -# If you install MySQL on some other places than @prefix@, then you -# have to do one of the following things for this script to work: -# -# - Run this script from within the MySQL installation directory -# - Create a /etc/my.cnf file with the following information: -# [mysqld] -# basedir= -# - Add the above to any other configuration file (for example ~/.my.ini) -# and copy my_print_defaults to /usr/bin -# - Add the path to the mysql-installation-directory to the basedir variable -# below. -# -# If you want to affect other MySQL variables, you should make your changes -# in the /etc/my.cnf, ~/.my.cnf or other MySQL configuration files. - basedir= # The following variables are only set for letting mysql.server find things. @@ -50,8 +35,10 @@ if test -z "$basedir" then basedir=@prefix@ bindir=@bindir@ + sbindir=@sbindir@ else bindir="$basedir/bin" + sbindir="$basedir/sbin" fi PATH=/sbin:/usr/sbin:/bin:/usr/bin:$basedir/bin @@ -65,11 +52,18 @@ case `echo "testing\c"`,`echo -n testing` in *) echo_n= echo_c='\c' ;; esac -parse_arguments() { +parse_server_arguments() { for arg do case "$arg" in --basedir=*) basedir=`echo "$arg" | sed -e 's/^[^=]*=//'` ;; --datadir=*) datadir=`echo "$arg" | sed -e 's/^[^=]*=//'` ;; + esac + done +} + +parse_manager_arguments() { + for arg do + case "$arg" in --pid-file=*) pid_file=`echo "$arg" | sed -e 's/^[^=]*=//'` ;; esac done @@ -125,14 +119,16 @@ then extra_args="-e $datadir/my.cnf" fi -parse_arguments `$print_defaults $extra_args mysqld server mysql_server mysql.server` +parse_server_arguments `$print_defaults $extra_args mysqld server mysql_server mysql.server` + +parse_manager_arguments `$print_defaults manager` # # Set pid file if not given # if test -z "$pid_file" then - pid_file=$datadir/`@HOSTNAME@`.pid + pid_file=$datadir/mysqlmanager-`@HOSTNAME@`.pid else case "$pid_file" in /* ) ;; @@ -147,18 +143,18 @@ case "$mode" in 'start') # Start daemon - if test -x $bindir/mysqld_safe + if test -x $sbindir/mysqlmanager then - # Give extra arguments to mysqld with the my.cnf file. This script may + # Give extra arguments to mysqlmanager with the my.cnf file. This script may # be overwritten at next upgrade. - $bindir/mysqld_safe --datadir=$datadir --pid-file=$pid_file >/dev/null 2>&1 & + $sbindir/mysqlmanager "--pid-file=$pid_file" --run-as-service >/dev/null 2>&1 & # Make lock for RedHat / SuSE if test -w /var/lock/subsys then - touch /var/lock/subsys/mysql + touch /var/lock/subsys/mysqlmanager fi else - echo "Can't execute $bindir/mysqld_safe from dir $basedir" + echo "Can't execute $sbindir/mysqlmanager from dir $basedir" fi ;; @@ -167,15 +163,15 @@ case "$mode" in # root password. if test -s "$pid_file" then - mysqld_pid=`cat $pid_file` - echo "Killing mysqld with pid $mysqld_pid" - kill $mysqld_pid - # mysqld should remove the pid_file when it exits, so wait for it. + mysqlmanager_pid=`cat $pid_file` + echo "Killing mysqlmanager with pid $mysqlmanager_pid" + kill $mysqlmanager_pid + # mysqlmanager should remove the pid_file when it exits, so wait for it. sleep 1 while [ -s $pid_file -a "$flags" != aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ] do - [ -z "$flags" ] && echo $echo_n "Wait for mysqld to exit$echo_c" || echo $echo_n ".$echo_c" + [ -z "$flags" ] && echo $echo_n "Wait for mysqlmanager to exit$echo_c" || echo $echo_n ".$echo_c" flags=a$flags sleep 1 done @@ -185,12 +181,12 @@ case "$mode" in then echo " done" fi # delete lock for RedHat / SuSE - if test -f /var/lock/subsys/mysql + if test -f /var/lock/subsys/mysqlmanager then - rm -f /var/lock/subsys/mysql + rm -f /var/lock/subsys/mysqlmanager fi else - echo "No mysqld pid file found. Looked for $pid_file." + echo "No mysqlmanager pid file found. Looked for $pid_file." fi ;; diff --git a/support-files/mysql.spec.sh b/support-files/mysql.spec.sh index 4a7f70b0b87..4767508315e 100644 --- a/support-files/mysql.spec.sh +++ b/support-files/mysql.spec.sh @@ -515,6 +515,7 @@ fi %attr(755, root, root) %{_bindir}/safe_mysqld %attr(755, root, root) %{_sbindir}/mysqld +%attr(755, root, root) %{_sbindir}/mysqlmanager %attr(755, root, root) %{_sbindir}/rcmysql %attr(644, root, root) %{_libdir}/mysql/mysqld.sym From 95e650e50a7ab59597a1c55ec48d7f807b2352a2 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 5 Feb 2005 14:04:49 +0300 Subject: [PATCH 12/42] --user option added to mysqlmanager server-tools/instance-manager/mysql_connection.cc: removed unsed method server-tools/instance-manager/mysqlmanager.cc: add --user option handling server-tools/instance-manager/options.cc: --user option added server-tools/instance-manager/options.h: user option added + some cleanup --- .../instance-manager/mysql_connection.cc | 7 -- server-tools/instance-manager/mysqlmanager.cc | 84 +++++++++++++++++++ server-tools/instance-manager/options.cc | 9 +- server-tools/instance-manager/options.h | 3 +- 4 files changed, 92 insertions(+), 11 deletions(-) diff --git a/server-tools/instance-manager/mysql_connection.cc b/server-tools/instance-manager/mysql_connection.cc index d3f58bf3771..7947de70459 100644 --- a/server-tools/instance-manager/mysql_connection.cc +++ b/server-tools/instance-manager/mysql_connection.cc @@ -82,7 +82,6 @@ private: private: /* Names are conventionally the same as in mysqld */ int check_connection(); - int check_user(const char *user, const char *password); int do_command(); int dispatch_command(enum enum_server_command command, const char *text, uint len); @@ -287,12 +286,6 @@ int Mysql_connection_thread::check_connection() } -int Mysql_connection_thread::check_user(const char *user, const char *password) -{ - return 0; -} - - int Mysql_connection_thread::do_command() { char *packet; diff --git a/server-tools/instance-manager/mysqlmanager.cc b/server-tools/instance-manager/mysqlmanager.cc index bdd7c4ebe78..e37dab911c1 100644 --- a/server-tools/instance-manager/mysqlmanager.cc +++ b/server-tools/instance-manager/mysqlmanager.cc @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include #include @@ -54,6 +56,8 @@ static void init_environment(char *progname); static void daemonize(const char *log_file_name); static void angel(const Options &options); +static struct passwd *check_user(const char *user); +static int set_user(const char *user, struct passwd *user_info); /* @@ -68,7 +72,19 @@ int main(int argc, char *argv[]) { init_environment(argv[0]); Options options; + struct passwd *user_info; + options.load(argc, argv); + + if ((user_info= check_user(options.user))) + { + if (set_user(options.user, user_info)) + { + options.cleanup(); + return 1; + } + } + if (options.run_as_service) { /* forks, and returns only in child */ @@ -84,6 +100,74 @@ int main(int argc, char *argv[]) /******************* Auxilary functions implementation **********************/ +/* Change to run as another user if started with --user */ + +static struct passwd *check_user(const char *user) +{ +#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__) + struct passwd *user_info; + uid_t user_id= geteuid(); + + /* Don't bother if we aren't superuser */ + if (user_id) + { + if (user) + { + /* Don't give a warning, if real user is same as given with --user */ + user_info= getpwnam(user); + if ((!user_info || user_id != user_info->pw_uid)) + log_info("One can only use the --user switch if running as root\n"); + } + return NULL; + } + if (!user) + { + log_info("You are running mysqlmanager as root! This might introduce security problems. It is safer to use --user option istead.\n"); + return NULL; + } + if (!strcmp(user, "root")) + return NULL; /* Avoid problem with dynamic libraries */ + if (!(user_info= getpwnam(user))) + { + /* Allow a numeric uid to be used */ + const char *pos; + for (pos= user; my_isdigit(default_charset_info, *pos); pos++) ; + if (*pos) /* Not numeric id */ + goto err; + if (!(user_info= getpwuid(atoi(user)))) + goto err; + else + return user_info; + } + else + return user_info; + +err: + log_error("Fatal error: Can't change to run as user '%s' ; Please check that the user exists!\n", user); +#endif + return NULL; +} + +static int set_user(const char *user, struct passwd *user_info) +{ + DBUG_ASSERT(user_info); +#ifdef HAVE_INITGROUPS + initgroups((char*) user,user_info->pw_gid); +#endif + if (setgid(user_info->pw_gid) == -1) + { + log_error("setgid() failed"); + return 1; + } + if (setuid(user_info->pw_uid) == -1) + { + log_error("setuid() failed"); + return 1; + } + return 0; +} + + /* Init environment, common for daemon and non-daemon diff --git a/server-tools/instance-manager/options.cc b/server-tools/instance-manager/options.cc index db117de03e5..bf6ba1e9163 100644 --- a/server-tools/instance-manager/options.cc +++ b/server-tools/instance-manager/options.cc @@ -35,7 +35,8 @@ const char *Options::pid_file_name= QUOTE(DEFAULT_PID_FILE_NAME); const char *Options::socket_file_name= QUOTE(DEFAULT_SOCKET_FILE_NAME); const char *Options::password_file_name= QUOTE(DEFAULT_PASSWORD_FILE_NAME); const char *Options::default_mysqld_path= QUOTE(DEFAULT_MYSQLD_PATH); -const char *Options::bind_address= 0; /* No default value */ +const char *Options::bind_address= 0; /* No default value */ +const char *Options::user= 0; /* No default value */ uint Options::monitoring_interval= DEFAULT_MONITORING_INTERVAL; uint Options::port_number= DEFAULT_PORT; /* just to declare */ @@ -54,7 +55,6 @@ enum options { OPT_MYSQLD_PATH, OPT_RUN_AS_SERVICE, OPT_USER, - OPT_PASSWORD, OPT_MONITORING_INTERVAL, OPT_PORT, OPT_BIND_ADDRESS @@ -107,6 +107,11 @@ static struct my_option my_long_options[] = "Daemonize and start angel process.", (gptr *) &Options::run_as_service, 0, 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 0, 0 }, + { "user", OPT_USER, "Username to start mysqlmanager", + (gptr *) &Options::user, + (gptr *) &Options::user, + 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + { "version", 'V', "Output version information and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }, diff --git a/server-tools/instance-manager/options.h b/server-tools/instance-manager/options.h index 8b673bd5fb1..bb6da51d1ca 100644 --- a/server-tools/instance-manager/options.h +++ b/server-tools/instance-manager/options.h @@ -34,8 +34,7 @@ struct Options static const char *socket_file_name; static const char *password_file_name; static const char *default_mysqld_path; - static const char *default_admin_user; - static const char *default_admin_password; + static const char *user; static uint monitoring_interval; static uint port_number; static const char *bind_address; From 79ba407d64ad4387b7b279f80a7bf95e68414df5 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 6 Feb 2005 23:43:34 +0300 Subject: [PATCH 13/42] setup fixes support-files/Makefile.am: New default file added support-files/mysql.server.sh: now mysqlmanager is started using mysql account support-files/mysql.spec.sh: my.cnf copied to the /etc/ to let mysqlmanager start [mysqld] instance at startup --- support-files/Makefile.am | 1 + support-files/my.cnf | 2 ++ support-files/mysql.server.sh | 5 ++++- support-files/mysql.spec.sh | 10 +++------- 4 files changed, 10 insertions(+), 8 deletions(-) create mode 100644 support-files/my.cnf diff --git a/support-files/Makefile.am b/support-files/Makefile.am index 7ae1071f9ec..c2131027db8 100644 --- a/support-files/Makefile.am +++ b/support-files/Makefile.am @@ -19,6 +19,7 @@ EXTRA_DIST = mysql.spec.sh \ my-small.cnf.sh \ + my.cnf \ my-medium.cnf.sh \ my-large.cnf.sh \ my-huge.cnf.sh \ diff --git a/support-files/my.cnf b/support-files/my.cnf new file mode 100644 index 00000000000..062c78be46a --- /dev/null +++ b/support-files/my.cnf @@ -0,0 +1,2 @@ +[mysqld] +port=3307 diff --git a/support-files/mysql.server.sh b/support-files/mysql.server.sh index 08e10c0192b..645b54b610a 100644 --- a/support-files/mysql.server.sh +++ b/support-files/mysql.server.sh @@ -136,6 +136,9 @@ else esac fi +user=@MYSQLD_USER@ +USER_OPTION="--user=$user" + # Safeguard (relative paths, core dumps..) cd $basedir @@ -147,7 +150,7 @@ case "$mode" in then # Give extra arguments to mysqlmanager with the my.cnf file. This script may # be overwritten at next upgrade. - $sbindir/mysqlmanager "--pid-file=$pid_file" --run-as-service >/dev/null 2>&1 & + $sbindir/mysqlmanager "--pid-file=$pid_file" $USER_OPTION --run-as-service >/dev/null 2>&1 & # Make lock for RedHat / SuSE if test -w /var/lock/subsys then diff --git a/support-files/mysql.spec.sh b/support-files/mysql.spec.sh index 4767508315e..5f3dad86d89 100644 --- a/support-files/mysql.spec.sh +++ b/support-files/mysql.spec.sh @@ -362,8 +362,9 @@ install -s -m755 $MBD/sql/mysqld-max $RBR%{_sbindir}/mysqld-max install -m644 $MBD/sql/mysqld-max.sym $RBR%{_libdir}/mysql/mysqld-max.sym install -m644 $MBD/sql/mysqld.sym $RBR%{_libdir}/mysql/mysqld.sym -# Install logrotate and autostart +# Install logrotate, autostart and config file install -m644 $MBD/support-files/mysql-log-rotate $RBR%{_sysconfdir}/logrotate.d/mysql +install -m644 $MBD/support-files/my.cnf $RBR%{_sysconfdir}/my.cnf install -m755 $MBD/support-files/mysql.server $RBR%{_sysconfdir}/init.d/mysql # Create a symlink "rcmysql", pointing to the init.script. SuSE users @@ -374,10 +375,6 @@ ln -s %{_sysconfdir}/init.d/mysql $RPM_BUILD_ROOT%{_sbindir}/rcmysql # (safe_mysqld will be gone in MySQL 4.1) ln -sf ./mysqld_safe $RBR%{_bindir}/safe_mysqld -# Touch the place where the my.cnf config file might be located -# Just to make sure it's in the file list and marked as a config file -touch $RBR%{_sysconfdir}/my.cnf - %pre server # Shut down a previously installed server first if test -x %{_sysconfdir}/init.d/mysql @@ -486,8 +483,6 @@ fi %doc %attr(644, root, man) %{_mandir}/man1/perror.1* %doc %attr(644, root, man) %{_mandir}/man1/replace.1* -%ghost %config(noreplace,missingok) %{_sysconfdir}/my.cnf - %attr(755, root, root) %{_bindir}/my_print_defaults %attr(755, root, root) %{_bindir}/myisamchk %attr(755, root, root) %{_bindir}/myisam_ftdump @@ -520,6 +515,7 @@ fi %attr(644, root, root) %{_libdir}/mysql/mysqld.sym %attr(644, root, root) %config(noreplace,missingok) %{_sysconfdir}/logrotate.d/mysql +%attr(644, root, root) %config(noreplace,missingok) %{_sysconfdir}/my.cnf %attr(755, root, root) %{_sysconfdir}/init.d/mysql %attr(755, root, root) %{_datadir}/mysql/ From dce2554f9130920ab18e4e31432df7d6ca165ee0 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 11 Feb 2005 14:21:59 +0300 Subject: [PATCH 14/42] Post-review fixes + some bugs fixed + several minor features BitKeeper/deleted/.del-client_func.c~3476a8a85cbd3c29: Delete: server-tools/instance-manager/client_func.c server-tools/instance-manager/Makefile.am: clien_func removed server-tools/instance-manager/buffer.cc: several methods added server-tools/instance-manager/buffer.h: Some error-handling fixes. server-tools/instance-manager/commands.cc: check for Buffer errors server-tools/instance-manager/guardian.cc: Guardian rewiriten. Not it works in a finite state machine-way. server-tools/instance-manager/guardian.h: Appropriate (to .cc) changes in the header + some comment added server-tools/instance-manager/instance.cc: added proxy thread to monitor instance. Two kinds of stop() now -- stop() and kill_instance which only sends a signal server-tools/instance-manager/instance.h: appropriate changes server-tools/instance-manager/instance_map.cc: cleanup server-tools/instance-manager/instance_map.h: cleanup server-tools/instance-manager/instance_options.cc: Caching of the pid-file-name is added. some comments added server-tools/instance-manager/instance_options.h: cleanup server-tools/instance-manager/listener.cc: listener my_thread_init added (though it doesn't use any mysys functions). Just in case server-tools/instance-manager/manager.cc: SIGCHLD handler removed. now instance monitoring is implemented through proxy threads. This is to work nicely with LinuxThreads server-tools/instance-manager/options.cc: added option to create a password file entry (this was implemented by Sergei Vojtovich) server-tools/instance-manager/parse.cc: inline function get_word moved to the header server-tools/instance-manager/parse.h: get_word moved here to use form parse_output server-tools/instance-manager/parse_output.cc: get_word() clone removed. now looking through the output linewise server-tools/instance-manager/protocol.cc: Buffer error chech added server-tools/instance-manager/user_map.cc: typo fixed --- server-tools/instance-manager/Makefile.am | 2 +- server-tools/instance-manager/buffer.cc | 16 +- server-tools/instance-manager/buffer.h | 14 +- server-tools/instance-manager/client_func.c | 32 -- server-tools/instance-manager/commands.cc | 12 +- server-tools/instance-manager/guardian.cc | 317 ++++++++++++------ server-tools/instance-manager/guardian.h | 49 ++- server-tools/instance-manager/instance.cc | 196 +++++++---- server-tools/instance-manager/instance.h | 9 +- server-tools/instance-manager/instance_map.cc | 20 -- server-tools/instance-manager/instance_map.h | 8 - .../instance-manager/instance_options.cc | 89 ++--- .../instance-manager/instance_options.h | 8 +- server-tools/instance-manager/listener.cc | 4 + server-tools/instance-manager/manager.cc | 26 +- server-tools/instance-manager/options.cc | 37 +- server-tools/instance-manager/parse.cc | 23 -- server-tools/instance-manager/parse.h | 30 ++ server-tools/instance-manager/parse_output.cc | 72 ++-- server-tools/instance-manager/protocol.cc | 3 +- server-tools/instance-manager/user_map.cc | 2 +- 21 files changed, 593 insertions(+), 376 deletions(-) delete mode 100644 server-tools/instance-manager/client_func.c diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index 4b776fecc83..21d11e2f7d6 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -74,7 +74,7 @@ mysqlmanager_SOURCES= command.cc command.h mysqlmanager.cc \ buffer.h buffer.cc parse.cc parse.h \ guardian.cc guardian.h \ parse_output.cc parse_output.h \ - mysql_manager_error.h client_func.c + mysql_manager_error.h mysqlmanager_LDADD= liboptions.a \ libnet.a \ diff --git a/server-tools/instance-manager/buffer.cc b/server-tools/instance-manager/buffer.cc index ca84adbfd10..3a2a818e478 100644 --- a/server-tools/instance-manager/buffer.cc +++ b/server-tools/instance-manager/buffer.cc @@ -40,7 +40,7 @@ RETURN 0 - ok - 1 - The buffer came to 16Mb barrier + 1 - got an error in reserve() */ int Buffer::append(uint position, const char *string, uint len_arg) @@ -71,7 +71,7 @@ int Buffer::append(uint position, const char *string, uint len_arg) RETURN 0 - ok - 1 - The buffer came to 16Mb barrier + 1 - realloc error or we have come to the 16Mb barrier */ int Buffer::reserve(uint position, uint len_arg) @@ -92,6 +92,18 @@ int Buffer::reserve(uint position, uint len_arg) return 0; err: + error= 1; return 1; } + +int Buffer::get_size() +{ + return buffer_size; +} + + +int Buffer::is_error() +{ + return error; +} diff --git a/server-tools/instance-manager/buffer.h b/server-tools/instance-manager/buffer.h index 66860bd67b5..260a9ef92f8 100644 --- a/server-tools/instance-manager/buffer.h +++ b/server-tools/instance-manager/buffer.h @@ -36,11 +36,17 @@ private: /* maximum buffer size is 16Mb */ enum { MAX_BUFFER_SIZE= 16777216 }; size_t buffer_size; + /* Error flag. Triggered if we get an error of some kind */ + int error; public: - Buffer() + Buffer(size_t buffer_size_arg= BUFFER_INITIAL_SIZE) + :buffer_size(BUFFER_INITIAL_SIZE), error(0) { - buffer=(char *) malloc(BUFFER_INITIAL_SIZE); - buffer_size= BUFFER_INITIAL_SIZE; + /* + As append() will invokes realloc() anyway, it's ok if malloc returns 0 + */ + if (!(buffer= (char*) malloc(buffer_size))) + buffer_size= 0; } ~Buffer() @@ -50,6 +56,8 @@ public: public: char *buffer; + int get_size(); + int is_error(); int append(uint position, const char *string, uint len_arg); int reserve(uint position, uint len_arg); }; diff --git a/server-tools/instance-manager/client_func.c b/server-tools/instance-manager/client_func.c deleted file mode 100644 index 92c106e5172..00000000000 --- a/server-tools/instance-manager/client_func.c +++ /dev/null @@ -1,32 +0,0 @@ -#include -#include -#include - -/* - Currently we cannot use libmysqlclient directly because of the linking - issues. Here we provide needed libmysqlclient functions. - TODO: to think how to use libmysqlclient code instead of copy&paste. - The other possible solution is to use simple_command directly. -*/ - -const char * STDCALL -mysql_get_server_info(MYSQL *mysql) -{ - return((char*) mysql->server_version); -} - -int STDCALL -mysql_ping(MYSQL *mysql) -{ - DBUG_ENTER("mysql_ping"); - DBUG_RETURN(simple_command(mysql,COM_PING,0,0,0)); -} - -int STDCALL -mysql_shutdown(MYSQL *mysql, enum mysql_enum_shutdown_level shutdown_level) -{ - uchar level[1]; - DBUG_ENTER("mysql_shutdown"); - level[0]= (uchar) shutdown_level; - DBUG_RETURN(simple_command(mysql, COM_SHUTDOWN, (char *)level, 1, 0)); -} diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc index 70774115429..3f38f21b008 100644 --- a/server-tools/instance-manager/commands.cc +++ b/server-tools/instance-manager/commands.cc @@ -184,7 +184,8 @@ int Show_instance_status::do_command(struct st_net *net, } - if (my_net_write(net, send_buff.buffer, (uint) position)) + if (my_net_write(net, send_buff.buffer, (uint) position) || + send_buff.is_error()) goto err; } @@ -270,7 +271,8 @@ int Show_instance_options::do_command(struct st_net *net, store_to_string(&send_buff, (char *) instance->options.mysqld_path, &position); - if (my_net_write(net, send_buff.buffer, (uint) position)) + if (my_net_write(net, send_buff.buffer, (uint) position) || + send_buff.is_error()) goto err; } @@ -279,7 +281,8 @@ int Show_instance_options::do_command(struct st_net *net, position= 0; store_to_string(&send_buff, (char *) "nonguarded", &position); store_to_string(&send_buff, "", &position); - if (my_net_write(net, send_buff.buffer, (uint) position)) + if (my_net_write(net, send_buff.buffer, (uint) position) || + send_buff.is_error()) goto err; } @@ -296,7 +299,8 @@ int Show_instance_options::do_command(struct st_net *net, store_to_string(&send_buff, option_value + 1, &position); /* join name and the value into the same option again */ *option_value= '='; - if (my_net_write(net, send_buff.buffer, (uint) position)) + if (my_net_write(net, send_buff.buffer, (uint) position) || + send_buff.is_error()) goto err; } } diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc index bc05fda1a8f..750c3495870 100644 --- a/server-tools/instance-manager/guardian.cc +++ b/server-tools/instance-manager/guardian.cc @@ -21,9 +21,32 @@ #include "guardian.h" #include "instance_map.h" +#include "instance.h" #include "mysql_manager_error.h" #include "log.h" #include +#include +#include + + +/* + The Guardian list node structure. Guardian utilizes it to store + guarded instances plus some additional info. +*/ + +struct GUARD_NODE +{ + Instance *instance; + /* state of an instance (i.e. STARTED, CRASHED, etc.) */ + int state; + /* the amount of attemts to restart instance (cleaned up at success) */ + int restart_counter; + /* triggered at a crash */ + time_t crash_moment; + /* General time field. Used to provide timeouts (at shutdown and restart) */ + time_t last_checked; +}; + C_MODE_START @@ -42,15 +65,13 @@ Guardian_thread::Guardian_thread(Thread_registry &thread_registry_arg, uint monitoring_interval_arg) : Guardian_thread_args(thread_registry_arg, instance_map_arg, monitoring_interval_arg), - thread_info(pthread_self()) + thread_info(pthread_self()), guarded_instances(0) { pthread_mutex_init(&LOCK_guardian, 0); pthread_cond_init(&COND_guardian, 0); - shutdown_guardian= FALSE; - is_stopped= FALSE; + shutdown_requested= FALSE; + stopped= FALSE; init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0); - guarded_instances= NULL; - starting_instances= NULL; } @@ -65,19 +86,107 @@ Guardian_thread::~Guardian_thread() } -void Guardian_thread::shutdown() +void Guardian_thread::request_shutdown(bool stop_instances_arg) { pthread_mutex_lock(&LOCK_guardian); - shutdown_guardian= TRUE; + /* stop instances or just clean up Guardian repository */ + stop_instances(stop_instances_arg); + shutdown_requested= TRUE; pthread_mutex_unlock(&LOCK_guardian); } -void Guardian_thread::request_stop_instances() +void Guardian_thread::process_instance(Instance *instance, + GUARD_NODE *current_node, + LIST **guarded_instances, + LIST *elem) { - pthread_mutex_lock(&LOCK_guardian); - request_stop= TRUE; - pthread_mutex_unlock(&LOCK_guardian); + int waitchild= Instance::DEFAULT_SHUTDOWN_DELAY; + /* The amount of times, Guardian attempts to restart an instance */ + int restart_retry= 100; + time_t current_time= time(NULL); + + if (current_node->state == STOPPING) + { + /* this brach is executed during shutdown */ + if (instance->options.shutdown_delay != NULL) + waitchild= atoi(instance->options.shutdown_delay); + + /* this returns true if and only if an instance was stopped for shure */ + if (instance->is_crashed()) + *guarded_instances= list_delete(*guarded_instances, elem); + else if (current_time - current_node->last_checked > waitchild) + { + instance->kill_instance(SIGKILL); + /* + Later we do elem= elem->next. This is ok, as we are only removing + the node from the list. The pointer to the next one is still valid. + */ + *guarded_instances= list_delete(*guarded_instances, elem); + } + + return; + } + + if (instance->is_running()) + { + /* clear status fields */ + current_node->restart_counter= 0; + current_node->crash_moment= 0; + current_node->state= STARTED; + } + else + { + switch (current_node->state) + { + case NOT_STARTED: + instance->start(); + current_node->last_checked= current_time; + log_info("guardian: starting instance %s", + instance->options.instance_name); + current_node->state= STARTING; + break; + case STARTED: /* fallthrough */ + case STARTING: /* let the instance start or crash */ + if (instance->is_crashed()) + { + current_node->crash_moment= current_time; + current_node->last_checked= current_time; + current_node->state= JUST_CRASHED; + /* fallthrough -- restart an instance immediately */ + } + else + break; + case JUST_CRASHED: + if (current_time - current_node->crash_moment <= 2) + { + instance->start(); + log_info("guardian: starting instance %s", + instance->options.instance_name); + } + else current_node->state= CRASHED; + break; + case CRASHED: /* just regular restarts */ + if (current_time - current_node->last_checked > + monitoring_interval) + { + if ((current_node->restart_counter < restart_retry)) + { + instance->start(); + current_node->last_checked= current_time; + ((GUARD_NODE *) elem->data)->restart_counter++; + log_info("guardian: starting instance %s", + instance->options.instance_name); + } + else current_node->state= CRASHED_AND_ABANDONED; + } + break; + case CRASHED_AND_ABANDONED: + break; /* do nothing */ + default: + DBUG_ASSERT(0); + } + } } @@ -96,8 +205,7 @@ void Guardian_thread::request_stop_instances() void Guardian_thread::run() { Instance *instance; - int restart_retry= 100; - LIST *loop; + LIST *elem; struct timespec timeout; thread_registry.register_thread(&thread_info); @@ -105,68 +213,31 @@ void Guardian_thread::run() my_thread_init(); pthread_mutex_lock(&LOCK_guardian); - - while (!shutdown_guardian) + /* loop, until all instances were shut down at the end */ + while (!(shutdown_requested && (guarded_instances == NULL))) { - int status= 0; - loop= guarded_instances; + elem= guarded_instances; - while (loop != NULL) + while (elem != NULL) { - instance= ((GUARD_NODE *) loop->data)->instance; - if (!instance->is_running()) - { - int state= 0; /* state of guardian */ + struct timespec timeout; - if ((((GUARD_NODE *) loop->data)->crash_moment == 0)) - state= 1; /* an instance just crashed */ - else - if (time(NULL) - ((GUARD_NODE *) loop->data)->crash_moment <= 2) - /* try to restart an instance immediately */ - state= 2; - else - state= 3; /* try to restart it */ + GUARD_NODE *current_node= (GUARD_NODE *) elem->data; + instance= ((GUARD_NODE *) elem->data)->instance; + process_instance(instance, current_node, &guarded_instances, elem); - if (state == 1) - ((GUARD_NODE *) loop->data)->crash_moment= time(NULL); - - if ((state == 1) || (state == 2)) - { - instance->start(); - ((GUARD_NODE *) loop->data)->restart_counter++; - log_info("guardian: starting instance %s", - instance->options.instance_name); - } - else - { - if ((status == ETIMEDOUT) && - (((GUARD_NODE *) loop->data)->restart_counter < restart_retry)) - { - instance->start(); - ((GUARD_NODE *) loop->data)->restart_counter++; - log_info("guardian: starting instance %s", - instance->options.instance_name); - } - } - } - else /* clear status fields */ - { - ((GUARD_NODE *) loop->data)->restart_counter= 0; - ((GUARD_NODE *) loop->data)->crash_moment= 0; - } - loop= loop->next; + elem= elem->next; } - move_to_list(&starting_instances, &guarded_instances); timeout.tv_sec= time(NULL) + monitoring_interval; timeout.tv_nsec= 0; - status= pthread_cond_timedwait(&COND_guardian, &LOCK_guardian, &timeout); + /* check the loop predicate before sleeping */ + if (!(shutdown_requested && (guarded_instances == NULL))) + pthread_cond_timedwait(&COND_guardian, &LOCK_guardian, &timeout); } + stopped= TRUE; pthread_mutex_unlock(&LOCK_guardian); - if (request_stop) - stop_instances(); - is_stopped= TRUE; /* now, when the Guardian is stopped we can stop the IM */ thread_registry.unregister_thread(&thread_info); thread_registry.request_shutdown(); @@ -174,7 +245,29 @@ void Guardian_thread::run() } -int Guardian_thread::start() +int Guardian_thread::is_stopped() +{ + int var; + pthread_mutex_lock(&LOCK_guardian); + var= stopped; + pthread_mutex_unlock(&LOCK_guardian); + return var; +} + + +/* + Initialize the list of guarded instances: loop through the Instance_map and + add all of the instances, which don't have 'nonguarded' option specified. + + SYNOPSYS + Guardian_thread::init() + + RETURN + 0 - ok + 1 - error occured +*/ + +int Guardian_thread::init() { Instance *instance; Instance_map::Iterator iterator(instance_map); @@ -183,7 +276,7 @@ int Guardian_thread::start() while ((instance= iterator.next())) { if ((instance->options.nonguarded == NULL)) - if (add_instance_to_list(instance, &guarded_instances)) + if (guard(instance)) return 1; } instance_map->unlock(); @@ -193,7 +286,7 @@ int Guardian_thread::start() /* - Start instance guarding + Add instance to the Guardian list SYNOPSYS guard() @@ -201,36 +294,15 @@ int Guardian_thread::start() DESCRIPTION - The instance is added to the list of starting instances. Then after one guardian - loop it is moved to the guarded instances list. Usually guard() is called after we - start an instance, so we need to give some time to the instance to start. + The instance is added to the guarded instances list. Usually guard() is + called after we start an instance. RETURN 0 - ok 1 - error occured */ - int Guardian_thread::guard(Instance *instance) -{ - return add_instance_to_list(instance, &starting_instances); -} - - -void Guardian_thread::move_to_list(LIST **from, LIST **to) -{ - LIST *tmp; - - while (*from) - { - tmp= rest(*from); - *to= list_add(*to, *from); - *from= tmp; - } -} - - -int Guardian_thread::add_instance_to_list(Instance *instance, LIST **list) { LIST *node; GUARD_NODE *content; @@ -244,10 +316,11 @@ int Guardian_thread::add_instance_to_list(Instance *instance, LIST **list) content->instance= instance; content->restart_counter= 0; content->crash_moment= 0; + content->state= NOT_STARTED; node->data= (void *) content; pthread_mutex_lock(&LOCK_guardian); - *list= list_add(*list, node); + guarded_instances= list_add(guarded_instances, node); pthread_mutex_unlock(&LOCK_guardian); return 0; @@ -256,7 +329,7 @@ int Guardian_thread::add_instance_to_list(Instance *instance, LIST **list) /* TODO: perhaps it would make sense to create a pool of the LIST elements - elements and give them upon request. Now we are loosing a bit of memory when + and give them upon request. Now we are loosing a bit of memory when guarded instance was stopped and then restarted (since we cannot free just a piece of the MEM_ROOT). */ @@ -288,21 +361,61 @@ int Guardian_thread::stop_guard(Instance *instance) return 0; } -int Guardian_thread::stop_instances() -{ - Instance *instance; - Instance_map::Iterator iterator(instance_map); +/* + Start Guardian shutdown. Attempt to start instances if requested. - while ((instance= iterator.next())) + SYNOPSYS + stop_instances() + stop_instances_arg whether we should stop instances at shutdown + + DESCRIPTION + + Loops through the guarded_instances list and prepares them for shutdown. + If stop_instances was requested, we need to issue a stop command and change + the state accordingly. Otherwise we could simply delete an entry. + NOTE: Guardian should be locked by the calling function + + RETURN + 0 - ok + 1 - error occured +*/ + +int Guardian_thread::stop_instances(bool stop_instances_arg) +{ + LIST *node; + node= guarded_instances; + while (node != NULL) { - if ((instance->options.nonguarded == NULL)) + if (!stop_instances_arg) { - if (stop_guard(instance)) - return 1; - /* let us try to stop the server */ - instance->stop(); + /* just forget about an instance */ + guarded_instances= list_delete(guarded_instances, node); + /* + This should still work fine, as we have only removed the + node from the list. The pointer to the next one is still valid + */ + node= node->next; + } + else + { + GUARD_NODE *current_node= (GUARD_NODE *) node->data; + /* + If instance is running or was running (and now probably hanging), + request stop. + */ + if (current_node->instance->is_running() || + (current_node->state == STARTED)) + { + current_node->state= STOPPING; + current_node->last_checked= time(NULL); + } + else + /* otherwise remove it from the list */ + guarded_instances= list_delete(guarded_instances, node); + /* But try to kill it anyway. Just in case */ + current_node->instance->kill_instance(SIGTERM); + node= node->next; } } - return 0; } diff --git a/server-tools/instance-manager/guardian.h b/server-tools/instance-manager/guardian.h index bf96436a636..0aec00099de 100644 --- a/server-tools/instance-manager/guardian.h +++ b/server-tools/instance-manager/guardian.h @@ -19,6 +19,7 @@ #include #include #include +#include "thread_registry.h" #ifdef __GNUC__ #pragma interface @@ -26,9 +27,8 @@ class Instance; class Instance_map; - -#include "thread_registry.h" -#include "instance.h" +class Thread_registry; +struct GUARD_NODE; C_MODE_START @@ -36,19 +36,11 @@ pthread_handler_decl(guardian, arg); C_MODE_END -typedef struct st_guard_node -{ - Instance *instance; - uint restart_counter; - time_t crash_moment; -} GUARD_NODE; - - struct Guardian_thread_args { Thread_registry &thread_registry; Instance_map *instance_map; - uint monitoring_interval; + int monitoring_interval; Guardian_thread_args(Thread_registry &thread_registry_arg, Instance_map *instance_map_arg, @@ -72,36 +64,41 @@ public: Instance_map *instance_map_arg, uint monitoring_interval_arg); ~Guardian_thread(); + /* Main funtion of the thread */ void run(); - int start(); - void shutdown(); - void request_stop_instances(); + /* Initialize list of guarded instances */ + int init(); + /* Request guardian shutdown. Stop instances if needed */ + void request_shutdown(bool stop_instances); + /* Start instance protection */ int guard(Instance *instance); + /* Stop instance protection */ int stop_guard(Instance *instance); - bool is_stopped; + /* Returns true if guardian thread is stopped */ + int is_stopped(); public: pthread_cond_t COND_guardian; private: - int stop_instances(); - int add_instance_to_list(Instance *instance, LIST **list); - void move_to_list(LIST **from, LIST **to); + /* Prepares Guardian shutdown. Stops instances is needed */ + int stop_instances(bool stop_instances_arg); + /* check instance state and act accordingly */ + void process_instance(Instance *instance, GUARD_NODE *current_node, + LIST **guarded_instances, LIST *elem); + int stopped; private: + /* states of an instance */ + enum { NOT_STARTED= 1, STARTING, STARTED, JUST_CRASHED, CRASHED, + CRASHED_AND_ABANDONED, STOPPING }; pthread_mutex_t LOCK_guardian; Thread_info thread_info; LIST *guarded_instances; - LIST *starting_instances; MEM_ROOT alloc; enum { MEM_ROOT_BLOCK_SIZE= 512 }; /* this variable is set to TRUE when we want to stop Guardian thread */ - bool shutdown_guardian; - /* - This var is usually set together with shutdown_guardian. this way we - request guardian to shut down all instances before termination - */ - bool request_stop; + bool shutdown_requested; }; #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_GUARDIAN_H */ diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc index 3ba943c9cd1..e654612de3e 100644 --- a/server-tools/instance-manager/instance.cc +++ b/server-tools/instance-manager/instance.cc @@ -27,6 +27,19 @@ #include #include + +C_MODE_START + +pthread_handler_decl(proxy, arg) +{ + Instance *instance= (Instance *) arg; + instance->fork_and_monitor(); + return 0; +} + +C_MODE_END + + /* The method starts an instance. @@ -44,6 +57,12 @@ int Instance::start() { pid_t pid; + /* clear crash flag */ + pthread_mutex_lock(&LOCK_instance); + crashed= 0; + pthread_mutex_unlock(&LOCK_instance); + + if (!is_running()) { if ((pid= options.get_pid()) != 0) /* check the pidfile */ @@ -52,17 +71,26 @@ int Instance::start() since IM lacks permmissions or hasn't found the pidifle", options.instance_name); - log_info("starting instance %s", options.instance_name); - switch (pid= fork()) { - case 0: - execv(options.mysqld_path, options.argv); - /* exec never returns */ - exit(1); - case -1: + /* + No need to monitor this thread in the Thread_registry, as all + instances are to be stopped during shutdown. + */ + pthread_t proxy_thd_id; + pthread_attr_t proxy_thd_attr; + int rc; + + pthread_attr_init(&proxy_thd_attr); + pthread_attr_setdetachstate(&proxy_thd_attr, PTHREAD_CREATE_DETACHED); + rc= pthread_create(&proxy_thd_id, &proxy_thd_attr, proxy, + this); + pthread_attr_destroy(&proxy_thd_attr); + if (rc) + { + log_error("Instance::start(): pthread_create(proxy) failed"); return ER_CANNOT_START_INSTANCE; - default: - return 0; } + + return 0; } /* the instance is started already */ @@ -70,9 +98,62 @@ int Instance::start() } +void Instance::fork_and_monitor() +{ + pid_t pid; + log_info("starting instance %s", options.instance_name); + switch (pid= fork()) { + case 0: + execv(options.mysqld_path, options.argv); + /* exec never returns */ + exit(1); + case -1: + log_info("cannot fork() to start instance %s", options.instance_name); + return; + default: + wait(NULL); + /* set instance state to crashed */ + pthread_mutex_lock(&LOCK_instance); + crashed= 1; + pthread_mutex_unlock(&LOCK_instance); + + /* + Wake connection threads waiting for an instance to stop. This + is needed if a user issued command to stop an instance via + mysql connection. This is not the case if Guardian stop the thread. + */ + pthread_cond_signal(&COND_instance_restarted); + /* wake guardian */ + pthread_cond_signal(&instance_map->guardian->COND_guardian); + /* thread exits */ + return; + } + /* we should never end up here */ + DBUG_ASSERT(0); +} + + +Instance::Instance(): crashed(0) +{ + pthread_mutex_init(&LOCK_instance, 0); + pthread_cond_init(&COND_instance_restarted, 0); +} + + Instance::~Instance() { pthread_mutex_destroy(&LOCK_instance); + pthread_cond_destroy(&COND_instance_restarted); +} + + +int Instance::is_crashed() +{ + int val; + pthread_mutex_lock(&LOCK_instance); + val= crashed; + pthread_mutex_unlock(&LOCK_instance); + return val; } @@ -95,20 +176,19 @@ bool Instance::is_running() pthread_mutex_lock(&LOCK_instance); mysql_init(&mysql); - /* try to connect to a server with the fake username/password pair */ + /* try to connect to a server with a fake username/password pair */ if (mysql_real_connect(&mysql, LOCAL_HOST, username, password, NullS, port, socket, 0)) { /* - Very strange. We have successfully connected to the server using - bullshit as username/password. Write a warning to the logfile. + We have successfully connected to the server using fake + username/password. Write a warning to the logfile. */ log_info("The Instance Manager was able to log into you server \ with faked compiled-in password while checking server status. \ Looks like something is wrong."); - mysql_close(&mysql); pthread_mutex_unlock(&LOCK_instance); return_val= TRUE; /* server is alive */ } @@ -151,60 +231,60 @@ int Instance::stop() if (options.shutdown_delay != NULL) waitchild= atoi(options.shutdown_delay); - if ((pid= options.get_pid()) != 0) /* get pid from pidfile */ + kill_instance(SIGTERM); + /* sleep on condition to wait for SIGCHLD */ + + timeout.tv_sec= time(NULL) + waitchild; + timeout.tv_nsec= 0; + if (pthread_mutex_lock(&LOCK_instance)) + goto err; + + while (options.get_pid() != 0) /* while server isn't stopped */ { - /* - If we cannot kill mysqld, then it has propably crashed. - Let us try to remove staled pidfile and return succes as mysqld - is probably stopped - */ - if (kill(pid, SIGTERM)) - { - if (options.unlink_pidfile()) - log_error("cannot remove pidfile for instance %i, this might be \ - since IM lacks permmissions or hasn't found the pidifle", - options.instance_name); + int status; - return 0; - } - - /* sleep on condition to wait for SIGCHLD */ - - timeout.tv_sec= time(NULL) + waitchild; - timeout.tv_nsec= 0; - if (pthread_mutex_lock(&instance_map->pid_cond.LOCK_pid)) - goto err; /* perhaps this should be procecced differently */ - - while (options.get_pid() != 0) /* while server isn't stopped */ - { - int status; - - status= pthread_cond_timedwait(&instance_map->pid_cond.COND_pid, - &instance_map->pid_cond.LOCK_pid, - &timeout); - if (status == ETIMEDOUT) - break; - } - - pthread_mutex_unlock(&instance_map->pid_cond.LOCK_pid); - - if (!kill(pid, SIGKILL)) - { - log_error("The instance %s has been stopped forsibly. Normally \ - it should not happed. Probably the instance has been \ - hanging. You should also check your IM setup", - options.instance_name); - } - - return 0; + status= pthread_cond_timedwait(&COND_instance_restarted, + &LOCK_instance, + &timeout); + if (status == ETIMEDOUT) + break; } + pthread_mutex_unlock(&LOCK_instance); + + kill_instance(SIGKILL); + + return 0; + return ER_INSTANCE_IS_NOT_STARTED; err: return ER_STOP_INSTANCE; } +void Instance::kill_instance(int signum) +{ + pid_t pid; + /* if there are no pid, everything seems to be fine */ + if ((pid= options.get_pid()) != 0) /* get pid from pidfile */ + { + /* + If we cannot kill mysqld, then it has propably crashed. + Let us try to remove staled pidfile and return successfully + as mysqld is probably stopped. + */ + if (!kill(pid, signum)) + options.unlink_pidfile(); + else + if (signum == SIGKILL) /* really killed instance with SIGKILL */ + log_error("The instance %s is being stopped forsibly. Normally \ + it should not happed. Probably the instance has been \ + hanging. You should also check your IM setup", + options.instance_name); + } + return; +} + /* We execute this function to initialize instance parameters. Return value: 0 - ok. 1 - unable to init DYNAMIC_ARRAY. @@ -212,8 +292,6 @@ err: int Instance::init(const char *name_arg) { - pthread_mutex_init(&LOCK_instance, 0); - return options.init(name_arg); } diff --git a/server-tools/instance-manager/instance.h b/server-tools/instance-manager/instance.h index 95852542fb4..fbe24e17554 100644 --- a/server-tools/instance-manager/instance.h +++ b/server-tools/instance-manager/instance.h @@ -30,14 +30,19 @@ class Instance_map; class Instance { public: + Instance(); + ~Instance(); int init(const char *name); int complete_initialization(Instance_map *instance_map_arg); - /* check if the instance is running and set up mysql connection if yes */ bool is_running(); int start(); int stop(); + /* send a signal to the instance */ + void kill_instance(int signo); + int is_crashed(); + void fork_and_monitor(); public: enum { DEFAULT_SHUTDOWN_DELAY= 35 }; @@ -49,7 +54,9 @@ private: double start of the instance. This happens when the instance is starting and we issue the start command once more. */ + int crashed; pthread_mutex_t LOCK_instance; + pthread_cond_t COND_instance_restarted; Instance_map *instance_map; }; diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index ba373087db4..5044bfb0f43 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -120,9 +120,6 @@ Instance_map::Instance_map(const char *default_mysqld_path_arg) int Instance_map::init() { - pthread_mutex_init(&pid_cond.LOCK_pid, 0); - pthread_cond_init(&pid_cond.COND_pid, 0); - if (hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0, get_instance_key, delete_instance, 0)) return 1; @@ -135,8 +132,6 @@ Instance_map::~Instance_map() hash_free(&hash); pthread_mutex_unlock(&LOCK_instance_map); pthread_mutex_destroy(&LOCK_instance_map); - pthread_mutex_destroy(&pid_cond.LOCK_pid); - pthread_cond_destroy(&pid_cond.COND_pid); } @@ -198,21 +193,6 @@ void Instance_map::complete_initialization() } -Instance * -Instance_map::find(uint instance_number) -{ - Instance *instance; - char name[80]; - - snprintf(name, sizeof(name) - 1, "mysqld%i", instance_number); - name[sizeof(name) - 1]= 0; /* safety */ - pthread_mutex_lock(&LOCK_instance_map); - instance= (Instance *) hash_search(&hash, (byte *) name, strlen(name)); - pthread_mutex_unlock(&LOCK_instance_map); - return instance; -} - - /* load options from config files and create appropriate instance structures */ int Instance_map::load() diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h index f2121f7141d..b8b5ad0a890 100644 --- a/server-tools/instance-manager/instance_map.h +++ b/server-tools/instance-manager/instance_map.h @@ -27,12 +27,6 @@ #include "protocol.h" #include "guardian.h" -typedef struct st_instance_cond -{ - pthread_mutex_t LOCK_pid; - pthread_cond_t COND_pid; -} CHILD_COND; - class Instance; extern int load_all_groups(char ***groups, const char *filename); extern void free_groups(char **groups); @@ -83,8 +77,6 @@ public: public: const char *mysqld_path; Guardian_thread *guardian; - /* structure used for syncronization reasons in the stop command */ - CHILD_COND pid_cond; private: enum { START_HASH_SIZE = 16 }; diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc index e7e17691330..ab97c86316b 100644 --- a/server-tools/instance-manager/instance_options.cc +++ b/server-tools/instance-manager/instance_options.cc @@ -27,25 +27,50 @@ #include -/* option_name should be prefixed with "--" */ -int Instance_options::get_default_option(char *result, const char *option_name, - size_t result_len) +/* + Get compiled-in value of default_option + + SYNOPSYS + get_default_option() + result buffer to put found value + result_len buffer size + oprion_name the name of the option, prefixed with "--" + + DESCRIPTION + + Get compile-in value of requested option from server + + RETURN + 0 - ok + 1 - error occured +*/ + +int Instance_options::get_default_option(char *result, size_t result_len, + const char *option_name) { int position= 0; + int rc= 1; char verbose_option[]= " --no-defaults --verbose --help"; - Buffer cmd; - cmd.append(position, mysqld_path, strlen(mysqld_path)); - position+= strlen(mysqld_path); - cmd.append(position, verbose_option, sizeof(verbose_option) - 1); - position+= sizeof(verbose_option) - 1; - cmd.append(position, "\0", 1); - /* get the value from "mysqld --help --verbose" */ - if (parse_output_and_get_value(cmd.buffer, option_name + 2, - result, result_len)) - return 1; + Buffer cmd(strlen(mysqld_path)+sizeof(verbose_option)+1); + if (cmd.get_size()) /* malloc succeeded */ + { + cmd.append(position, mysqld_path, strlen(mysqld_path)); + position+= strlen(mysqld_path); + cmd.append(position, verbose_option, sizeof(verbose_option) - 1); + position+= sizeof(verbose_option) - 1; + cmd.append(position, "\0", 1); - return 0; + if (cmd.is_error()) + goto err; + /* get the value from "mysqld --help --verbose" */ + rc= parse_output_and_get_value(cmd.buffer, option_name + 2, + result, result_len); + } + + return rc; +err: + return 1; } @@ -56,51 +81,33 @@ void Instance_options::get_pid_filename(char *result) if (mysqld_datadir == NULL) { - get_default_option(datadir, "--datadir", MAX_PATH_LEN); + get_default_option(datadir, sizeof(datadir), "--datadir"); } else strxnmov(datadir, MAX_PATH_LEN - 1, strchr(mysqld_datadir, '=') + 1, "/", NullS); - /* well, we should never get it */ - if (mysqld_pid_file != NULL) - pid_file= strchr(pid_file, '=') + 1; - else - DBUG_ASSERT(0); + DBUG_ASSERT(mysqld_pid_file); + pid_file= strchr(pid_file, '=') + 1; /* get the full path to the pidfile */ my_load_path(result, pid_file, datadir); - } int Instance_options::unlink_pidfile() { - char pid_file_path[MAX_PATH_LEN]; - - /* - This works as we know that pid_file_path is of - MAX_PATH_LEN == FN_REFLEN length - */ - get_pid_filename((char *)&pid_file_path); - - return unlink(pid_file_path); + return unlink(pid_file_with_path); } pid_t Instance_options::get_pid() { - char pid_file_path[MAX_PATH_LEN]; - - /* - This works as we know that pid_file_path is of - MAX_PATH_LEN == FN_REFLEN length - */ - get_pid_filename((char *)&pid_file_path); + FILE *pid_file_stream; /* get the pid */ - if (FILE *pid_file_stream= my_fopen(pid_file_path, - O_RDONLY | O_BINARY, MYF(0))) + if (pid_file_stream= my_fopen(pid_file_with_path, + O_RDONLY | O_BINARY, MYF(0))) { pid_t pid; @@ -140,6 +147,8 @@ int Instance_options::complete_initialization(const char *default_path) add_option(pidfilename); } + get_pid_filename(pid_file_with_path); + /* we need to reserve space for the final zero + possible default options */ if (!(argv= (char**) alloc_root(&alloc, (options_array.elements + 1 + MAX_NUMBER_OF_DEFAULT_OPTIONS) * sizeof(char*)))) @@ -244,6 +253,8 @@ int Instance_options::add_to_argv(const char* option) return 0; } + +/* function for debug purposes */ void Instance_options::print_argv() { int i; diff --git a/server-tools/instance-manager/instance_options.h b/server-tools/instance-manager/instance_options.h index 1ed11fc9afa..f6d4a6d93c6 100644 --- a/server-tools/instance-manager/instance_options.h +++ b/server-tools/instance-manager/instance_options.h @@ -39,7 +39,7 @@ public: Instance_options() : mysqld_socket(0), mysqld_datadir(0), mysqld_bind_address(0), mysqld_pid_file(0), mysqld_port(0), mysqld_path(0), nonguarded(0), - filled_default_options(0) + shutdown_delay(0), filled_default_options(0) {} ~Instance_options(); /* fills in argv */ @@ -60,6 +60,7 @@ public: enum { MAX_PATH_LEN= 512 }; enum { MAX_NUMBER_OF_DEFAULT_OPTIONS= 2 }; enum { MEM_ROOT_BLOCK_SIZE= 512 }; + char pid_file_with_path[MAX_PATH_LEN]; char **argv; /* We need the some options, so we store them as a separate pointers */ const char *mysqld_socket; @@ -72,11 +73,12 @@ public: const char *mysqld_path; const char *nonguarded; const char *shutdown_delay; + /* this value is computed and cashed here */ DYNAMIC_ARRAY options_array; private: int add_to_argv(const char *option); - int get_default_option(char *result, const char *option_name, - size_t result_len); + int get_default_option(char *result, size_t result_len, + const char *option_name); private: uint filled_default_options; MEM_ROOT alloc; diff --git a/server-tools/instance-manager/listener.cc b/server-tools/instance-manager/listener.cc index 8266c0dac4c..14970989e92 100644 --- a/server-tools/instance-manager/listener.cc +++ b/server-tools/instance-manager/listener.cc @@ -85,6 +85,8 @@ void Listener_thread::run() thread_registry.register_thread(&thread_info); + my_thread_init(); + /* I. prepare 'listen' sockets */ int ip_socket= socket(AF_INET, SOCK_STREAM, 0); @@ -263,11 +265,13 @@ void Listener_thread::run() unlink(unix_socket_address.sun_path); thread_registry.unregister_thread(&thread_info); + my_thread_end(); return; err: thread_registry.unregister_thread(&thread_info); thread_registry.request_shutdown(); + my_thread_end(); return; } diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index b48c020786f..af8dac47dca 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -90,7 +90,6 @@ void manager(const Options &options) sigemptyset(&mask); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGTERM); - sigaddset(&mask, SIGCHLD); sigaddset(&mask, SIGPIPE); sigaddset(&mask, SIGHUP); /* @@ -128,7 +127,7 @@ void manager(const Options &options) int rc; /* - NOTE: Guardian should be shutdowned first. Only then all other threads + NOTE: Guardian should be shutdown first. Only then all other threads need to be stopped. This should be done, as guardian is responsible for shutting down the instances, and this is a long operation. */ @@ -160,12 +159,8 @@ void manager(const Options &options) more then 10 alarms at the same time. */ init_thr_alarm(10); - /* - Now we can init the list of guarded instances. We have to do it after - alarm structures initialization as we have to use net_* functions while - making the list. And they in their turn need alarms for timeout suppport. - */ - guardian_thread.start(); + /* init list of guarded instances */ + guardian_thread.init(); /* After the list of guarded instances have been initialized, Guardian should start them. @@ -182,18 +177,12 @@ void manager(const Options &options) case THR_SERVER_ALARM: process_alarm(signo); break; - case SIGCHLD: - wait(NULL); - /* wake threads waiting for an instance to shutdown */ - pthread_cond_broadcast(&instance_map.pid_cond.COND_pid); - /* wake guardian */ - pthread_cond_signal(&guardian_thread.COND_guardian); - break; default: - if (!guardian_thread.is_stopped) + { + if (!guardian_thread.is_stopped()) { - guardian_thread.request_stop_instances(); - guardian_thread.shutdown(); + bool stop_instances= true; + guardian_thread.request_shutdown(stop_instances); pthread_cond_signal(&guardian_thread.COND_guardian); } else @@ -201,6 +190,7 @@ void manager(const Options &options) thread_registry.deliver_shutdown(); shutdown_complete= TRUE; } + } break; } } diff --git a/server-tools/instance-manager/options.cc b/server-tools/instance-manager/options.cc index bf6ba1e9163..18d98a3d9a6 100644 --- a/server-tools/instance-manager/options.cc +++ b/server-tools/instance-manager/options.cc @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include "priv.h" @@ -77,6 +79,9 @@ static struct my_option my_long_options[] = (gptr *) &Options::socket_file_name, (gptr *) &Options::socket_file_name, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + { "passwd", 'P', "Prepare entry for passwd file and exit.", 0, 0, 0, + GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }, + { "bind-address", OPT_BIND_ADDRESS, "Bind address to use for connection.", (gptr *) &Options::bind_address, (gptr *) &Options::bind_address, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, @@ -142,6 +147,34 @@ static void usage() my_print_variables(my_long_options); } + +static void passwd() +{ + char user[1024], pw[1024], *p; + char crypted_pw[SCRAMBLED_PASSWORD_CHAR_LENGTH + 1]; + + fprintf(stderr, "Creating record for new user.\n"); + fprintf(stderr, "Enter user name: "); + if (!fgets(user, sizeof(user), stdin)) + { + fprintf(stderr, "Unable to read user.\n"); + return; + } + if ((p= strchr(user, '\n'))) *p= 0; + + fprintf(stderr, "Enter password: "); + if (! fgets(pw, sizeof(pw), stdin)) + { + fprintf(stderr, "Unable to read password.\n"); + return; + } + if ((p= strchr(pw, '\n'))) *p= 0; + + make_scrambled_password(crypted_pw, pw); + printf("%s:%s\n", user, crypted_pw); +} + + C_MODE_START static my_bool @@ -153,7 +186,9 @@ get_one_option(int optid, case 'V': version(); exit(0); - case 'I': + case 'P': + passwd(); + exit(0); case '?': usage(); exit(0); diff --git a/server-tools/instance-manager/parse.cc b/server-tools/instance-manager/parse.cc index d029267f9b8..4c897ddf933 100644 --- a/server-tools/instance-manager/parse.cc +++ b/server-tools/instance-manager/parse.cc @@ -49,29 +49,6 @@ static struct tokens_st tokens[]= { }; -/* - tries to find next word in the text - if found, returns the beginning and puts word length to word_len argument. - if not found returns pointer to first non-space or to '\0', word_len == 0 -*/ - -inline void get_word(const char **text, uint *word_len) -{ - const char *word_end; - - /* skip space */ - while (my_isspace(default_charset_info, **text)) - ++(*text); - - word_end= *text; - - while (my_isalnum(default_charset_info, *word_end)) - ++word_end; - - *word_len= word_end - *text; -} - - /* Returns token no if word corresponds to some token, otherwise returns TOK_NOT_FOUND diff --git a/server-tools/instance-manager/parse.h b/server-tools/instance-manager/parse.h index 236a9bee53a..92519893302 100644 --- a/server-tools/instance-manager/parse.h +++ b/server-tools/instance-manager/parse.h @@ -20,4 +20,34 @@ Command *parse_command(Command_factory *factory, const char *text); +/* define kinds of the word seek method */ +enum { ALPHANUM= 1, NONSPACE }; + +/* + tries to find next word in the text + if found, returns the beginning and puts word length to word_len argument. + if not found returns pointer to first non-space or to '\0', word_len == 0 +*/ + +inline void get_word(const char **text, uint *word_len, + int seek_method= ALPHANUM) +{ + const char *word_end; + + /* skip space */ + while (my_isspace(default_charset_info, **text)) + ++(*text); + + word_end= *text; + + if (seek_method == ALPHANUM) + while (my_isalnum(default_charset_info, *word_end)) + ++word_end; + else + while (!my_isspace(default_charset_info, *word_end)) + ++word_end; + + *word_len= word_end - *text; +} + #endif /* INCLUDES_MYSQL_INSTANCE_MANAGER_PARSE_H */ diff --git a/server-tools/instance-manager/parse_output.cc b/server-tools/instance-manager/parse_output.cc index 2bc51922e69..4276062caf2 100644 --- a/server-tools/instance-manager/parse_output.cc +++ b/server-tools/instance-manager/parse_output.cc @@ -14,43 +14,38 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "parse.h" + #include #include #include #include -/* buf should be of appropriate size. Otherwise the word will be truncated */ -static int get_word(FILE *file, char *buf, size_t size) -{ - int currchar; - currchar= getc(file); +/* + Parse output of the given command - /* skip space */ - while (my_isspace(default_charset_info, (char) currchar) && - currchar != EOF && size > 1) - { - currchar= getc(file); - } + SYNOPSYS + parse_output_and_get_value() - while (!my_isspace(default_charset_info, (char) currchar) && - currchar != EOF && size > 1) - { - *buf++= (char) currchar; - currchar= getc(file); - size--; - } + command the command to execue with popen. + word the word to look for (usually an option name) + result the buffer to store the next word (option value) + result_len self-explanatory - *buf= '\0'; - return 0; -} + DESCRIPTION + Parse output of the "command". Find the "word" and return the next one +*/ int parse_output_and_get_value(const char *command, const char *word, char *result, size_t result_len) { FILE *output; - int wordlen; + uint wordlen; + /* should be enought to store the string from the output */ + enum { MAX_LINE_LEN= 512 }; + char linebuf[MAX_LINE_LEN]; wordlen= strlen(word); @@ -62,19 +57,32 @@ int parse_output_and_get_value(const char *command, const char *word, */ setvbuf(output, NULL, _IOFBF, 0); - get_word(output, result, result_len); - while (strncmp(word, result, wordlen) && *result != '\0') + while (fgets(linebuf, sizeof(linebuf) - 1, output)) { - get_word(output, result, result_len); + uint lineword_len= 0; + char *linep= linebuf; + + linebuf[sizeof(linebuf) - 1]= '\0'; /* safety */ + + /* + Get the word, which might contain non-alphanumeric characters. (Usually + these are '/', '-' and '.' in the path expressions and filenames) + */ + get_word((const char **) &linep, &lineword_len, NONSPACE); + if (!strncmp(word, linep, wordlen) && *result != '\0') + { + /* + If we have found the word, return the next one. This is usually + an option value. + */ + get_word((const char **) &linep, &lineword_len, NONSPACE); + DBUG_ASSERT(result_len > lineword_len); + strncpy(result, linep, lineword_len); + goto pclose; + } } - /* - If we have found the word, return the next one. This is usually - an option value. - */ - if (*result != '\0') - get_word(output, result, result_len); - +pclose: if (pclose(output)) return 1; diff --git a/server-tools/instance-manager/protocol.cc b/server-tools/instance-manager/protocol.cc index 581157ccd72..60cd0e8714f 100644 --- a/server-tools/instance-manager/protocol.cc +++ b/server-tools/instance-manager/protocol.cc @@ -154,7 +154,8 @@ int send_fields(struct st_net *net, LIST *fields) store_to_string(&send_buff, (char *) "", &position); /* table name alias */ store_to_string(&send_buff, field->name, &position); /* column name */ store_to_string(&send_buff, field->name, &position); /* column name alias */ - if (send_buff.reserve(position, 12)) + send_buff.reserve(position, 12); + if (send_buff.is_error()) goto err; send_buff.buffer[position++]= 12; int2store(send_buff.buffer + position, 1); /* charsetnr */ diff --git a/server-tools/instance-manager/user_map.cc b/server-tools/instance-manager/user_map.cc index 21d66912813..7cb2cd67d7f 100644 --- a/server-tools/instance-manager/user_map.cc +++ b/server-tools/instance-manager/user_map.cc @@ -69,7 +69,7 @@ int User::init(const char *line) return 0; err: - log_error("error parsing user and password at line %d", line); + log_error("error parsing user and password at line %s", line); return 1; } From 5bd607785a1f72b826831ab45c99d75b69846531 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 13 Feb 2005 15:13:33 +0300 Subject: [PATCH 15/42] various fixes server-tools/instance-manager/buffer.cc: use my_realloc instead of realloc server-tools/instance-manager/buffer.h: use my_malloc instead of malloc server-tools/instance-manager/commands.cc: No need to send a buffer if there were some error while writing to it server-tools/instance-manager/instance_options.cc: cleanup server-tools/instance-manager/manager.cc: check sigwait return value server-tools/instance-manager/parse_output.cc: fixed a bug, found with valgrind --- server-tools/instance-manager/buffer.cc | 8 ++++---- server-tools/instance-manager/buffer.h | 3 ++- server-tools/instance-manager/commands.cc | 16 ++++++++-------- .../instance-manager/instance_options.cc | 6 ++---- server-tools/instance-manager/manager.cc | 9 ++++++++- server-tools/instance-manager/parse_output.cc | 17 ++++++++++++++--- 6 files changed, 38 insertions(+), 21 deletions(-) diff --git a/server-tools/instance-manager/buffer.cc b/server-tools/instance-manager/buffer.cc index 3a2a818e478..aaed161838d 100644 --- a/server-tools/instance-manager/buffer.cc +++ b/server-tools/instance-manager/buffer.cc @@ -81,10 +81,10 @@ int Buffer::reserve(uint position, uint len_arg) if (position + len_arg>= buffer_size) { - buffer= (char *) realloc(buffer, - min(MAX_BUFFER_SIZE, - max((uint) (buffer_size*1.5), - position + len_arg))); + buffer= (char *) my_realloc(buffer, + min(MAX_BUFFER_SIZE, + max((uint) (buffer_size*1.5), + position + len_arg)), MYF(0)); if (buffer == NULL) goto err; buffer_size= (uint) (buffer_size*1.5); diff --git a/server-tools/instance-manager/buffer.h b/server-tools/instance-manager/buffer.h index 260a9ef92f8..85dd210cac2 100644 --- a/server-tools/instance-manager/buffer.h +++ b/server-tools/instance-manager/buffer.h @@ -17,6 +17,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include +#include #ifdef __GNUC__ #pragma interface @@ -45,7 +46,7 @@ public: /* As append() will invokes realloc() anyway, it's ok if malloc returns 0 */ - if (!(buffer= (char*) malloc(buffer_size))) + if (!(buffer= (char*) my_malloc(buffer_size, MYF(0)))) buffer_size= 0; } diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc index 3f38f21b008..4667e76bfd6 100644 --- a/server-tools/instance-manager/commands.cc +++ b/server-tools/instance-manager/commands.cc @@ -184,8 +184,8 @@ int Show_instance_status::do_command(struct st_net *net, } - if (my_net_write(net, send_buff.buffer, (uint) position) || - send_buff.is_error()) + if (send_buff.is_error() || + my_net_write(net, send_buff.buffer, (uint) position)) goto err; } @@ -271,8 +271,8 @@ int Show_instance_options::do_command(struct st_net *net, store_to_string(&send_buff, (char *) instance->options.mysqld_path, &position); - if (my_net_write(net, send_buff.buffer, (uint) position) || - send_buff.is_error()) + if (send_buff.is_error() || + my_net_write(net, send_buff.buffer, (uint) position)) goto err; } @@ -281,8 +281,8 @@ int Show_instance_options::do_command(struct st_net *net, position= 0; store_to_string(&send_buff, (char *) "nonguarded", &position); store_to_string(&send_buff, "", &position); - if (my_net_write(net, send_buff.buffer, (uint) position) || - send_buff.is_error()) + if (send_buff.is_error() || + my_net_write(net, send_buff.buffer, (uint) position)) goto err; } @@ -299,8 +299,8 @@ int Show_instance_options::do_command(struct st_net *net, store_to_string(&send_buff, option_value + 1, &position); /* join name and the value into the same option again */ *option_value= '='; - if (my_net_write(net, send_buff.buffer, (uint) position) || - send_buff.is_error()) + if (send_buff.is_error() || + my_net_write(net, send_buff.buffer, (uint) position)) goto err; } } diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc index ab97c86316b..7c41a556cc4 100644 --- a/server-tools/instance-manager/instance_options.cc +++ b/server-tools/instance-manager/instance_options.cc @@ -80,9 +80,7 @@ void Instance_options::get_pid_filename(char *result) char datadir[MAX_PATH_LEN]; if (mysqld_datadir == NULL) - { get_default_option(datadir, sizeof(datadir), "--datadir"); - } else strxnmov(datadir, MAX_PATH_LEN - 1, strchr(mysqld_datadir, '=') + 1, "/", NullS); @@ -106,8 +104,8 @@ pid_t Instance_options::get_pid() FILE *pid_file_stream; /* get the pid */ - if (pid_file_stream= my_fopen(pid_file_with_path, - O_RDONLY | O_BINARY, MYF(0))) + if ((pid_file_stream= my_fopen(pid_file_with_path, + O_RDONLY | O_BINARY, MYF(0))) != NULL) { pid_t pid; diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index af8dac47dca..87ad938615f 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -171,7 +171,14 @@ void manager(const Options &options) while (!shutdown_complete) { - sigwait(&mask, &signo); + int status= 0; + + if (status= my_sigwait(&mask, &signo)) + { + log_error("sigwait() failed"); + goto err; + } + switch (signo) { case THR_SERVER_ALARM: diff --git a/server-tools/instance-manager/parse_output.cc b/server-tools/instance-manager/parse_output.cc index 4276062caf2..317970d2047 100644 --- a/server-tools/instance-manager/parse_output.cc +++ b/server-tools/instance-manager/parse_output.cc @@ -36,6 +36,10 @@ DESCRIPTION Parse output of the "command". Find the "word" and return the next one + + RETURN + 0 - ok + 1 - error occured */ int parse_output_and_get_value(const char *command, const char *word, @@ -49,7 +53,8 @@ int parse_output_and_get_value(const char *command, const char *word, wordlen= strlen(word); - output= popen(command, "r"); + if ((output= popen(command, "r")) == NULL) + goto err; /* We want fully buffered stream. We also want system to @@ -69,15 +74,18 @@ int parse_output_and_get_value(const char *command, const char *word, these are '/', '-' and '.' in the path expressions and filenames) */ get_word((const char **) &linep, &lineword_len, NONSPACE); - if (!strncmp(word, linep, wordlen) && *result != '\0') + if (!strncmp(word, linep, wordlen)) { /* If we have found the word, return the next one. This is usually an option value. */ + linep+= lineword_len; /* swallow the previous one */ get_word((const char **) &linep, &lineword_len, NONSPACE); - DBUG_ASSERT(result_len > lineword_len); + if (result_len <= lineword_len) + goto err; strncpy(result, linep, lineword_len); + result[lineword_len]= '\0'; goto pclose; } } @@ -87,4 +95,7 @@ pclose: return 1; return 0; + +err: + return 1; } From deba12cd34ad4378465d5691de1d50fe1233121a Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 15 Feb 2005 04:38:33 +0300 Subject: [PATCH 16/42] some more cleanups and fixes server-tools/instance-manager/commands.cc: typo fixed server-tools/instance-manager/instance.cc: moved options.complete_initialization to the instance::complete_initialization server-tools/instance-manager/instance.h: moved options.complete_initialization to the Instance::complete_initialization server-tools/instance-manager/instance_map.cc: moved options.complete_initialization to the Instance::complete_initialization, added code to create default instance if none is given iin config. file server-tools/instance-manager/instance_map.h: complete_initialization now returns an error in case of a problem server-tools/instance-manager/instance_options.cc: some error handling added server-tools/instance-manager/instance_options.h: error handling added server-tools/instance-manager/manager.cc: error handling added server-tools/instance-manager/mysqlmanager.cc: stop mysqlmanager if options were not loaded correctly server-tools/instance-manager/options.cc: return-value added server-tools/instance-manager/options.h: return-value added to the OPtions::load() server-tools/instance-manager/parse_output.cc: no need to examine mysqld --help -v termination status --- server-tools/instance-manager/commands.cc | 2 +- server-tools/instance-manager/instance.cc | 5 +- server-tools/instance-manager/instance.h | 3 +- server-tools/instance-manager/instance_map.cc | 47 ++++++++++++++----- server-tools/instance-manager/instance_map.h | 2 +- .../instance-manager/instance_options.cc | 12 +++-- .../instance-manager/instance_options.h | 2 +- server-tools/instance-manager/manager.cc | 16 +++++-- server-tools/instance-manager/mysqlmanager.cc | 6 ++- server-tools/instance-manager/options.cc | 8 ++-- server-tools/instance-manager/options.h | 2 +- server-tools/instance-manager/parse_output.cc | 4 +- 12 files changed, 77 insertions(+), 32 deletions(-) diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc index 4667e76bfd6..4e88f358148 100644 --- a/server-tools/instance-manager/commands.cc +++ b/server-tools/instance-manager/commands.cc @@ -276,7 +276,7 @@ int Show_instance_options::do_command(struct st_net *net, goto err; } - if (instance->options.nonguarded == NULL) + if (instance->options.nonguarded != NULL) { position= 0; store_to_string(&send_buff, (char *) "nonguarded", &position); diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc index e654612de3e..f2140ee7200 100644 --- a/server-tools/instance-manager/instance.cc +++ b/server-tools/instance-manager/instance.cc @@ -296,8 +296,9 @@ int Instance::init(const char *name_arg) } -int Instance::complete_initialization(Instance_map *instance_map_arg) +int Instance::complete_initialization(Instance_map *instance_map_arg, + const char *mysqld_path) { instance_map= instance_map_arg; - return 0; + return options.complete_initialization(mysqld_path); } diff --git a/server-tools/instance-manager/instance.h b/server-tools/instance-manager/instance.h index fbe24e17554..d2a3eb98410 100644 --- a/server-tools/instance-manager/instance.h +++ b/server-tools/instance-manager/instance.h @@ -34,7 +34,8 @@ public: ~Instance(); int init(const char *name); - int complete_initialization(Instance_map *instance_map_arg); + int complete_initialization(Instance_map *instance_map_arg, + const char *mysqld_path); bool is_running(); int start(); diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index 5044bfb0f43..191677889e9 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -74,7 +74,7 @@ static void delete_instance(void *u) 1 - error occured */ -static int process_option(void * ctx, const char *group, const char *option) +static int process_option(void *ctx, const char *group, const char *option) { Instance_map *map= NULL; Instance *instance= NULL; @@ -178,18 +178,41 @@ Instance_map::find(const char *name, uint name_len) } -void Instance_map::complete_initialization() +int Instance_map::complete_initialization() { Instance *instance; uint i= 0; - while (i < hash.records) + if (hash.records == 0) /* no instances found */ { - instance= (Instance *) hash_element(&hash, i); - instance->complete_initialization(this); - instance->options.complete_initialization(mysqld_path); - i++; + if ((instance= new Instance) == 0) + goto err; + + if (instance->init("mysqld") || add_instance(instance)) + goto err_instance; + + /* + After an instance have been added to the instance_map, + hash_free should handle it's deletion. + */ + if (instance->complete_initialization(this, mysqld_path)) + goto err; } + else + while (i < hash.records) + { + instance= (Instance *) hash_element(&hash, i); + if (instance->complete_initialization(this, mysqld_path)) + goto err; + i++; + } + + return 0; +err: + return 1; +err_instance: + delete instance; + return 1; } @@ -197,13 +220,11 @@ void Instance_map::complete_initialization() int Instance_map::load() { - int error; + if (process_default_option_files("my", process_option, (void *) this) || + complete_initialization()) + return 1; - error= process_default_option_files("my", process_option, (void *) this); - - complete_initialization(); - - return error; + return 0; } diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h index b8b5ad0a890..60fde43ce12 100644 --- a/server-tools/instance-manager/instance_map.h +++ b/server-tools/instance-manager/instance_map.h @@ -72,7 +72,7 @@ public: /* adds instance to internal hash */ int add_instance(Instance *instance); /* inits instances argv's after all options have been loaded */ - void complete_initialization(); + int complete_initialization(); public: const char *mysqld_path; diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc index 7c41a556cc4..6b795934d08 100644 --- a/server-tools/instance-manager/instance_options.cc +++ b/server-tools/instance-manager/instance_options.cc @@ -74,13 +74,17 @@ err: } -void Instance_options::get_pid_filename(char *result) +int Instance_options::get_pid_filename(char *result) { const char *pid_file= mysqld_pid_file; char datadir[MAX_PATH_LEN]; if (mysqld_datadir == NULL) - get_default_option(datadir, sizeof(datadir), "--datadir"); + { + /* we might get an error here if we have wrong path to the mysqld binary */ + if (get_default_option(datadir, sizeof(datadir), "--datadir")) + return 1; + } else strxnmov(datadir, MAX_PATH_LEN - 1, strchr(mysqld_datadir, '=') + 1, "/", NullS); @@ -90,6 +94,7 @@ void Instance_options::get_pid_filename(char *result) /* get the full path to the pidfile */ my_load_path(result, pid_file, datadir); + return 0; } @@ -145,7 +150,8 @@ int Instance_options::complete_initialization(const char *default_path) add_option(pidfilename); } - get_pid_filename(pid_file_with_path); + if (get_pid_filename(pid_file_with_path)) + goto err; /* we need to reserve space for the final zero + possible default options */ if (!(argv= (char**) alloc_root(&alloc, (options_array.elements + 1 diff --git a/server-tools/instance-manager/instance_options.h b/server-tools/instance-manager/instance_options.h index f6d4a6d93c6..888658969f2 100644 --- a/server-tools/instance-manager/instance_options.h +++ b/server-tools/instance-manager/instance_options.h @@ -48,7 +48,7 @@ public: int add_option(const char* option); int init(const char *instance_name_arg); pid_t get_pid(); - void get_pid_filename(char *result); + int get_pid_filename(char *result); int unlink_pidfile(); void print_argv(); diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index 87ad938615f..cf7820d0335 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -77,8 +77,18 @@ void manager(const Options &options) instance_map.guardian= &guardian_thread; - if (instance_map.init() || user_map.init() || instance_map.load() || - user_map.load(options.password_file_name)) + if (instance_map.init() || user_map.init()) + return; + + if (instance_map.load()) + { + log_error("Cannot init instances repository. This might be caused by " + "the wrong config file options. For instance, missing mysqld " + "binary. Aborting."); + return; + } + + if (user_map.load(options.password_file_name)) return; /* write pid file */ @@ -173,7 +183,7 @@ void manager(const Options &options) { int status= 0; - if (status= my_sigwait(&mask, &signo)) + if ((status= my_sigwait(&mask, &signo)) != 0) { log_error("sigwait() failed"); goto err; diff --git a/server-tools/instance-manager/mysqlmanager.cc b/server-tools/instance-manager/mysqlmanager.cc index e37dab911c1..e1d267b492e 100644 --- a/server-tools/instance-manager/mysqlmanager.cc +++ b/server-tools/instance-manager/mysqlmanager.cc @@ -74,7 +74,8 @@ int main(int argc, char *argv[]) Options options; struct passwd *user_info; - options.load(argc, argv); + if (options.load(argc, argv)) + goto err; if ((user_info= check_user(options.user))) { @@ -96,6 +97,9 @@ int main(int argc, char *argv[]) options.cleanup(); my_end(0); return 0; +err: + my_end(0); + return 1; } /******************* Auxilary functions implementation **********************/ diff --git a/server-tools/instance-manager/options.cc b/server-tools/instance-manager/options.cc index 18d98a3d9a6..b136ae3c386 100644 --- a/server-tools/instance-manager/options.cc +++ b/server-tools/instance-manager/options.cc @@ -207,14 +207,16 @@ C_MODE_END May not return. */ -void Options::load(int argc, char **argv) +int Options::load(int argc, char **argv) { + int rc; /* config-file options are prepended to command-line ones */ load_defaults("my", default_groups, &argc, &argv); - if (int rc= handle_options(&argc, &argv, my_long_options, get_one_option)) - exit(rc); + if (rc= handle_options(&argc, &argv, my_long_options, get_one_option)) + return rc; Options::saved_argv= argv; + return 0; } void Options::cleanup() diff --git a/server-tools/instance-manager/options.h b/server-tools/instance-manager/options.h index bb6da51d1ca..1b8570dd7ba 100644 --- a/server-tools/instance-manager/options.h +++ b/server-tools/instance-manager/options.h @@ -41,7 +41,7 @@ struct Options static char **saved_argv; - static void load(int argc, char **argv); + static int load(int argc, char **argv); void cleanup(); }; diff --git a/server-tools/instance-manager/parse_output.cc b/server-tools/instance-manager/parse_output.cc index 317970d2047..58e3fbf9fbd 100644 --- a/server-tools/instance-manager/parse_output.cc +++ b/server-tools/instance-manager/parse_output.cc @@ -91,8 +91,8 @@ int parse_output_and_get_value(const char *command, const char *word, } pclose: - if (pclose(output)) - return 1; + /* we are not interested in the termination status */ + pclose(output); return 0; From e2d78b85bf004c8c0ec25165d0c9980a3253914a Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 18 Feb 2005 14:58:30 +0300 Subject: [PATCH 17/42] made IM to work with --defaults-file smoothly BitKeeper/deleted/.del-my.cnf~9322f78f12eb2f3c: Delete: support-files/my.cnf include/my_sys.h: fixed prototype to reflect changes in default.c mysys/default.c: use my_search_option_files instead of process_default_option_files. This is used from the IM. server-tools/instance-manager/instance.cc: added parameter to complete_initialization(). this ine marks whether we are creating the only instance (for instance, when no config file given) server-tools/instance-manager/instance.h: prototype changed server-tools/instance-manager/instance_map.cc: Now call my_search_option_files to work smootly with --defaults-file server-tools/instance-manager/instance_map.h: added first_option member. This is set of mysqlmanager was started with --defaults-file or with --extra-defaults-file to be passed to the my_search_option_files server-tools/instance-manager/instance_options.cc: if we have only one instance, name the pidfile `hostname`.pid for compatibility reasons. server-tools/instance-manager/instance_options.h: header fixed according to .cc server-tools/instance-manager/manager.cc: prototype fixed server-tools/instance-manager/options.cc: save --defaults-file of --defaults-extra-file to pass it to my_search_option_files server-tools/instance-manager/options.h: added an optin to save --default-file options support-files/Makefile.am: revert changes --- include/my_sys.h | 8 ++-- mysys/default.c | 42 ++----------------- server-tools/instance-manager/instance.cc | 5 ++- server-tools/instance-manager/instance.h | 2 +- server-tools/instance-manager/instance_map.cc | 31 +++++++++++--- server-tools/instance-manager/instance_map.h | 4 +- .../instance-manager/instance_options.cc | 19 +++++++-- .../instance-manager/instance_options.h | 2 +- server-tools/instance-manager/manager.cc | 2 +- server-tools/instance-manager/options.cc | 11 ++++- server-tools/instance-manager/options.h | 2 + support-files/Makefile.am | 1 - support-files/my.cnf | 2 - 13 files changed, 70 insertions(+), 61 deletions(-) delete mode 100644 support-files/my.cnf diff --git a/include/my_sys.h b/include/my_sys.h index 72d4bec74b1..7d3b402bc6f 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -531,7 +531,7 @@ typedef uint32 ha_checksum; /* Define the type of function to be passed to process_default_option_files */ typedef int (*Process_option_func)(void *ctx, const char *group_name, - const char *option); + const char *option); #include @@ -776,9 +776,9 @@ extern void get_defaults_files(int argc, char **argv, char **defaults, char **extra_defaults); extern int load_defaults(const char *conf_file, const char **groups, int *argc, char ***argv); -extern int process_default_option_files(const char *conf_file, - Process_option_func func, - void *func_ctx); +extern int my_search_option_files(const char *conf_file, int *argc, + char ***argv, uint *args_used, + Process_option_func func, void *func_ctx); extern void free_defaults(char **argv); extern void print_defaults(const char *conf_file, const char **groups); extern my_bool my_compress(byte *, ulong *, ulong *); diff --git a/mysys/default.c b/mysys/default.c index 73dca3b6c2f..1c06feec634 100644 --- a/mysys/default.c +++ b/mysys/default.c @@ -83,7 +83,7 @@ static char *remove_end_comment(char *ptr); Process config files in default directories. SYNOPSIS - search_files() + my_search_option_files() conf_file Basename for configuration file to search for. If this is a path, then only this file is read. argc Pointer to argc of original program @@ -103,13 +103,13 @@ static char *remove_end_comment(char *ptr); 1 given cinf_file doesn't exist */ -static int search_files(const char *conf_file, int *argc, char ***argv, +int my_search_option_files(const char *conf_file, int *argc, char ***argv, uint *args_used, Process_option_func func, void *func_ctx) { const char **dirs, *forced_default_file; int error= 0; - DBUG_ENTER("search_files"); + DBUG_ENTER("my_search_option_files"); /* Check if we want to force the use a specific default file */ get_defaults_files(*argc, *argv, @@ -180,40 +180,6 @@ err: } -/* - Simplified version of search_files (no argv, argc to process). - - SYNOPSIS - process_default_option_files() - conf_file Basename for configuration file to search for. - If this is a path, then only this file is read. - func Pointer to the function to process options - func_ctx It's context. Usually it is the structure to - store additional options. - - DESCRIPTION - - Often we want only to get options from default config files. In this case we - don't want to provide any argc and argv parameters. This function is a - simplified variant of search_files which allows us to forget about - argc, argv. - - RETURN - 0 ok - 1 given cinf_file doesn't exist -*/ - -int process_default_option_files(const char *conf_file, - Process_option_func func, void *func_ctx) -{ - int argc= 1; - /* this is a dummy variable for search_files() */ - uint args_used; - - return search_files(conf_file, &argc, NULL, &args_used, func, func_ctx); -} - - /* The option handler for load_defaults. @@ -363,7 +329,7 @@ int load_defaults(const char *conf_file, const char **groups, ctx.args= &args; ctx.group= &group; - error= search_files(conf_file, argc, argv, &args_used, + error= my_search_option_files(conf_file, argc, argv, &args_used, handle_default_option, (void *) &ctx); /* Here error contains <> 0 only if we have a fully specified conf_file diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc index f2140ee7200..165b06644f4 100644 --- a/server-tools/instance-manager/instance.cc +++ b/server-tools/instance-manager/instance.cc @@ -297,8 +297,9 @@ int Instance::init(const char *name_arg) int Instance::complete_initialization(Instance_map *instance_map_arg, - const char *mysqld_path) + const char *mysqld_path, + int only_instance) { instance_map= instance_map_arg; - return options.complete_initialization(mysqld_path); + return options.complete_initialization(mysqld_path, only_instance); } diff --git a/server-tools/instance-manager/instance.h b/server-tools/instance-manager/instance.h index d2a3eb98410..9f3334ba464 100644 --- a/server-tools/instance-manager/instance.h +++ b/server-tools/instance-manager/instance.h @@ -35,7 +35,7 @@ public: ~Instance(); int init(const char *name); int complete_initialization(Instance_map *instance_map_arg, - const char *mysqld_path); + const char *mysqld_path, int only_instance= 0); bool is_running(); int start(); diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index 191677889e9..919f9c955a4 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -110,10 +110,10 @@ err_new_instance: C_MODE_END -Instance_map::Instance_map(const char *default_mysqld_path_arg) +Instance_map::Instance_map(const char *default_mysqld_path_arg, + const char *first_option_arg): +mysqld_path(default_mysqld_path_arg), first_option(first_option_arg) { - mysqld_path= default_mysqld_path_arg; - pthread_mutex_init(&LOCK_instance_map, 0); } @@ -193,9 +193,10 @@ int Instance_map::complete_initialization() /* After an instance have been added to the instance_map, - hash_free should handle it's deletion. + hash_free should handle it's deletion => goto err, not + err_instance. */ - if (instance->complete_initialization(this, mysqld_path)) + if (instance->complete_initialization(this, mysqld_path, 1)) goto err; } else @@ -220,7 +221,25 @@ err_instance: int Instance_map::load() { - if (process_default_option_files("my", process_option, (void *) this) || + int argc= 1; + /* this is a dummy variable for search_option_files() */ + uint args_used= 0; + const char *argv_options[3]; + char **argv= (char **) &argv_options; + + /* the name of the program may be orbitrary here in fact */ + argv_options[0]= "mysqlmanager"; + if (first_option != NULL) + { + argc= 2; + argv_options[1]= first_option; + argv_options[2]= '\0'; + } + else + argv_options[1]= '\0'; + + if (my_search_option_files("my", &argc, (char ***) &argv, &args_used, + process_option, (void *) this) || complete_initialization()) return 1; diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h index 60fde43ce12..98d98b5c516 100644 --- a/server-tools/instance-manager/instance_map.h +++ b/server-tools/instance-manager/instance_map.h @@ -64,7 +64,8 @@ public: int unlock(); int init(); - Instance_map(const char *default_mysqld_path_arg); + Instance_map(const char *default_mysqld_path_arg, + const char *first_option_arg); ~Instance_map(); /* loads options from config files */ @@ -79,6 +80,7 @@ public: Guardian_thread *guardian; private: + const char *first_option; enum { START_HASH_SIZE = 16 }; pthread_mutex_t LOCK_instance_map; HASH hash; diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc index 6b795934d08..4242b8b6454 100644 --- a/server-tools/instance-manager/instance_options.cc +++ b/server-tools/instance-manager/instance_options.cc @@ -123,7 +123,8 @@ pid_t Instance_options::get_pid() } -int Instance_options::complete_initialization(const char *default_path) +int Instance_options::complete_initialization(const char *default_path, + int only_instance) { const char *tmp; @@ -140,11 +141,23 @@ int Instance_options::complete_initialization(const char *default_path) { char pidfilename[MAX_PATH_LEN]; char hostname[MAX_PATH_LEN]; + + /* + If we created only one istance [mysqld], because no config. files were + found, we would like to model mysqld pid file values. + */ if (!gethostname(hostname, sizeof(hostname) - 1)) - strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", hostname, "-", - instance_name, ".pid", NullS); + (only_instance == 0) ? + strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", instance_name, "-", + hostname, ".pid", NullS): + strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", hostname, + ".pid", NullS); + else + (only_instance == 0) ? strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", instance_name, + ".pid", NullS): + strxnmov(pidfilename, MAX_PATH_LEN - 1, "--pid-file=", "mysql", ".pid", NullS); add_option(pidfilename); diff --git a/server-tools/instance-manager/instance_options.h b/server-tools/instance-manager/instance_options.h index 888658969f2..5e91ee80f00 100644 --- a/server-tools/instance-manager/instance_options.h +++ b/server-tools/instance-manager/instance_options.h @@ -43,7 +43,7 @@ public: {} ~Instance_options(); /* fills in argv */ - int complete_initialization(const char *default_path); + int complete_initialization(const char *default_path, int only_instance); int add_option(const char* option); int init(const char *instance_name_arg); diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index cf7820d0335..89d49ba9219 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -67,7 +67,7 @@ void manager(const Options &options) */ User_map user_map; - Instance_map instance_map(options.default_mysqld_path); + Instance_map instance_map(options.default_mysqld_path, options.first_option); Guardian_thread guardian_thread(thread_registry, &instance_map, options.monitoring_interval); diff --git a/server-tools/instance-manager/options.cc b/server-tools/instance-manager/options.cc index b136ae3c386..4a9745451a1 100644 --- a/server-tools/instance-manager/options.cc +++ b/server-tools/instance-manager/options.cc @@ -37,6 +37,7 @@ const char *Options::pid_file_name= QUOTE(DEFAULT_PID_FILE_NAME); const char *Options::socket_file_name= QUOTE(DEFAULT_SOCKET_FILE_NAME); const char *Options::password_file_name= QUOTE(DEFAULT_PASSWORD_FILE_NAME); const char *Options::default_mysqld_path= QUOTE(DEFAULT_MYSQLD_PATH); +const char *Options::first_option= 0; /* No default value */ const char *Options::bind_address= 0; /* No default value */ const char *Options::user= 0; /* No default value */ uint Options::monitoring_interval= DEFAULT_MONITORING_INTERVAL; @@ -210,10 +211,18 @@ C_MODE_END int Options::load(int argc, char **argv) { int rc; + + if (argc >= 2) + { + if (is_prefix(argv[1],"--defaults-file=") || + is_prefix(argv[1],"--defaults-extra-file=")) + Options::first_option= argv[1]; + } + /* config-file options are prepended to command-line ones */ load_defaults("my", default_groups, &argc, &argv); - if (rc= handle_options(&argc, &argv, my_long_options, get_one_option)) + if ((rc= handle_options(&argc, &argv, my_long_options, get_one_option)) != 0) return rc; Options::saved_argv= argv; return 0; diff --git a/server-tools/instance-manager/options.h b/server-tools/instance-manager/options.h index 1b8570dd7ba..3df259864be 100644 --- a/server-tools/instance-manager/options.h +++ b/server-tools/instance-manager/options.h @@ -35,6 +35,8 @@ struct Options static const char *password_file_name; static const char *default_mysqld_path; static const char *user; + /* the option which should be passed to process_default_option_files */ + static const char *first_option; static uint monitoring_interval; static uint port_number; static const char *bind_address; diff --git a/support-files/Makefile.am b/support-files/Makefile.am index 4a9a9c60f30..0a6077f0efc 100644 --- a/support-files/Makefile.am +++ b/support-files/Makefile.am @@ -19,7 +19,6 @@ EXTRA_DIST = mysql.spec.sh \ my-small.cnf.sh \ - my.cnf \ my-medium.cnf.sh \ my-large.cnf.sh \ my-huge.cnf.sh \ diff --git a/support-files/my.cnf b/support-files/my.cnf deleted file mode 100644 index 062c78be46a..00000000000 --- a/support-files/my.cnf +++ /dev/null @@ -1,2 +0,0 @@ -[mysqld] -port=3307 From 97f26ff5f686a1ca2e97009b66ab40a4d29528e6 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 18 Feb 2005 15:23:55 +0300 Subject: [PATCH 18/42] new variant of passwd() by S. Vojtovich server-tools/instance-manager/Makefile.am: link get_password() to the IM server-tools/instance-manager/options.cc: make passwd() function to read password in mysqladmin way --- server-tools/instance-manager/Makefile.am | 1 + server-tools/instance-manager/options.cc | 18 +++++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index aab7db968fd..86effe3b9f0 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -36,6 +36,7 @@ liboptions_a_CPPFLAGS= $(CPPFLAGS) \ -DPROTOCOL_VERSION=@PROTOCOL_VERSION@ liboptions_a_SOURCES= options.h options.cc priv.h priv.cc +liboptions_a_LIBADD= $(top_builddir)/libmysql/get_password.$(OBJEXT) # MySQL sometimes uses symlinks to reuse code # All symlinked files are grouped in libnet.a diff --git a/server-tools/instance-manager/options.cc b/server-tools/instance-manager/options.cc index 4a9745451a1..9e4205b1407 100644 --- a/server-tools/instance-manager/options.cc +++ b/server-tools/instance-manager/options.cc @@ -151,27 +151,31 @@ static void usage() static void passwd() { - char user[1024], pw[1024], *p; + char user[1024], *p; + const char *pw1, *pw2; + char pw1msg[]= "Enter password: "; + char pw2msg[]= "Re-type password: "; char crypted_pw[SCRAMBLED_PASSWORD_CHAR_LENGTH + 1]; fprintf(stderr, "Creating record for new user.\n"); fprintf(stderr, "Enter user name: "); - if (!fgets(user, sizeof(user), stdin)) + if (! fgets(user, sizeof(user), stdin)) { fprintf(stderr, "Unable to read user.\n"); return; } if ((p= strchr(user, '\n'))) *p= 0; - fprintf(stderr, "Enter password: "); - if (! fgets(pw, sizeof(pw), stdin)) + pw1= get_tty_password(pw1msg); + pw2= get_tty_password(pw2msg); + + if (strcmp(pw1, pw2)) { - fprintf(stderr, "Unable to read password.\n"); + fprintf(stderr, "Sorry, passwords do not match.\n"); return; } - if ((p= strchr(pw, '\n'))) *p= 0; - make_scrambled_password(crypted_pw, pw); + make_scrambled_password(crypted_pw, pw1); printf("%s:%s\n", user, crypted_pw); } From 6de9347515d12423ede44ccaafede98b250f542a Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 19 Feb 2005 02:25:40 +0300 Subject: [PATCH 19/42] revert my changes in the spec.file support-files/mysql.spec.sh: revert changes --- support-files/mysql.spec.sh | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/support-files/mysql.spec.sh b/support-files/mysql.spec.sh index a4cd156a0a4..a7ee5fa8a07 100644 --- a/support-files/mysql.spec.sh +++ b/support-files/mysql.spec.sh @@ -417,9 +417,8 @@ install -s -m755 $MBD/sql/mysqld-max $RBR%{_sbindir}/mysqld-max install -m644 $MBD/sql/mysqld-max.sym $RBR%{_libdir}/mysql/mysqld-max.sym install -m644 $MBD/sql/mysqld.sym $RBR%{_libdir}/mysql/mysqld.sym -# Install logrotate, autostart and config file +# Install logrotate and autostart install -m644 $MBD/support-files/mysql-log-rotate $RBR%{_sysconfdir}/logrotate.d/mysql -install -m644 $MBD/support-files/my.cnf $RBR%{_sysconfdir}/my.cnf install -m755 $MBD/support-files/mysql.server $RBR%{_sysconfdir}/init.d/mysql # Create a symlink "rcmysql", pointing to the init.script. SuSE users @@ -430,6 +429,10 @@ ln -s %{_sysconfdir}/init.d/mysql $RPM_BUILD_ROOT%{_sbindir}/rcmysql # (safe_mysqld will be gone in MySQL 4.1) ln -sf ./mysqld_safe $RBR%{_bindir}/safe_mysqld +# Touch the place where the my.cnf config file might be located +# Just to make sure it's in the file list and marked as a config file +touch $RBR%{_sysconfdir}/my.cnf + %pre server # Shut down a previously installed server first if test -x %{_sysconfdir}/init.d/mysql @@ -547,6 +550,8 @@ fi %doc %attr(644, root, man) %{_mandir}/man1/perror.1* %doc %attr(644, root, man) %{_mandir}/man1/replace.1* +%ghost %config(noreplace,missingok) %{_sysconfdir}/my.cnf + %attr(755, root, root) %{_bindir}/my_print_defaults %attr(755, root, root) %{_bindir}/myisamchk %attr(755, root, root) %{_bindir}/myisam_ftdump @@ -574,12 +579,10 @@ fi %attr(755, root, root) %{_bindir}/safe_mysqld %attr(755, root, root) %{_sbindir}/mysqld -%attr(755, root, root) %{_sbindir}/mysqlmanager %attr(755, root, root) %{_sbindir}/rcmysql %attr(644, root, root) %{_libdir}/mysql/mysqld.sym %attr(644, root, root) %config(noreplace,missingok) %{_sysconfdir}/logrotate.d/mysql -%attr(644, root, root) %config(noreplace,missingok) %{_sysconfdir}/my.cnf %attr(755, root, root) %{_sysconfdir}/init.d/mysql %attr(755, root, root) %{_datadir}/mysql/ From d9f6581f259d859c3b87cd1b93481b10a2c47011 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 22 Feb 2005 21:05:17 +0200 Subject: [PATCH 20/42] row0sel.c: Fix bug #8677: if one used LOCK TABLES, created an InnoDB temp table, and did a multi-table update where a MyISAM table was the update table and the temp table was a read table, then InnoDB aserted in row0sel.c because n_mysql_tables_in_use was 0. Also, we remove the assertion altogether and just print an error to the .err log if this important consistency check fails. Then it is up to the user to read the .err log and notice the problem if there still are errors in MySQL's table locking. innobase/row/row0sel.c: Fix bug #8677: if one used LOCK TABLES, created an InnoDB temp table, and did a multi-table update where a MyISAM table was the update table and the temp table was a read table, then InnoDB aserted in row0sel.c because n_mysql_tables_in_use was 0. Also, we remove the assertion altogether and just print an error to the .err log if this important consistency check fails. Then it is up to the user to read the .err log and notice the problem if there still are errors in MySQL's table locking. --- innobase/row/row0sel.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c index 52228caccb0..2de02081176 100644 --- a/innobase/row/row0sel.c +++ b/innobase/row/row0sel.c @@ -2912,14 +2912,19 @@ row_search_for_mysql( ut_error; } - if (trx->n_mysql_tables_in_use == 0) { + if (trx->n_mysql_tables_in_use == 0 + && prebuilt->select_lock_type == LOCK_NONE) { + /* Note that if MySQL uses an InnoDB temp table that it + created inside LOCK TABLES, then n_mysql_tables_in_use can + be zero; in that case select_lock_type is set to LOCK_X in + ::start_stmt. */ + fputs( "InnoDB: Error: MySQL is trying to perform a SELECT\n" "InnoDB: but it has not locked any tables in ::external_lock()!\n", stderr); trx_print(stderr, trx); fputc('\n', stderr); - ut_a(0); } /* fprintf(stderr, "Match mode %lu\n search tuple ", (ulong) match_mode); From 3c5b16eebed40cf72d9deaa800229266bdf75f9a Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 25 Feb 2005 18:12:53 +0400 Subject: [PATCH 21/42] mysqldump.c: Bug#7997 Add implicit --skip-set-charset when mysqldump from 4.0 server w/ 4.1 client client/mysqldump.c: Bug#7997 Add implicit --skip-set-charset when mysqldump from 4.0 server w/ 4.1 client --- client/mysqldump.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/mysqldump.c b/client/mysqldump.c index c2c44024cf4..2c0bdf9a7a9 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -861,6 +861,11 @@ static int dbConnect(char *host, char *user,char *passwd) DBerror(&mysql_connection, "when trying to connect"); return 1; } + /* + Don't dump SET NAMES with a pre-4.1 server (bug#7997). + */ + if (mysql_get_server_version(&mysql_connection) < 40100) + opt_set_charset= 0; /* As we're going to set SQL_MODE, it would be lost on reconnect, so we cannot reconnect. From 46a8c5221750f4f0b5baf870d4f131d8f3de2821 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 25 Feb 2005 22:16:14 +0200 Subject: [PATCH 22/42] fil0fil.c: Check if node == NULL, which means we are outside tablespace bounds, and print a big fatal error message innobase/fil/fil0fil.c: Check if node == NULL, which means we are outside tablespace bounds, and print a big fatal error message --- innobase/fil/fil0fil.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/innobase/fil/fil0fil.c b/innobase/fil/fil0fil.c index a8da074cab9..16ae8e6e228 100644 --- a/innobase/fil/fil0fil.c +++ b/innobase/fil/fil0fil.c @@ -3800,13 +3800,6 @@ fil_io( node = UT_LIST_GET_FIRST(space->chain); for (;;) { - if (space->id != 0 && node->size == 0) { - /* We do not know the size of a single-table tablespace - before we open the file */ - - break; - } - if (node == NULL) { fprintf(stderr, "InnoDB: Error: trying to access page number %lu in space %lu,\n" @@ -3820,6 +3813,13 @@ fil_io( ut_error; } + if (space->id != 0 && node->size == 0) { + /* We do not know the size of a single-table tablespace + before we open the file */ + + break; + } + if (node->size > block_offset) { /* Found! */ break; From 1a72e219f206fbf20691baaf2d1fbdb2dd73e13c Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 26 Feb 2005 17:15:04 +0300 Subject: [PATCH 23/42] Fix for BUG#8560: Set max_sort_char for any 8-bit charset with binary collation. max_sort_char is needed by my_like_range_simple to produce upper bound constants for LIKE "str_%" and similar expressions. mysql-test/r/ctype_cp1251.result: Test for BUG#8560 mysql-test/t/ctype_cp1251.test: Test for BUG#8560 --- mysql-test/r/ctype_cp1251.result | 16 ++++++++++++++++ mysql-test/t/ctype_cp1251.test | 14 ++++++++++++++ strings/ctype-bin.c | 9 ++++++++- 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/ctype_cp1251.result b/mysql-test/r/ctype_cp1251.result index 3793e962d40..647f8c6236c 100644 --- a/mysql-test/r/ctype_cp1251.result +++ b/mysql-test/r/ctype_cp1251.result @@ -54,3 +54,19 @@ select collation(a), collation(b), collation(binary 'ccc') from t1 limit 1; collation(a) collation(b) collation(binary 'ccc') cp1251_bin binary binary drop table t1; +create table t1 ( +a varchar(16) character set cp1251 collate cp1251_bin not null, +b int(10) default null, +primary key(a) +) charset=cp1251; +insert into t1 (a) values ('air'), +('we'),('g'),('we_toshko'), ('s0urce'),('we_ivo'),('we_iliyan'), +('we_martin'),('vw_grado'),('vw_vasko'),('tn_vili'),('tn_kalina'), +('tn_fakira'),('vw_silvia'),('vw_starshi'),('vw_geo'),('vw_b0x1'); +select * from t1 where a like 'we_%'; +a b +we_iliyan NULL +we_ivo NULL +we_martin NULL +we_toshko NULL +drop table t1; diff --git a/mysql-test/t/ctype_cp1251.test b/mysql-test/t/ctype_cp1251.test index 66a8a5aa909..2d670ec3607 100644 --- a/mysql-test/t/ctype_cp1251.test +++ b/mysql-test/t/ctype_cp1251.test @@ -32,3 +32,17 @@ select * from t1 where lower(b)='bbb'; select charset(a), charset(b), charset(binary 'ccc') from t1 limit 1; select collation(a), collation(b), collation(binary 'ccc') from t1 limit 1; drop table t1; + +# Test for BUG#8560 +create table t1 ( + a varchar(16) character set cp1251 collate cp1251_bin not null, + b int(10) default null, + primary key(a) +) charset=cp1251; +insert into t1 (a) values ('air'), + ('we'),('g'),('we_toshko'), ('s0urce'),('we_ivo'),('we_iliyan'), + ('we_martin'),('vw_grado'),('vw_vasko'),('tn_vili'),('tn_kalina'), + ('tn_fakira'),('vw_silvia'),('vw_starshi'),('vw_geo'),('vw_b0x1'); + +select * from t1 where a like 'we_%'; +drop table t1; diff --git a/strings/ctype-bin.c b/strings/ctype-bin.c index 618879607ec..425985e6bc1 100644 --- a/strings/ctype-bin.c +++ b/strings/ctype-bin.c @@ -67,6 +67,13 @@ static uchar bin_char_array[] = }; +static my_bool +my_coll_init_8bit_bin(CHARSET_INFO *cs, + void *(*alloc)(uint) __attribute__((unused))) +{ + cs->max_sort_char=255; + return FALSE; +} static int my_strnncoll_binary(CHARSET_INFO * cs __attribute__((unused)), const uchar *s, uint slen, @@ -428,7 +435,7 @@ skip: MY_COLLATION_HANDLER my_collation_8bit_bin_handler = { - NULL, /* init */ + my_coll_init_8bit_bin, my_strnncoll_8bit_bin, my_strnncollsp_8bit_bin, my_strnxfrm_8bit_bin, From dc9059e008c172d53e157b9386d12893dc308dfa Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 27 Feb 2005 18:41:34 +0300 Subject: [PATCH 24/42] post-review fixes + cleanup + some minor fixes server-tools/instance-manager/buffer.cc: coding style fixes server-tools/instance-manager/buffer.h: wrong constructor initialisation fixed server-tools/instance-manager/commands.cc: cleanup server-tools/instance-manager/guardian.cc: cleanup + added lock/unlock routines server-tools/instance-manager/guardian.h: GUARD_NODE moved to the header server-tools/instance-manager/instance.cc: Fix for the linuxthreads/POSIX signal handling problem (see comments in the code) server-tools/instance-manager/instance.h: condition variable renamed and commented server-tools/instance-manager/instance_map.cc: We need to refresh guardian during flush_instances server-tools/instance-manager/instance_map.h: removed obsolete function declaration server-tools/instance-manager/instance_options.cc: added caching of computed values server-tools/instance-manager/instance_options.h: added vars to cache some option values server-tools/instance-manager/listener.cc: check whether we are running on the linux threads server-tools/instance-manager/manager.cc: lock guardian before init() server-tools/instance-manager/parse_output.cc: cleanup server-tools/instance-manager/priv.cc: added global variables to detect whether we are running on the LinuxThreads server-tools/instance-manager/priv.h: added global variables to detect whether we are running on the LinuxThreads --- server-tools/instance-manager/buffer.cc | 4 +- server-tools/instance-manager/buffer.h | 2 +- server-tools/instance-manager/commands.cc | 19 ++- server-tools/instance-manager/guardian.cc | 117 ++++++++++-------- server-tools/instance-manager/guardian.h | 32 ++++- server-tools/instance-manager/instance.cc | 46 +++++-- server-tools/instance-manager/instance.h | 6 +- server-tools/instance-manager/instance_map.cc | 6 + server-tools/instance-manager/instance_map.h | 1 - .../instance-manager/instance_options.cc | 14 ++- .../instance-manager/instance_options.h | 11 +- server-tools/instance-manager/listener.cc | 7 ++ server-tools/instance-manager/manager.cc | 8 ++ server-tools/instance-manager/parse_output.cc | 2 +- server-tools/instance-manager/priv.cc | 9 ++ server-tools/instance-manager/priv.h | 12 ++ 16 files changed, 206 insertions(+), 90 deletions(-) diff --git a/server-tools/instance-manager/buffer.cc b/server-tools/instance-manager/buffer.cc index aaed161838d..b000a48d5ae 100644 --- a/server-tools/instance-manager/buffer.cc +++ b/server-tools/instance-manager/buffer.cc @@ -79,13 +79,13 @@ int Buffer::reserve(uint position, uint len_arg) if (position + len_arg >= MAX_BUFFER_SIZE) goto err; - if (position + len_arg>= buffer_size) + if (position + len_arg >= buffer_size) { buffer= (char *) my_realloc(buffer, min(MAX_BUFFER_SIZE, max((uint) (buffer_size*1.5), position + len_arg)), MYF(0)); - if (buffer == NULL) + if (!(buffer)) goto err; buffer_size= (uint) (buffer_size*1.5); } diff --git a/server-tools/instance-manager/buffer.h b/server-tools/instance-manager/buffer.h index 85dd210cac2..a94047b11a5 100644 --- a/server-tools/instance-manager/buffer.h +++ b/server-tools/instance-manager/buffer.h @@ -41,7 +41,7 @@ private: int error; public: Buffer(size_t buffer_size_arg= BUFFER_INITIAL_SIZE) - :buffer_size(BUFFER_INITIAL_SIZE), error(0) + :buffer_size(buffer_size_arg), error(0) { /* As append() will invokes realloc() anyway, it's ok if malloc returns 0 diff --git a/server-tools/instance-manager/commands.cc b/server-tools/instance-manager/commands.cc index 4e88f358148..9831ad3a891 100644 --- a/server-tools/instance-manager/commands.cc +++ b/server-tools/instance-manager/commands.cc @@ -170,7 +170,7 @@ int Show_instance_status::do_command(struct st_net *net, Instance *instance; store_to_string(&send_buff, (char *) instance_name, &position); - if ((instance= instance_map->find(instance_name, strlen(instance_name))) == NULL) + if (!(instance= instance_map->find(instance_name, strlen(instance_name)))) goto err; if (instance->is_running()) { @@ -201,7 +201,7 @@ err: int Show_instance_status::execute(struct st_net *net, ulong connection_id) { - if (instance_name != NULL) + if ((instance_name)) { if (do_command(net, instance_name)) return ER_OUT_OF_RESOURCES; @@ -257,14 +257,13 @@ int Show_instance_options::do_command(struct st_net *net, { Instance *instance; - if ((instance= instance_map-> - find(instance_name, strlen(instance_name))) == NULL) + if (!(instance= instance_map->find(instance_name, strlen(instance_name)))) goto err; store_to_string(&send_buff, (char *) "instance_name", &position); store_to_string(&send_buff, (char *) instance_name, &position); if (my_net_write(net, send_buff.buffer, (uint) position)) goto err; - if (instance->options.mysqld_path != NULL) + if ((instance->options.mysqld_path)) { position= 0; store_to_string(&send_buff, (char *) "mysqld-path", &position); @@ -276,7 +275,7 @@ int Show_instance_options::do_command(struct st_net *net, goto err; } - if (instance->options.nonguarded != NULL) + if ((instance->options.nonguarded)) { position= 0; store_to_string(&send_buff, (char *) "nonguarded", &position); @@ -317,7 +316,7 @@ err: int Show_instance_options::execute(struct st_net *net, ulong connection_id) { - if (instance_name != NULL) + if ((instance_name)) { if (do_command(net, instance_name)) return ER_OUT_OF_RESOURCES; @@ -351,10 +350,10 @@ int Start_instance::execute(struct st_net *net, ulong connection_id) } else { - if (err_code= instance->start()) + if ((err_code= instance->start())) return err_code; - if (instance->options.nonguarded == NULL) + if (!(instance->options.nonguarded)) instance_map->guardian->guard(instance); net_send_ok(net, connection_id); @@ -385,7 +384,7 @@ int Stop_instance::execute(struct st_net *net, ulong connection_id) } else { - if (instance->options.nonguarded == NULL) + if (!(instance->options.nonguarded)) instance_map->guardian-> stop_guard(instance); if ((err_code= instance->stop())) diff --git a/server-tools/instance-manager/guardian.cc b/server-tools/instance-manager/guardian.cc index 750c3495870..4fbfaf2672d 100644 --- a/server-tools/instance-manager/guardian.cc +++ b/server-tools/instance-manager/guardian.cc @@ -29,23 +29,6 @@ #include -/* - The Guardian list node structure. Guardian utilizes it to store - guarded instances plus some additional info. -*/ - -struct GUARD_NODE -{ - Instance *instance; - /* state of an instance (i.e. STARTED, CRASHED, etc.) */ - int state; - /* the amount of attemts to restart instance (cleaned up at success) */ - int restart_counter; - /* triggered at a crash */ - time_t crash_moment; - /* General time field. Used to provide timeouts (at shutdown and restart) */ - time_t last_checked; -}; C_MODE_START @@ -99,9 +82,9 @@ void Guardian_thread::request_shutdown(bool stop_instances_arg) void Guardian_thread::process_instance(Instance *instance, GUARD_NODE *current_node, LIST **guarded_instances, - LIST *elem) + LIST *node) { - int waitchild= Instance::DEFAULT_SHUTDOWN_DELAY; + uint waitchild= (uint) Instance::DEFAULT_SHUTDOWN_DELAY; /* The amount of times, Guardian attempts to restart an instance */ int restart_retry= 100; time_t current_time= time(NULL); @@ -109,21 +92,21 @@ void Guardian_thread::process_instance(Instance *instance, if (current_node->state == STOPPING) { /* this brach is executed during shutdown */ - if (instance->options.shutdown_delay != NULL) - waitchild= atoi(instance->options.shutdown_delay); + if (instance->options.shutdown_delay_val) + waitchild= instance->options.shutdown_delay_val; - /* this returns true if and only if an instance was stopped for shure */ + /* this returns true if and only if an instance was stopped for sure */ if (instance->is_crashed()) - *guarded_instances= list_delete(*guarded_instances, elem); - else if (current_time - current_node->last_checked > waitchild) - { - instance->kill_instance(SIGKILL); - /* - Later we do elem= elem->next. This is ok, as we are only removing - the node from the list. The pointer to the next one is still valid. - */ - *guarded_instances= list_delete(*guarded_instances, elem); - } + *guarded_instances= list_delete(*guarded_instances, node); + else if ( (uint) (current_time - current_node->last_checked) > waitchild) + { + instance->kill_instance(SIGKILL); + /* + Later we do node= node->next. This is ok, as we are only removing + the node from the list. The pointer to the next one is still valid. + */ + *guarded_instances= list_delete(*guarded_instances, node); + } return; } @@ -174,11 +157,12 @@ void Guardian_thread::process_instance(Instance *instance, { instance->start(); current_node->last_checked= current_time; - ((GUARD_NODE *) elem->data)->restart_counter++; - log_info("guardian: starting instance %s", + current_node->restart_counter++; + log_info("guardian: restarting instance %s", instance->options.instance_name); } - else current_node->state= CRASHED_AND_ABANDONED; + else + current_node->state= CRASHED_AND_ABANDONED; } break; case CRASHED_AND_ABANDONED: @@ -205,7 +189,7 @@ void Guardian_thread::process_instance(Instance *instance, void Guardian_thread::run() { Instance *instance; - LIST *elem; + LIST *node; struct timespec timeout; thread_registry.register_thread(&thread_info); @@ -216,23 +200,23 @@ void Guardian_thread::run() /* loop, until all instances were shut down at the end */ while (!(shutdown_requested && (guarded_instances == NULL))) { - elem= guarded_instances; + node= guarded_instances; - while (elem != NULL) + while (node != NULL) { struct timespec timeout; - GUARD_NODE *current_node= (GUARD_NODE *) elem->data; - instance= ((GUARD_NODE *) elem->data)->instance; - process_instance(instance, current_node, &guarded_instances, elem); + GUARD_NODE *current_node= (GUARD_NODE *) node->data; + instance= ((GUARD_NODE *) node->data)->instance; + process_instance(instance, current_node, &guarded_instances, node); - elem= elem->next; + node= node->next; } timeout.tv_sec= time(NULL) + monitoring_interval; timeout.tv_nsec= 0; /* check the loop predicate before sleeping */ - if (!(shutdown_requested && (guarded_instances == NULL))) + if (!(shutdown_requested && (!(guarded_instances)))) pthread_cond_timedwait(&COND_guardian, &LOCK_guardian, &timeout); } @@ -262,6 +246,8 @@ int Guardian_thread::is_stopped() SYNOPSYS Guardian_thread::init() + NOTE: One should always lock guardian before calling this routine. + RETURN 0 - ok 1 - error occured @@ -273,14 +259,22 @@ int Guardian_thread::init() Instance_map::Iterator iterator(instance_map); instance_map->lock(); + /* clear the list of guarded instances */ + free_root(&alloc, MYF(0)); + init_alloc_root(&alloc, MEM_ROOT_BLOCK_SIZE, 0); + guarded_instances= NULL; + while ((instance= iterator.next())) { - if ((instance->options.nonguarded == NULL)) - if (guard(instance)) + if (!(instance->options.nonguarded)) + if (guard(instance, TRUE)) /* do not lock guardian */ + { + instance_map->unlock(); return 1; + } } - instance_map->unlock(); + instance_map->unlock(); return 0; } @@ -291,6 +285,8 @@ int Guardian_thread::init() SYNOPSYS guard() instance the instance to be guarded + nolock whether we prefer do not lock Guardian here, + but use external locking instead DESCRIPTION @@ -302,7 +298,7 @@ int Guardian_thread::init() 1 - error occured */ -int Guardian_thread::guard(Instance *instance) +int Guardian_thread::guard(Instance *instance, bool nolock) { LIST *node; GUARD_NODE *content; @@ -310,7 +306,7 @@ int Guardian_thread::guard(Instance *instance) node= (LIST *) alloc_root(&alloc, sizeof(LIST)); content= (GUARD_NODE *) alloc_root(&alloc, sizeof(GUARD_NODE)); - if ((node == NULL) || (content == NULL)) + if ((!(node)) || (!(content))) return 1; /* we store the pointers to instances from the instance_map's MEM_ROOT */ content->instance= instance; @@ -319,16 +315,21 @@ int Guardian_thread::guard(Instance *instance) content->state= NOT_STARTED; node->data= (void *) content; - pthread_mutex_lock(&LOCK_guardian); - guarded_instances= list_add(guarded_instances, node); - pthread_mutex_unlock(&LOCK_guardian); + if (nolock) + guarded_instances= list_add(guarded_instances, node); + else + { + pthread_mutex_lock(&LOCK_guardian); + guarded_instances= list_add(guarded_instances, node); + pthread_mutex_unlock(&LOCK_guardian); + } return 0; } /* - TODO: perhaps it would make sense to create a pool of the LIST elements + TODO: perhaps it would make sense to create a pool of the LIST nodeents and give them upon request. Now we are loosing a bit of memory when guarded instance was stopped and then restarted (since we cannot free just a piece of the MEM_ROOT). @@ -419,3 +420,15 @@ int Guardian_thread::stop_instances(bool stop_instances_arg) } return 0; } + + +int Guardian_thread::lock() +{ + return pthread_mutex_lock(&LOCK_guardian); +} + + +int Guardian_thread::unlock() +{ + return pthread_mutex_unlock(&LOCK_guardian); +} diff --git a/server-tools/instance-manager/guardian.h b/server-tools/instance-manager/guardian.h index 0aec00099de..e43b5d565d7 100644 --- a/server-tools/instance-manager/guardian.h +++ b/server-tools/instance-manager/guardian.h @@ -60,22 +60,47 @@ struct Guardian_thread_args class Guardian_thread: public Guardian_thread_args { public: + /* states of an instance */ + enum INSTANCE_STATE { NOT_STARTED= 1, STARTING, STARTED, JUST_CRASHED, + CRASHED, CRASHED_AND_ABANDONED, STOPPING }; + + /* + The Guardian list node structure. Guardian utilizes it to store + guarded instances plus some additional info. + */ + + struct GUARD_NODE + { + Instance *instance; + /* state of an instance (i.e. STARTED, CRASHED, etc.) */ + INSTANCE_STATE state; + /* the amount of attemts to restart instance (cleaned up at success) */ + int restart_counter; + /* triggered at a crash */ + time_t crash_moment; + /* General time field. Used to provide timeouts (at shutdown and restart) */ + time_t last_checked; + }; + + Guardian_thread(Thread_registry &thread_registry_arg, Instance_map *instance_map_arg, uint monitoring_interval_arg); ~Guardian_thread(); /* Main funtion of the thread */ void run(); - /* Initialize list of guarded instances */ + /* Initialize or refresh the list of guarded instances */ int init(); /* Request guardian shutdown. Stop instances if needed */ void request_shutdown(bool stop_instances); /* Start instance protection */ - int guard(Instance *instance); + int guard(Instance *instance, bool nolock= FALSE); /* Stop instance protection */ int stop_guard(Instance *instance); /* Returns true if guardian thread is stopped */ int is_stopped(); + int lock(); + int unlock(); public: pthread_cond_t COND_guardian; @@ -89,9 +114,6 @@ private: int stopped; private: - /* states of an instance */ - enum { NOT_STARTED= 1, STARTING, STARTED, JUST_CRASHED, CRASHED, - CRASHED_AND_ABANDONED, STOPPING }; pthread_mutex_t LOCK_guardian; Thread_info thread_info; LIST *guarded_instances; diff --git a/server-tools/instance-manager/instance.cc b/server-tools/instance-manager/instance.cc index 165b06644f4..97282f3f800 100644 --- a/server-tools/instance-manager/instance.cc +++ b/server-tools/instance-manager/instance.cc @@ -22,6 +22,8 @@ #include "mysql_manager_error.h" #include "log.h" #include "instance_map.h" +#include "priv.h" + #include #include #include @@ -30,6 +32,13 @@ C_MODE_START +/* + Proxy thread is a simple way to avoid all pitfalls of the threads + implementation in the OS (e.g. LinuxThreads). With such a thread we + don't have to process SIGCHLD, which is a tricky business if we want + to do it in a portable way. +*/ + pthread_handler_decl(proxy, arg) { Instance *instance= (Instance *) arg; @@ -111,7 +120,26 @@ void Instance::fork_and_monitor() log_info("cannot fork() to start instance %s", options.instance_name); return; default: - wait(NULL); + /* + Here we wait for the child created. This process differs for systems + running LinuxThreads and POSIX Threads compliant systems. This is because + according to POSIX we could wait() for a child in any thread of the + process. While LinuxThreads require that wait() is called by the thread, + which created the child. + On the other hand we could not expect mysqld to return the pid, we + got in from fork(), to wait4() fucntion when running on LinuxThreads. + This is because MySQL shutdown thread is not the one, which was created + by our fork() call. + So basically we have two options: whether the wait() call returns only in + the creator thread, but we cannot use waitpid() since we have no idea + which pid we should wait for (in fact it should be the pid of shutdown + thread, but we don't know this one). Or we could use waitpid(), but + couldn't use wait(), because it could return in any wait() in the program. + */ + if (linuxthreads) + wait(NULL); /* LinuxThreads were detected */ + else + waitpid(pid, NULL, 0); /* set instance state to crashed */ pthread_mutex_lock(&LOCK_instance); crashed= 1; @@ -122,7 +150,7 @@ void Instance::fork_and_monitor() is needed if a user issued command to stop an instance via mysql connection. This is not the case if Guardian stop the thread. */ - pthread_cond_signal(&COND_instance_restarted); + pthread_cond_signal(&COND_instance_stopped); /* wake guardian */ pthread_cond_signal(&instance_map->guardian->COND_guardian); /* thread exits */ @@ -136,14 +164,14 @@ void Instance::fork_and_monitor() Instance::Instance(): crashed(0) { pthread_mutex_init(&LOCK_instance, 0); - pthread_cond_init(&COND_instance_restarted, 0); + pthread_cond_init(&COND_instance_stopped, 0); } Instance::~Instance() { + pthread_cond_destroy(&COND_instance_stopped); pthread_mutex_destroy(&LOCK_instance); - pthread_cond_destroy(&COND_instance_restarted); } @@ -168,7 +196,7 @@ bool Instance::is_running() bool return_val; if (options.mysqld_port) - port= atoi(strchr(options.mysqld_port, '=') + 1); + port= options.mysqld_port_val; if (options.mysqld_socket) socket= strchr(options.mysqld_socket, '=') + 1; @@ -226,10 +254,10 @@ int Instance::stop() { pid_t pid; struct timespec timeout; - int waitchild= DEFAULT_SHUTDOWN_DELAY; + uint waitchild= (uint) DEFAULT_SHUTDOWN_DELAY; - if (options.shutdown_delay != NULL) - waitchild= atoi(options.shutdown_delay); + if (options.shutdown_delay_val) + waitchild= options.shutdown_delay_val; kill_instance(SIGTERM); /* sleep on condition to wait for SIGCHLD */ @@ -243,7 +271,7 @@ int Instance::stop() { int status; - status= pthread_cond_timedwait(&COND_instance_restarted, + status= pthread_cond_timedwait(&COND_instance_stopped, &LOCK_instance, &timeout); if (status == ETIMEDOUT) diff --git a/server-tools/instance-manager/instance.h b/server-tools/instance-manager/instance.h index 9f3334ba464..c141ff7b30d 100644 --- a/server-tools/instance-manager/instance.h +++ b/server-tools/instance-manager/instance.h @@ -57,7 +57,11 @@ private: */ int crashed; pthread_mutex_t LOCK_instance; - pthread_cond_t COND_instance_restarted; + /* + This condition variable is used to wake threads waiting for instance to + stop in Instance::stop() + */ + pthread_cond_t COND_instance_stopped; Instance_map *instance_map; }; diff --git a/server-tools/instance-manager/instance_map.cc b/server-tools/instance-manager/instance_map.cc index 919f9c955a4..d19d42662e5 100644 --- a/server-tools/instance-manager/instance_map.cc +++ b/server-tools/instance-manager/instance_map.cc @@ -151,12 +151,15 @@ int Instance_map::flush_instances() { int rc; + guardian->lock(); pthread_mutex_lock(&LOCK_instance_map); hash_free(&hash); hash_init(&hash, default_charset_info, START_HASH_SIZE, 0, 0, get_instance_key, delete_instance, 0); pthread_mutex_unlock(&LOCK_instance_map); rc= load(); + guardian->init(); + guardian->unlock(); return rc; } @@ -183,6 +186,7 @@ int Instance_map::complete_initialization() Instance *instance; uint i= 0; + if (hash.records == 0) /* no instances found */ { if ((instance= new Instance) == 0) @@ -191,6 +195,7 @@ int Instance_map::complete_initialization() if (instance->init("mysqld") || add_instance(instance)) goto err_instance; + /* After an instance have been added to the instance_map, hash_free should handle it's deletion => goto err, not @@ -227,6 +232,7 @@ int Instance_map::load() const char *argv_options[3]; char **argv= (char **) &argv_options; + /* the name of the program may be orbitrary here in fact */ argv_options[0]= "mysqlmanager"; if (first_option != NULL) diff --git a/server-tools/instance-manager/instance_map.h b/server-tools/instance-manager/instance_map.h index 98d98b5c516..8d8ea33cf5d 100644 --- a/server-tools/instance-manager/instance_map.h +++ b/server-tools/instance-manager/instance_map.h @@ -57,7 +57,6 @@ public: public: /* returns a pointer to the instance or NULL, if there is no such instance */ Instance *find(const char *name, uint name_len); - Instance *find(uint instance_number); int flush_instances(); int lock(); diff --git a/server-tools/instance-manager/instance_options.cc b/server-tools/instance-manager/instance_options.cc index 4242b8b6454..e1b81bc93b1 100644 --- a/server-tools/instance-manager/instance_options.cc +++ b/server-tools/instance-manager/instance_options.cc @@ -79,7 +79,7 @@ int Instance_options::get_pid_filename(char *result) const char *pid_file= mysqld_pid_file; char datadir[MAX_PATH_LEN]; - if (mysqld_datadir == NULL) + if (!(mysqld_datadir)) { /* we might get an error here if we have wrong path to the mysqld binary */ if (get_default_option(datadir, sizeof(datadir), "--datadir")) @@ -128,16 +128,22 @@ int Instance_options::complete_initialization(const char *default_path, { const char *tmp; - if (mysqld_path == NULL) + if (!(mysqld_path)) { if (!(mysqld_path= strdup_root(&alloc, default_path))) goto err; } + if (mysqld_port) + mysqld_port_val= atoi(strchr(mysqld_port, '=') + 1); + + if (shutdown_delay) + shutdown_delay_val= atoi(shutdown_delay); + if (!(tmp= strdup_root(&alloc, "--no-defaults"))) goto err; - if (mysqld_pid_file == NULL) + if (!(mysqld_pid_file)) { char pidfilename[MAX_PATH_LEN]; char hostname[MAX_PATH_LEN]; @@ -265,7 +271,7 @@ int Instance_options::add_to_argv(const char* option) { DBUG_ASSERT(filled_default_options < MAX_NUMBER_OF_DEFAULT_OPTIONS); - if (option != NULL) + if ((option)) argv[filled_default_options++]= (char *) option; return 0; } diff --git a/server-tools/instance-manager/instance_options.h b/server-tools/instance-manager/instance_options.h index 5e91ee80f00..06ad0156bc0 100644 --- a/server-tools/instance-manager/instance_options.h +++ b/server-tools/instance-manager/instance_options.h @@ -37,9 +37,10 @@ class Instance_options { public: Instance_options() : - mysqld_socket(0), mysqld_datadir(0), mysqld_bind_address(0), - mysqld_pid_file(0), mysqld_port(0), mysqld_path(0), nonguarded(0), - shutdown_delay(0), filled_default_options(0) + mysqld_socket(0), mysqld_datadir(0), + mysqld_bind_address(0), mysqld_pid_file(0), mysqld_port(0), + mysqld_port_val(0), mysqld_path(0), nonguarded(0), shutdown_delay(0), + shutdown_delay_val(0), filled_default_options(0) {} ~Instance_options(); /* fills in argv */ @@ -68,11 +69,13 @@ public: const char *mysqld_bind_address; const char *mysqld_pid_file; const char *mysqld_port; - uint instance_name_len; + uint mysqld_port_val; const char *instance_name; + uint instance_name_len; const char *mysqld_path; const char *nonguarded; const char *shutdown_delay; + uint shutdown_delay_val; /* this value is computed and cashed here */ DYNAMIC_ARRAY options_array; private: diff --git a/server-tools/instance-manager/listener.cc b/server-tools/instance-manager/listener.cc index 14970989e92..946bab11a2e 100644 --- a/server-tools/instance-manager/listener.cc +++ b/server-tools/instance-manager/listener.cc @@ -31,6 +31,7 @@ #include "instance_map.h" #include "log.h" #include "mysql_connection.h" +#include "priv.h" /* @@ -82,6 +83,12 @@ void Listener_thread::run() int arg= 1; /* value to be set by setsockopt */ int unix_socket; uint im_port; + /* we use this var to check whether we are running on LinuxThreads */ + pid_t thread_pid; + + thread_pid= getpid(); + /* set global variable */ + linuxthreads= (thread_pid != manager_pid); thread_registry.register_thread(&thread_info); diff --git a/server-tools/instance-manager/manager.cc b/server-tools/instance-manager/manager.cc index 89d49ba9219..8629c5e9d9c 100644 --- a/server-tools/instance-manager/manager.cc +++ b/server-tools/instance-manager/manager.cc @@ -16,6 +16,7 @@ #include "manager.h" +#include "priv.h" #include "thread_registry.h" #include "listener.h" #include "instance_map.h" @@ -75,11 +76,13 @@ void manager(const Options &options) Listener_thread_args listener_args(thread_registry, options, user_map, instance_map); + manager_pid= getpid(); instance_map.guardian= &guardian_thread; if (instance_map.init() || user_map.init()) return; + if (instance_map.load()) { log_error("Cannot init instances repository. This might be caused by " @@ -170,7 +173,12 @@ void manager(const Options &options) */ init_thr_alarm(10); /* init list of guarded instances */ + guardian_thread.lock(); + guardian_thread.init(); + + guardian_thread.unlock(); + /* After the list of guarded instances have been initialized, Guardian should start them. diff --git a/server-tools/instance-manager/parse_output.cc b/server-tools/instance-manager/parse_output.cc index 58e3fbf9fbd..6fee0797395 100644 --- a/server-tools/instance-manager/parse_output.cc +++ b/server-tools/instance-manager/parse_output.cc @@ -53,7 +53,7 @@ int parse_output_and_get_value(const char *command, const char *word, wordlen= strlen(word); - if ((output= popen(command, "r")) == NULL) + if (!(output= popen(command, "r"))) goto err; /* diff --git a/server-tools/instance-manager/priv.cc b/server-tools/instance-manager/priv.cc index 4b47fe5b593..dd192370aaf 100644 --- a/server-tools/instance-manager/priv.cc +++ b/server-tools/instance-manager/priv.cc @@ -16,6 +16,15 @@ #include "priv.h" +/* the pid of the manager process (of the signal thread on the LinuxThreads) */ +pid_t manager_pid; + +/* + This flag is set if mysqlmanager has detected that it is running on the + system using LinuxThreads +*/ +bool linuxthreads; + /* The following string must be less then 80 characters, as mysql_connection.cc relies on it diff --git a/server-tools/instance-manager/priv.h b/server-tools/instance-manager/priv.h index 8014df7260c..5bee8aa1463 100644 --- a/server-tools/instance-manager/priv.h +++ b/server-tools/instance-manager/priv.h @@ -16,6 +16,18 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include + + +/* the pid of the manager process (of the signal thread on the LinuxThreads) */ +extern pid_t manager_pid; + +/* + This flag is set if mysqlmanager has detected that it is running on the + system using LinuxThreads +*/ +extern bool linuxthreads; + extern const char mysqlmanager_version[]; extern const int mysqlmanager_version_length; From 8b8c9452ddc734c42301f64e625c31779968232a Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 28 Feb 2005 11:59:46 +0200 Subject: [PATCH 25/42] Fixed wrong memory references found by purify (No really critical errors found, but a few possible wrong results) innobase/dict/dict0dict.c: Replace memcmp with comparison of characters to avoid warnings from purify when 'sptr' points to a very short string mysql-test/r/select_found.result: Add missing drop table mysql-test/r/type_set.result: More tests mysql-test/t/select_found.test: Add missing drop table mysql-test/t/type_set.test: More tests mysys/my_init.c: Avoid warning from purify (purify doesn't handle getrusage() properly) sql/field.h: enum & set are sorted as numbers. This fixes an access to uninitialized memory when enum/set are multi-byte characters sql/filesort.cc: enum & set are sorted as numbers. This fixes an access to uninitialized memory when enum/set are multi-byte characters sql/item_cmpfunc.cc: Fixed warning from purify. (Not critical as the arguments are passed to a function but not used) Allocate Arg_comparator() with 'new' instead of sql_alloc() to ensure proper initialization sql/mysqld.cc: Wait for signal handler to stop when running --bootstrap (Fixes warning from purify) sql/sql_insert.cc: Initialize slot used by innodb.cc (not critical) sql/sql_lex.h: Better comments sql/sql_repl.cc: memcmp -> bcmp() to avoid warning from purify sql/sql_select.cc: Fix for out-of-bound memory reference when doing DISTINCT on const expressions strings/ctype-simple.c: Fixes to not access uninitialized memory (Not critical) --- innobase/dict/dict0dict.c | 3 +- mysql-test/r/select_found.result | 1 + mysql-test/r/type_set.result | 18 ++++++++ mysql-test/t/select_found.test | 1 + mysql-test/t/type_set.test | 2 + mysys/my_init.c | 4 ++ sql/field.h | 3 ++ sql/filesort.cc | 2 +- sql/item_cmpfunc.cc | 8 +++- sql/mysqld.cc | 42 +++++++++++------- sql/sql_insert.cc | 3 +- sql/sql_lex.h | 73 ++++++++++++++++++++++---------- sql/sql_repl.cc | 2 +- sql/sql_select.cc | 33 ++++++++++----- strings/ctype-simple.c | 9 ++-- 15 files changed, 148 insertions(+), 56 deletions(-) diff --git a/innobase/dict/dict0dict.c b/innobase/dict/dict0dict.c index 186f3be2f31..a73dd8b9dd9 100644 --- a/innobase/dict/dict0dict.c +++ b/innobase/dict/dict0dict.c @@ -2671,7 +2671,8 @@ scan_more: /* Starting quote: remember the quote character. */ quote = *sptr; } else if (*sptr == '#' - || (0 == memcmp("-- ", sptr, 3))) { + || (sptr[0] == '-' && sptr[1] == '-' && + sptr[2] == ' ')) { for (;;) { /* In Unix a newline is 0x0A while in Windows it is 0x0D followed by 0x0A */ diff --git a/mysql-test/r/select_found.result b/mysql-test/r/select_found.result index 665277f68f2..39a949ef86b 100644 --- a/mysql-test/r/select_found.result +++ b/mysql-test/r/select_found.result @@ -254,3 +254,4 @@ a SELECT FOUND_ROWS(); FOUND_ROWS() 1 +DROP TABLE t1; diff --git a/mysql-test/r/type_set.result b/mysql-test/r/type_set.result index 9c82f59fc69..5aedefda283 100644 --- a/mysql-test/r/type_set.result +++ b/mysql-test/r/type_set.result @@ -29,6 +29,12 @@ a A a,A a,A +select s from t1 order by concat(s); +s +A +a +a,A +a,A drop table t1; CREATE TABLE t1 (c set('ae','oe','ue','ss') collate latin1_german2_ci); INSERT INTO t1 VALUES ('ä'),('ö'),('ü'),('ß'); @@ -47,4 +53,16 @@ ss ss ae,oe,ue,ss ae,oe,ue,ss +SELECT c FROM t1 ORDER BY concat(c); +c +ae +ae +ae,oe,ue,ss +ae,oe,ue,ss +oe +oe +ss +ss +ue +ue DROP TABLE t1; diff --git a/mysql-test/t/select_found.test b/mysql-test/t/select_found.test index d31d7d0b02e..5bd068eb0e6 100644 --- a/mysql-test/t/select_found.test +++ b/mysql-test/t/select_found.test @@ -175,3 +175,4 @@ CREATE TABLE t1 (a int, b int); INSERT INTO t1 VALUES (1,2), (1,3), (1,4), (1,5); SELECT SQL_CALC_FOUND_ROWS DISTINCT 'a' FROM t1 GROUP BY b LIMIT 2; SELECT FOUND_ROWS(); +DROP TABLE t1; diff --git a/mysql-test/t/type_set.test b/mysql-test/t/type_set.test index e4aeecb2c79..b6410a9ea3d 100644 --- a/mysql-test/t/type_set.test +++ b/mysql-test/t/type_set.test @@ -23,6 +23,7 @@ create table t1 (s set ('a','A') character set latin1 collate latin1_bin); show create table t1; insert into t1 values ('a'),('a,A'),('A,a'),('A'); select s from t1 order by s; +select s from t1 order by concat(s); drop table t1; # @@ -34,4 +35,5 @@ INSERT INTO t1 VALUES ('ae'),('oe'),('ue'),('ss'); INSERT INTO t1 VALUES ('ä,ö,ü,ß'); INSERT INTO t1 VALUES ('ae,oe,ue,ss'); SELECT c FROM t1 ORDER BY c; +SELECT c FROM t1 ORDER BY concat(c); DROP TABLE t1; diff --git a/mysys/my_init.c b/mysys/my_init.c index c32fcfe6a09..bee485c3bed 100644 --- a/mysys/my_init.c +++ b/mysys/my_init.c @@ -145,6 +145,10 @@ void my_end(int infoflag) { #ifdef HAVE_GETRUSAGE struct rusage rus; +#ifdef HAVE_purify + /* Purify assumes that rus is uninitialized after getrusage call */ + bzero((char*) &rus, sizeof(rus)); +#endif if (!getrusage(RUSAGE_SELF, &rus)) fprintf(info_file,"\n\ User time %.2f, System time %.2f\n\ diff --git a/sql/field.h b/sql/field.h index fd0f2f9c2f1..b5d88504939 100644 --- a/sql/field.h +++ b/sql/field.h @@ -277,6 +277,7 @@ public: virtual bool get_date(TIME *ltime,uint fuzzydate); virtual bool get_time(TIME *ltime); virtual CHARSET_INFO *charset(void) const { return &my_charset_bin; } + virtual CHARSET_INFO *sort_charset(void) const { return charset(); } virtual bool has_charset(void) const { return FALSE; } virtual void set_charset(CHARSET_INFO *charset) { } bool set_warning(const unsigned int level, const unsigned int code, @@ -1152,6 +1153,8 @@ public: bool optimize_range(uint idx, uint part) { return 0; } bool eq_def(Field *field); bool has_charset(void) const { return TRUE; } + /* enum and set are sorted as integers */ + CHARSET_INFO *sort_charset(void) const { return &my_charset_bin; } field_cast_enum field_cast_type() { return FIELD_CAST_ENUM; } }; diff --git a/sql/filesort.cc b/sql/filesort.cc index 24088210f1a..76ce9ac4ce2 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -1127,7 +1127,7 @@ sortlength(SORT_FIELD *sortorder, uint s_length, bool *multi_byte_charset) else { sortorder->length=sortorder->field->pack_length(); - if (use_strnxfrm((cs=sortorder->field->charset()))) + if (use_strnxfrm((cs=sortorder->field->sort_charset()))) { sortorder->need_strxnfrm= 1; *multi_byte_charset= 1; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index aff11ac7b25..690da1be18c 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -265,7 +265,7 @@ int Arg_comparator::set_compare_func(Item_bool_func2 *item, Item_result type) comparators= 0; return 1; } - if (!(comparators= (Arg_comparator *) sql_alloc(sizeof(Arg_comparator)*n))) + if (!(comparators= new Arg_comparator[n])) return 1; for (uint i=0; i < n; i++) { @@ -1528,6 +1528,12 @@ in_row::in_row(uint elements, Item * item) size= sizeof(cmp_item_row); compare= (qsort2_cmp) cmp_row; tmp.store_value(item); + /* + We need to reset these as otherwise we will call sort() with + uninitialized (even if not used) elements + */ + used_count= elements; + collation= 0; } in_row::~in_row() diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 1591b205d46..f1b1d8a7d86 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -530,6 +530,7 @@ extern "C" pthread_handler_decl(handle_slave,arg); static ulong find_bit_type(const char *x, TYPELIB *bit_lib); static void clean_up(bool print_message); static void clean_up_mutexes(void); +static void wait_for_signal_thread_to_end(void); static int test_if_case_insensitive(const char *dir_name); static void create_pid_file(); @@ -918,6 +919,7 @@ extern "C" void unireg_abort(int exit_code) sql_print_error("Aborting\n"); clean_up(exit_code || !opt_bootstrap); /* purecov: inspected */ DBUG_PRINT("quit",("done with cleanup in unireg_abort")); + wait_for_signal_thread_to_end(); clean_up_mutexes(); my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0); exit(exit_code); /* purecov: inspected */ @@ -1023,6 +1025,29 @@ void clean_up(bool print_message) } /* clean_up */ +/* + This is mainly needed when running with purify, but it's still nice to + know that all child threads have died when mysqld exits +*/ + +static void wait_for_signal_thread_to_end() +{ +#ifndef __NETWARE__ + uint i; + /* + Wait up to 10 seconds for signal thread to die. We use this mainly to + avoid getting warnings that my_thread_end has not been called + */ + for (i= 0 ; i < 100 && signal_thread_in_use; i++) + { + if (pthread_kill(signal_thread, MYSQL_KILL_SIGNAL)) + break; + my_sleep(100); // Give it time to die + } +#endif +} + + static void clean_up_mutexes() { (void) pthread_mutex_destroy(&LOCK_mysql_create_db); @@ -2117,6 +2142,7 @@ extern "C" void *signal_hand(void *arg __attribute__((unused))) while ((error=my_sigwait(&set,&sig)) == EINTR) ; if (cleanup_done) { + DBUG_PRINT("quit",("signal_handler: calling my_thread_end()")); my_thread_end(); signal_thread_in_use= 0; pthread_exit(0); // Safety @@ -3111,21 +3137,7 @@ we force server id to 2, but this MySQL server will not act as a slave."); CloseHandle(hEventShutdown); } #endif -#ifndef __NETWARE__ - { - uint i; - /* - Wait up to 10 seconds for signal thread to die. We use this mainly to - avoid getting warnings that my_thread_end has not been called - */ - for (i= 0 ; i < 100 && signal_thread_in_use; i++) - { - if (pthread_kill(signal_thread, MYSQL_KILL_SIGNAL)) - break; - my_sleep(100); // Give it time to die - } - } -#endif + wait_for_signal_thread_to_end(); clean_up_mutexes(); my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 844e5e7dac2..1f190a450de 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -688,7 +688,8 @@ public: thd.current_tablenr=0; thd.version=refresh_version; thd.command=COM_DELAYED_INSERT; - thd.lex->current_select= 0; /* for my_message_sql */ + thd.lex->current_select= 0; // for my_message_sql + thd.lex->sql_command= SQLCOM_INSERT; // For innodb::store_lock() bzero((char*) &thd.net, sizeof(thd.net)); // Safety bzero((char*) &table_list, sizeof(table_list)); // Safety diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 3e2f6a3f2b5..f48ff42bbf8 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -125,27 +125,46 @@ enum tablespace_op_type /* The state of the lex parsing for selects - All select describing structures linked with following pointers: - - list of neighbors (next/prev) (prev of first element point to slave - pointer of upper structure) - - one level units for unit (union) structure - - member of one union(unit) for ordinary select_lex - - pointer to master - - outer select_lex for unit (union) - - unit structure for ordinary select_lex - - pointer to slave - - first list element of select_lex belonged to this unit for unit - - first unit in list of units that belong to this select_lex (as - subselects or derived tables) for ordinary select_lex - - list of all select_lex (for group operation like correcting list of opened - tables) - - if unit contain several selects (union) then it have special - select_lex called fake_select_lex. It used for storing global parameters - and executing union. subqueries of global ORDER BY clause will be - attached to this fake_select_lex, which will allow them correctly - resolve fields of 'upper' union and other more outer selects. + master and slaves are pointers to select_lex. + master is pointer to upper level node. + slave is pointer to lower level node + select_lex is a SELECT without union + unit is container of either + - One SELECT + - UNION of selects + select_lex and unit are both inherited form select_lex_node + neighbors are two select_lex or units on the same level - for example for following query: + All select describing structures linked with following pointers: + - list of neighbors (next/prev) (prev of first element point to slave + pointer of upper structure) + - For select this is a list of UNION's (or one element list) + - For units this is a list of sub queries for the upper level select + + - pointer to master (master), which is + If this is a unit + - pointer to outer select_lex + If this is a select_lex + - pointer to outer unit structure for select + + - pointer to slave (slave), which is either: + If this is a unit: + - first SELECT that belong to this unit + If this is a select_lex + - first unit that belong to this SELECT (subquries or derived tables) + + - list of all select_lex (link_next/link_prev) + This is to be used for things like derived tables creation, where we + go through this list and create the derived tables. + + If unit contain several selects (UNION now, INTERSECT etc later) + then it have special select_lex called fake_select_lex. It used for + storing global parameters (like ORDER BY, LIMIT) and executing union. + Subqueries used in global ORDER BY clause will be attached to this + fake_select_lex, which will allow them correctly resolve fields of + 'upper' UNION and outer selects. + + For example for following query: select * from table1 @@ -163,6 +182,11 @@ enum tablespace_op_type we will have following structure: + select1: (select * from table1 ...) + select2: (select * from table2 ...) + select3: (select * from table3) + select1.1.1: (select * from table1_1_1) + ... main unit fake0 @@ -185,7 +209,12 @@ enum tablespace_op_type relation in main unit will be following: - + (bigger picture for: + main unit + fake0 + select1 select2 select3 + in the above picture) + main unit |^^^^|fake_select_lex |||||+--------------------------------------------+ @@ -382,7 +411,7 @@ private: typedef class st_select_lex_unit SELECT_LEX_UNIT; /* - SELECT_LEX - store information of parsed SELECT_LEX statment + SELECT_LEX - store information of parsed SELECT statment */ class st_select_lex: public st_select_lex_node { diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index fd165ad1fa5..d02bb5ff0a3 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -246,7 +246,7 @@ bool log_in_use(const char* log_name) if ((linfo = tmp->current_linfo)) { pthread_mutex_lock(&linfo->lock); - result = !memcmp(log_name, linfo->log_file_name, log_name_len); + result = !bcmp(log_name, linfo->log_file_name, log_name_len); pthread_mutex_unlock(&linfo->lock); if (result) break; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 05314097ca3..a210fbbbe02 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -7622,8 +7622,8 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table, { byte *key_buffer, *key_pos, *record=table->record[0]; int error; - handler *file=table->file; - ulong extra_length=ALIGN_SIZE(key_length)-key_length; + handler *file= table->file; + ulong extra_length= ALIGN_SIZE(key_length)-key_length; uint *field_lengths,*field_length; HASH hash; DBUG_ENTER("remove_dup_with_hash_index"); @@ -7637,22 +7637,34 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table, NullS)) DBUG_RETURN(1); + { + Field **ptr; + ulong total_length= 0; + for (ptr= first_field, field_length=field_lengths ; *ptr ; ptr++) + { + uint length= (*ptr)->pack_length(); + (*field_length++)= length; + total_length+= length; + } + DBUG_PRINT("info",("field_count: %u key_length: %lu total_length: %lu", + field_count, key_length, total_length)); + DBUG_ASSERT(total_length <= key_length); + key_length= total_length; + extra_length= ALIGN_SIZE(key_length)-key_length; + } + if (hash_init(&hash, &my_charset_bin, (uint) file->records, 0, - key_length,(hash_get_key) 0, 0, 0)) + key_length, (hash_get_key) 0, 0, 0)) { my_free((char*) key_buffer,MYF(0)); DBUG_RETURN(1); } - { - Field **ptr; - for (ptr= first_field, field_length=field_lengths ; *ptr ; ptr++) - (*field_length++)= (*ptr)->pack_length(); - } file->ha_rnd_init(1); key_pos=key_buffer; for (;;) { + byte *org_key_pos; if (thd->killed) { my_error(ER_SERVER_SHUTDOWN,MYF(0)); @@ -7675,6 +7687,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table, } /* copy fields to key buffer */ + org_key_pos= key_pos; field_length=field_lengths; for (Field **ptr= first_field ; *ptr ; ptr++) { @@ -7682,14 +7695,14 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table, key_pos+= *field_length++; } /* Check if it exists before */ - if (hash_search(&hash,key_pos-key_length,key_length)) + if (hash_search(&hash, org_key_pos, key_length)) { /* Duplicated found ; Remove the row */ if ((error=file->delete_row(record))) goto err; } else - (void) my_hash_insert(&hash, key_pos-key_length); + (void) my_hash_insert(&hash, org_key_pos); key_pos+=extra_length; } my_free((char*) key_buffer,MYF(0)); diff --git a/strings/ctype-simple.c b/strings/ctype-simple.c index c2a6aa4e17f..080e0b780b7 100644 --- a/strings/ctype-simple.c +++ b/strings/ctype-simple.c @@ -518,7 +518,6 @@ longlong my_strntoll_8bit(CHARSET_INFO *cs __attribute__((unused)), register unsigned int cutlim; register ulonglong i; register const char *s, *e; - register unsigned char c; const char *save; int overflow; @@ -581,8 +580,9 @@ longlong my_strntoll_8bit(CHARSET_INFO *cs __attribute__((unused)), overflow = 0; i = 0; - for (c = *s; s != e; c = *++s) + for ( ; s != e; s++) { + register unsigned char c= *s; if (c>='0' && c<='9') c -= '0'; else if (c>='A' && c<='Z') @@ -641,7 +641,6 @@ ulonglong my_strntoull_8bit(CHARSET_INFO *cs, register unsigned int cutlim; register ulonglong i; register const char *s, *e; - register unsigned char c; const char *save; int overflow; @@ -704,8 +703,10 @@ ulonglong my_strntoull_8bit(CHARSET_INFO *cs, overflow = 0; i = 0; - for (c = *s; s != e; c = *++s) + for ( ; s != e; s++) { + register unsigned char c= *s; + if (c>='0' && c<='9') c -= '0'; else if (c>='A' && c<='Z') From d4c2c74724b17d5f09f6f6bd110bb65258037722 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 28 Feb 2005 16:05:18 +0300 Subject: [PATCH 26/42] portability fix --- server-tools/instance-manager/priv.h | 1 + 1 file changed, 1 insertion(+) diff --git a/server-tools/instance-manager/priv.h b/server-tools/instance-manager/priv.h index 5bee8aa1463..decc3605dff 100644 --- a/server-tools/instance-manager/priv.h +++ b/server-tools/instance-manager/priv.h @@ -16,6 +16,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include From 7a703d6acee84b845184f86e7d50865967308c77 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 28 Feb 2005 16:20:02 +0200 Subject: [PATCH 27/42] Fix for BUG#8532. The problem was in that the code that analyses the applicability of the QUICK_GROUP_MIN_MAX access method for DISTINC queries assumed that there are no duplicate column references in the DISTINCT clause, and it added non-exiting key parts for the duplicate column references. The solution adds a test to check whether the select list already contained a field with the same name. If such field was already present, then it was already decided to use its key part for index access. In this such case we must skip the duplicate field instead of counting it as a new field. mysql-test/r/group_min_max.result: Added test results for BUG#8532. mysql-test/t/group_min_max.test: Added tests for BUG#8532. sql/opt_range.cc: The problem was in that the code that analyses the applicability of the QUICK_GROUP_MIN_MAX access method for DISTINC queries assumed that there are no duplicate column references in the DISTINCT clause, and it added non-exiting key parts for the duplicate column references. The solution adds a test to check whether the select list already contained a field with the same name. If such field was already present, then it was already decided to use its key part for index access. In this such case we must skip the duplicate field instead of counting it as a new field. --- mysql-test/r/group_min_max.result | 38 +++++++++++++++++++++++++++++++ mysql-test/t/group_min_max.test | 6 ++++- sql/opt_range.cc | 11 ++++++++- 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/group_min_max.result b/mysql-test/r/group_min_max.result index c7be93b0fd7..766e43b2299 100644 --- a/mysql-test/r/group_min_max.result +++ b/mysql-test/r/group_min_max.result @@ -1657,6 +1657,44 @@ a b c d +select distinct a1,a1 from t1; +a1 a1 +a a +b b +c c +d d +select distinct a2,a1,a2,a1 from t1; +a2 a1 a2 a1 +a a a a +b a b a +a b a b +b b b b +a c a c +b c b c +a d a d +b d b d +select distinct t1.a1,t2.a1 from t1,t2; +a1 a1 +a a +b a +c a +d a +a b +b b +c b +d b +a c +b c +c c +d c +a d +b d +c d +d d +a e +b e +c e +d e explain select distinct a1,a2,b from t1; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 range NULL idx_t1_1 147 NULL 17 Using index for group-by diff --git a/mysql-test/t/group_min_max.test b/mysql-test/t/group_min_max.test index b42125566d5..d85b3395853 100644 --- a/mysql-test/t/group_min_max.test +++ b/mysql-test/t/group_min_max.test @@ -475,11 +475,15 @@ select distinct a1,a2,b,c from t2 where (a2 >= 'b') and (b = 'a') and (c = 'i121 select distinct a1,a2,b from t2 where (a1 > 'a') and (a2 > 'a') and (b = 'c'); select distinct b from t2 where (a2 >= 'b') and (b = 'a'); --- BUG 6303 +-- BUG #6303 select distinct t_00.a1 from t1 t_00 where exists ( select * from t2 where a1 = t_00.a1 ); +-- BUG #8532 - SELECT DISTINCT a, a causes server to crash +select distinct a1,a1 from t1; +select distinct a2,a1,a2,a1 from t1; +select distinct t1.a1,t2.a1 from t1,t2; -- -- DISTINCT queries with GROUP-BY diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 12e5c60312b..812d5a41cbc 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -6878,6 +6878,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree) SEL_ARG *cur_index_tree= NULL; ha_rows cur_quick_prefix_records= 0; uint cur_param_idx; + key_map cur_used_key_parts; for (uint cur_index= 0 ; cur_index_info != cur_index_info_end ; cur_index_info++, cur_index++) @@ -6925,17 +6926,25 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree) else if (join->select_distinct) { select_items_it.rewind(); + cur_used_key_parts.clear_all(); while ((item= select_items_it++)) { item_field= (Item_field*) item; /* (SA5) already checked above. */ /* Find the order of the key part in the index. */ key_part_nr= get_field_keypart(cur_index_info, item_field->field); + /* + Check if this attribute was already present in the select list. + If it was present, then its corresponding key part was alredy used. + */ + if (cur_used_key_parts.is_set(key_part_nr)) + continue; if (key_part_nr < 1 || key_part_nr > join->fields_list.elements) goto next_index; cur_part= cur_index_info->key_part + key_part_nr - 1; cur_group_prefix_len+= cur_part->store_length; + cur_used_key_parts.set_bit(key_part_nr); + ++cur_group_key_parts; } - cur_group_key_parts= join->fields_list.elements; } else DBUG_ASSERT(FALSE); From cfff7e6e184ab70b262ab36eaf8bee07746f87aa Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 28 Feb 2005 16:34:02 +0100 Subject: [PATCH 28/42] Fixed BUG#7646: Stored procedure hang if show binlog events Return false from show_binlog_events() if successful, otherwise stored procedures will think it failed. sql/sql_repl.cc: Return false from show_binlog_events() if successful, otherwise stored procedures will think it failed. --- sql/sql_repl.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 3b4e822a3df..80c7dba8f13 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -1274,6 +1274,7 @@ bool mysql_show_binlog_events(THD* thd) DBUG_ENTER("show_binlog_events"); List field_list; const char *errmsg = 0; + bool ret = TRUE; IO_CACHE log; File file = -1; Format_description_log_event *description_event= new @@ -1376,6 +1377,8 @@ bool mysql_show_binlog_events(THD* thd) pthread_mutex_unlock(log_lock); } + ret= FALSE; + err: delete description_event; if (file >= 0) @@ -1395,7 +1398,7 @@ err: pthread_mutex_lock(&LOCK_thread_count); thd->current_linfo = 0; pthread_mutex_unlock(&LOCK_thread_count); - DBUG_RETURN(TRUE); + DBUG_RETURN(ret); } From 110f6abd3800f0a3be9febc81e295966d2fb9f77 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 28 Feb 2005 18:07:06 +0100 Subject: [PATCH 29/42] Fixed BUG#8760: Stored Procedures: Invalid SQLSTATE is allowed in a DECLARE ? HANDLER FOR stmt. mysql-test/r/sp-error.result: New test case for BUG#8776 (check format of sqlstates in handler declarations). mysql-test/t/sp-error.test: New test case for BUG#8776 (check format of sqlstates in handler declarations). sql/share/errmsg.txt: New error message for malformed SQLSTATEs. sql/sp_pcontext.cc: Added function for checking SQLSTATE format. sql/sp_pcontext.h: Added function for checking SQLSTATE format. sql/sql_yacc.yy: Check format of SQLSTATE in handler declaration. --- mysql-test/r/sp-error.result | 28 ++++++++++++++++++++++++++ mysql-test/t/sp-error.test | 38 ++++++++++++++++++++++++++++++++++++ sql/share/errmsg.txt | 2 ++ sql/sp_pcontext.cc | 24 +++++++++++++++++++++++ sql/sp_pcontext.h | 6 ++++++ sql/sql_yacc.yy | 12 +++++++----- 6 files changed, 105 insertions(+), 5 deletions(-) diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index 1c2f4662ef1..1182c3d3569 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -466,4 +466,32 @@ ERROR 70100: Query execution was interrupted call bug6807()| ERROR 70100: Query execution was interrupted drop procedure bug6807| +drop procedure if exists bug8776_1| +drop procedure if exists bug8776_2| +drop procedure if exists bug8776_3| +drop procedure if exists bug8776_4| +create procedure bug8776_1() +begin +declare continue handler for sqlstate '42S0200test' begin end; +begin end; +end| +ERROR 42000: Bad SQLSTATE: '42S0200test' +create procedure bug8776_2() +begin +declare continue handler for sqlstate '4200' begin end; +begin end; +end| +ERROR 42000: Bad SQLSTATE: '4200' +create procedure bug8776_3() +begin +declare continue handler for sqlstate '420000' begin end; +begin end; +end| +ERROR 42000: Bad SQLSTATE: '420000' +create procedure bug8776_4() +begin +declare continue handler for sqlstate '42x00' begin end; +begin end; +end| +ERROR 42000: Bad SQLSTATE: '42x00' drop table t1| diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test index 594daf66fcb..0f775958d7a 100644 --- a/mysql-test/t/sp-error.test +++ b/mysql-test/t/sp-error.test @@ -641,6 +641,44 @@ call bug6807()| drop procedure bug6807| +# +# BUG#876: Stored Procedures: Invalid SQLSTATE is allowed in +# a DECLARE ? HANDLER FOR stmt. +# +--disable_warnings +drop procedure if exists bug8776_1| +drop procedure if exists bug8776_2| +drop procedure if exists bug8776_3| +drop procedure if exists bug8776_4| +--enable_warnings +--error ER_SP_BAD_SQLSTATE +create procedure bug8776_1() +begin + declare continue handler for sqlstate '42S0200test' begin end; + begin end; +end| + +--error ER_SP_BAD_SQLSTATE +create procedure bug8776_2() +begin + declare continue handler for sqlstate '4200' begin end; + begin end; +end| + +--error ER_SP_BAD_SQLSTATE +create procedure bug8776_3() +begin + declare continue handler for sqlstate '420000' begin end; + begin end; +end| + +--error ER_SP_BAD_SQLSTATE +create procedure bug8776_4() +begin + declare continue handler for sqlstate '42x00' begin end; + begin end; +end| + drop table t1| diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 5757510bfb4..ba4ef70486b 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5326,3 +5326,5 @@ ER_PROC_AUTO_REVOKE_FAIL eng "Failed to revoke all privileges to dropped routine" ER_DATA_TOO_LONG 22001 eng "Data too long for column '%s' at row %ld" +ER_SP_BAD_SQLSTATE 42000 + eng "Bad SQLSTATE: '%s'" diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc index 7176498f276..15d3f87ff29 100644 --- a/sql/sp_pcontext.cc +++ b/sql/sp_pcontext.cc @@ -26,6 +26,30 @@ #include "sp_pcontext.h" #include "sp_head.h" +/* + * Sanity check for SQLSTATEs. Will not check if it's really an existing + * state (there are just too many), but will check length and bad characters. + * Returns TRUE if it's ok, FALSE if it's bad. + */ +bool +sp_cond_check(LEX_STRING *sqlstate) +{ + int i; + const char *p; + + if (sqlstate->length != 5) + return FALSE; + for (p= sqlstate->str, i= 0 ; i < 5 ; i++) + { + char c = p[i]; + + if ((c < '0' || '9' < c) && + (c < 'A' || 'Z' < c)) + return FALSE; + } + return TRUE; +} + sp_pcontext::sp_pcontext(sp_pcontext *prev) : Sql_alloc(), m_psubsize(0), m_csubsize(0), m_hsubsize(0), m_handlers(0), m_parent(prev) diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h index 66f631f4938..42d8140b78c 100644 --- a/sql/sp_pcontext.h +++ b/sql/sp_pcontext.h @@ -60,6 +60,12 @@ typedef struct sp_cond_type uint mysqlerr; } sp_cond_type_t; +/* Sanity check for SQLSTATEs. Will not check if it's really an existing + * state (there are just too many), but will check length bad characters. + */ +extern bool +sp_cond_check(LEX_STRING *sqlstate); + typedef struct sp_cond { LEX_STRING name; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 45c3e94f0ff..a69b6a96982 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1760,13 +1760,15 @@ sp_cond: } | SQLSTATE_SYM opt_value TEXT_STRING_literal { /* SQLSTATE */ - uint len= ($3.length < sizeof($$->sqlstate)-1 ? - $3.length : sizeof($$->sqlstate)-1); - + if (!sp_cond_check(&$3)) + { + my_error(ER_SP_BAD_SQLSTATE, MYF(0), $3.str); + YYABORT; + } $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); $$->type= sp_cond_type_t::state; - memcpy($$->sqlstate, $3.str, len); - $$->sqlstate[len]= '\0'; + memcpy($$->sqlstate, $3.str, 5); + $$->sqlstate[5]= '\0'; } ; From d2ead18b69716bdd1e5610e666e26d1ae1d72197 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 28 Feb 2005 20:21:21 +0300 Subject: [PATCH 30/42] Fix for BUG#8726: In JOIN::optimize on ER_TOO_BIG_SELECT error set JOIN::error to -1 to cause the error to be sent to the client. mysql-test/r/select_safe.result: Testcase for BUG#8726 mysql-test/t/select_safe.test: Testcase for BUG#8726 sql/sql_select.cc: Fix for BUG#8726: On ER_TOO_BIG_SELECT set JOIN::error to -1 to cause the error to be sent to the client. --- mysql-test/r/select_safe.result | 15 +++++++++++++++ mysql-test/t/select_safe.test | 20 ++++++++++++++++++++ sql/sql_select.cc | 2 +- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/select_safe.result b/mysql-test/r/select_safe.result index 766ee8c0e14..7a29db42dd9 100644 --- a/mysql-test/r/select_safe.result +++ b/mysql-test/r/select_safe.result @@ -78,4 +78,19 @@ id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t2 ref b b 21 test.t1.b 6 Using where SET MAX_SEEKS_FOR_KEY=DEFAULT; drop table t1; +create table t1 (a int); +insert into t1 values (1),(2),(3),(4),(5); +insert into t1 select * from t1; +insert into t1 select * from t1; +insert into t1 select * from t1; +set local max_join_size=8; +select * from (select * from t1) x; +ERROR 42000: The SELECT would examine more than MAX_JOIN_SIZE rows; check your WHERE and use SET SQL_BIG_SELECTS=1 or SET SQL_MAX_JOIN_SIZE=# if the SELECT is okay +set local max_join_size=1; +select * from (select * from t1 a, t1 b) x; +ERROR 42000: The SELECT would examine more than MAX_JOIN_SIZE rows; check your WHERE and use SET SQL_BIG_SELECTS=1 or SET SQL_MAX_JOIN_SIZE=# if the SELECT is okay +set local max_join_size=1; +select * from (select 1 union select 2 union select 3) x; +ERROR 42000: The SELECT would examine more than MAX_JOIN_SIZE rows; check your WHERE and use SET SQL_BIG_SELECTS=1 or SET SQL_MAX_JOIN_SIZE=# if the SELECT is okay +drop table t1; SET SQL_SAFE_UPDATES=0,SQL_SELECT_LIMIT=DEFAULT, SQL_MAX_JOIN_SIZE=DEFAULT; diff --git a/mysql-test/t/select_safe.test b/mysql-test/t/select_safe.test index 3cafd31a879..5b2dfb00bb7 100644 --- a/mysql-test/t/select_safe.test +++ b/mysql-test/t/select_safe.test @@ -66,4 +66,24 @@ SET MAX_SEEKS_FOR_KEY=DEFAULT; drop table t1; +# BUG#8726 +create table t1 (a int); +insert into t1 values (1),(2),(3),(4),(5); +insert into t1 select * from t1; +insert into t1 select * from t1; +insert into t1 select * from t1; + +set local max_join_size=8; +--error 1104 +select * from (select * from t1) x; + +set local max_join_size=1; +--error 1104 +select * from (select * from t1 a, t1 b) x; + +set local max_join_size=1; +--error 1104 +select * from (select 1 union select 2 union select 3) x; +drop table t1; + SET SQL_SAFE_UPDATES=0,SQL_SELECT_LIMIT=DEFAULT, SQL_MAX_JOIN_SIZE=DEFAULT; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index a210fbbbe02..d207d50e306 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -579,7 +579,7 @@ JOIN::optimize() !(select_options & SELECT_DESCRIBE)) { /* purecov: inspected */ my_message(ER_TOO_BIG_SELECT, ER(ER_TOO_BIG_SELECT), MYF(0)); - error= 1; /* purecov: inspected */ + error= -1; DBUG_RETURN(1); } if (const_tables && !thd->locked_tables && From e99c035ffaf8c01bf4d848ddf10219103fc6a314 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 1 Mar 2005 01:38:04 +0300 Subject: [PATCH 31/42] fix compilation failure on sunfire280 --- mysys/errors.c | 1 - 1 file changed, 1 deletion(-) diff --git a/mysys/errors.c b/mysys/errors.c index 75a2c7048a0..4472b7173fa 100644 --- a/mysys/errors.c +++ b/mysys/errors.c @@ -50,7 +50,6 @@ const char * NEAR globerrs[GLOBERRS]= "Can't sync file '%s' to disk (Errcode: %d)", "Collation '%s' is not a compiled collation and is not specified in the '%s' file", "File '%s' not found (Errcode: %d)", - "" }; void init_glob_errs(void) From ea24940fbaa9f4e55f41843c2441fad7c2af5689 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 1 Mar 2005 08:17:03 +0200 Subject: [PATCH 32/42] Fixed XA recovery for InnoDB. Note that XA recovery is still disabled until it has been comprehensive tested. innobase/log/log0log.c: Added general documentation of InnoDB redo-logs. innobase/trx/trx0roll.c: Prepared transactions are not rolled back in a recovery if innobase_force_recovery = 0. But they are rolled back if innobase_force_recovery > 0. innobase/trx/trx0trx.c: Disable the XA code in InnoDB crash recovery until it has been comprehensive tested. SHOW INNODB STATUS now prints different output for prepared transactions. innobase/trx/trx0undo.c: Do not unnecessary write X/Open XA XID. XID is written in the prepare. Space for a XID should be reserved at this stage. sql/ha_innodb.cc: Remove error. --- innobase/log/log0log.c | 26 ++++++++++++++++++++++++++ innobase/trx/trx0roll.c | 12 +++++++++++- innobase/trx/trx0trx.c | 28 +++++++++++++++------------- innobase/trx/trx0undo.c | 14 ++++++-------- sql/ha_innodb.cc | 3 --- 5 files changed, 58 insertions(+), 25 deletions(-) diff --git a/innobase/log/log0log.c b/innobase/log/log0log.c index 1ab91b71e8f..5915146b466 100644 --- a/innobase/log/log0log.c +++ b/innobase/log/log0log.c @@ -24,6 +24,32 @@ Created 12/9/1995 Heikki Tuuri #include "trx0sys.h" #include "trx0trx.h" +/* +General philosophy of InnoDB redo-logs: + +1) Every change to a contents of a data page must be done +through mtr, which in mtr_commit() writes log records +to the InnoDB redo log. + +2) Normally these changes are performed using a mlog_write_ulint() +or similar function. + +3) In some page level operations only a code number of a +c-function and its parameters are written to the log to +reduce the size of the log. + + 3a) You should not add parameters to these kind of functions + (e.g. trx_undo_header_create(), trx_undo_insert_header_reuse()) + + 3b) You should not add such functionality which either change + working when compared with the old or are dependent on data + outside of the page. These kind of functions should implement + self-contained page transformation and it should be unchanged + if you don't have very essential reasons to change log + semantics or format. + +*/ + /* Current free limit of space 0; protected by the log sys mutex; 0 means uninitialized */ ulint log_fsp_current_free_limit = 0; diff --git a/innobase/trx/trx0roll.c b/innobase/trx/trx0roll.c index 559c2cb5128..4c68e0a0dd3 100644 --- a/innobase/trx/trx0roll.c +++ b/innobase/trx/trx0roll.c @@ -440,7 +440,17 @@ loop: if ((trx->sess || (trx->conc_state == TRX_NOT_STARTED))) { trx = UT_LIST_GET_NEXT(trx_list, trx); } else if (trx->conc_state == TRX_PREPARED) { - trx->sess = trx_dummy_sess; + + /* Roll back all prepared transactions if + innobase_force_recovery > 0 in my.cnf */ + + if (srv_force_recovery > 0) { + trx->conc_state = TRX_ACTIVE; + break; + } else { + trx->sess = trx_dummy_sess; + trx = UT_LIST_GET_NEXT(trx_list, trx); + } } else { break; } diff --git a/innobase/trx/trx0trx.c b/innobase/trx/trx0trx.c index a33ca4b8ec3..344fe280711 100644 --- a/innobase/trx/trx0trx.c +++ b/innobase/trx/trx0trx.c @@ -435,14 +435,14 @@ trx_lists_init_at_db_start(void) if (undo->state == TRX_UNDO_PREPARED) { - fprintf(stderr, + fprintf(stderr, "InnoDB: Transaction %lu %lu was in the XA prepared state.\n", ut_dulint_get_high(trx->id), ut_dulint_get_low(trx->id)); - /* trx->conc_state = TRX_PREPARED; */ - trx->conc_state = - TRX_ACTIVE; + trx->conc_state = TRX_ACTIVE; + + /* trx->conc_state = TRX_PREPARED;*/ } else { trx->conc_state = TRX_COMMITTED_IN_MEMORY; @@ -498,16 +498,15 @@ trx_lists_init_at_db_start(void) commit or abort decision from MySQL */ if (undo->state == TRX_UNDO_PREPARED) { - - fprintf(stderr, + fprintf(stderr, "InnoDB: Transaction %lu %lu was in the XA prepared state.\n", - ut_dulint_get_high(trx->id), - ut_dulint_get_low(trx->id)); + ut_dulint_get_high(trx->id), + ut_dulint_get_low(trx->id)); - /* trx->conc_state = TRX_PREPARED; */ - trx->conc_state = - TRX_ACTIVE; + trx->conc_state = TRX_ACTIVE; + /* trx->conc_state = + TRX_PREPARED; */ } else { trx->conc_state = TRX_COMMITTED_IN_MEMORY; @@ -1638,10 +1637,13 @@ trx_print( fputs(", not started", f); break; case TRX_ACTIVE: - case TRX_PREPARED: fprintf(f, ", ACTIVE %lu sec", (ulong)difftime(time(NULL), trx->start_time)); break; + case TRX_PREPARED: + fprintf(f, ", ACTIVE (PREPARED) %lu sec", + (ulong)difftime(time(NULL), trx->start_time)); + break; case TRX_COMMITTED_IN_MEMORY: fputs(", COMMITTED IN MEMORY", f); break; @@ -1938,7 +1940,7 @@ trx_get_trx_by_xid( if (xid->gtrid_length == trx->xid.gtrid_length && xid->bqual_length == trx->xid.bqual_length && - memcmp(xid, &trx->xid, + memcmp(xid->data, trx->xid.data, xid->gtrid_length + xid->bqual_length) == 0) { break; diff --git a/innobase/trx/trx0undo.c b/innobase/trx/trx0undo.c index 545e1b77fee..88185973dfc 100644 --- a/innobase/trx/trx0undo.c +++ b/innobase/trx/trx0undo.c @@ -599,11 +599,10 @@ trx_undo_read_xid( Adds the XA XID after an undo log old-style header. */ static void -trx_undo_header_add_xid( -/*====================*/ +trx_undo_header_add_space_for_xid( +/*==============================*/ page_t* undo_page,/* in: undo log segment header page */ trx_ulogf_t* log_hdr,/* in: undo log header */ - XID* xid, /* in: X/Open XA transaction identification */ mtr_t* mtr) /* in: mtr */ { trx_upagef_t* page_hdr; @@ -620,9 +619,8 @@ trx_undo_header_add_xid( new_free = free + (TRX_UNDO_LOG_XA_HDR_SIZE - TRX_UNDO_LOG_OLD_HDR_SIZE); - trx_undo_write_xid(log_hdr, xid, mtr); - /* Now that we added the XID after the header, update the free offset + /* Add space for a XID after the header, update the free offset fields on the undo log page and in the undo log header */ mlog_write_ulint(page_hdr + TRX_UNDO_PAGE_START, new_free, @@ -1532,7 +1530,7 @@ trx_undo_create( offset = trx_undo_header_create(undo_page, trx_id, mtr); - trx_undo_header_add_xid(undo_page, undo_page + offset, xid, mtr); + trx_undo_header_add_space_for_xid(undo_page, undo_page + offset, mtr); undo = trx_undo_mem_create(rseg, id, type, trx_id, xid, page_no, offset); @@ -1599,7 +1597,7 @@ trx_undo_reuse_cached( if (type == TRX_UNDO_INSERT) { offset = trx_undo_insert_header_reuse(undo_page, trx_id, mtr); - trx_undo_header_add_xid(undo_page, undo_page + offset, xid, + trx_undo_header_add_space_for_xid(undo_page, undo_page + offset, mtr); } else { ut_a(mach_read_from_2(undo_page + TRX_UNDO_PAGE_HDR @@ -1607,7 +1605,7 @@ trx_undo_reuse_cached( == TRX_UNDO_UPDATE); offset = trx_undo_header_create(undo_page, trx_id, mtr); - trx_undo_header_add_xid(undo_page, undo_page + offset, xid, + trx_undo_header_add_space_for_xid(undo_page, undo_page + offset, mtr); } diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index f68ad99ac44..b733af7774e 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -3071,9 +3071,6 @@ ha_innobase::unlock_row(void) DBUG_ENTER("ha_innobase::unlock_row"); - ut_ad(prebuilt->trx == - (trx_t*) current_thd->transaction.all.innobase_tid); - if (last_query_id != user_thd->query_id) { ut_print_timestamp(stderr); fprintf(stderr, From 679088a2ae1353f14a970182fa64f58170e81b72 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 1 Mar 2005 14:54:47 +0300 Subject: [PATCH 33/42] Fix for Bug#8801: the bug was in co-operation of Item_ref with view-merge algorithm and prepared statements: in case when some Item_ref pointing to a view column was substituted with a reference pointing to the view expression for that column Item_ref::ref member of the original Item_ref was left pointing to not_found_item (0x1). As we currently perform expression substition part of the view-merge algorithm per each execution of a prepared statement or stored procedure, we need to preserve original Item_ref objects usable. sql/item.cc: Set member Item_ref::ref to null whenever the item itself is substituted with another item. This is necessary if we want to re-execute a prepared statement next time. Additionally Item_ref::fix_fields() implementation was cleaned up (by Monty and myself) to reduce the number of if branches. This doesn't change the logic of this function. --- sql/item.cc | 301 +++++++++++++++++++++++++--------------------------- 1 file changed, 146 insertions(+), 155 deletions(-) diff --git a/sql/item.cc b/sql/item.cc index 07d5200784d..2e8b16a4f9d 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -3513,6 +3513,9 @@ Item_ref::Item_ref(Item **item, const char *table_name_par, Item_field::fix_fields, here we first search the SELECT and GROUP BY clauses, and then we search the FROM clause. + POSTCONDITION + Item_ref::ref is 0 or points to a valid item + RETURN TRUE if error FALSE on success @@ -3534,168 +3537,155 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) if (ref == not_found_item) /* This reference was not resolved. */ { - /* - If there is an outer select, and it is not a derived table (which do - not support the use of outer fields for now), try to resolve this - reference in the outer select(s). - - We treat each subselect as a separate namespace, so that different - subselects may contain columns with the same names. The subselects are - searched starting from the innermost. - */ - if (outer_sel && (current_sel->master_unit()->first_select()->linkage != + TABLE_LIST *table_list; + Field *from_field; + SELECT_LEX *last; + ref= 0; + + if (!outer_sel || (current_sel->master_unit()->first_select()->linkage == DERIVED_TABLE_TYPE)) - { - TABLE_LIST *table_list; - Field *from_field= (Field*) not_found_field; - SELECT_LEX *last= 0; - - for ( ; outer_sel ; - outer_sel= (prev_unit= outer_sel->master_unit())->outer_select()) - { - last= outer_sel; - Item_subselect *prev_subselect_item= prev_unit->item; - - /* Search in the SELECT and GROUP lists of the outer select. */ - if (outer_sel->resolve_mode == SELECT_LEX::SELECT_MODE) - { - if (!(ref= resolve_ref_in_select_and_group(thd, this, outer_sel))) - return TRUE; /* Some error occurred (e.g. ambiguous names). */ - if (ref != not_found_item) - { - DBUG_ASSERT(*ref && (*ref)->fixed); - prev_subselect_item->used_tables_cache|= (*ref)->used_tables(); - prev_subselect_item->const_item_cache&= (*ref)->const_item(); - break; - } - } - - /* Search in the tables of the FROM clause of the outer select. */ - table_list= outer_sel->get_table_list(); - if (outer_sel->resolve_mode == SELECT_LEX::INSERT_MODE && table_list) - /* - It is a primary INSERT st_select_lex => do not resolve against the - first table. - */ - table_list= table_list->next_local; - - place= prev_subselect_item->parsing_place; - /* - Check table fields only if the subquery is used somewhere out of - HAVING or the outer SELECT does not use grouping (i.e. tables are - accessible). - TODO: - Here we could first find the field anyway, and then test this - condition, so that we can give a better error message - - ER_WRONG_FIELD_WITH_GROUP, instead of the less informative - ER_BAD_FIELD_ERROR which we produce now. - */ - if ((place != IN_HAVING || - (!outer_sel->with_sum_func && - outer_sel->group_list.elements == 0))) - { - /* - In case of view, find_field_in_tables() write pointer to view - field expression to 'reference', i.e. it substitute that - expression instead of this Item_ref - */ - if ((from_field= find_field_in_tables(thd, this, table_list, - reference, - IGNORE_EXCEPT_NON_UNIQUE, - TRUE)) != - not_found_field) - { - if (from_field != view_ref_found) - { - prev_subselect_item->used_tables_cache|= from_field->table->map; - prev_subselect_item->const_item_cache= 0; - } - else - { - Item::Type type= (*reference)->type(); - prev_subselect_item->used_tables_cache|= - (*reference)->used_tables(); - prev_subselect_item->const_item_cache&= - (*reference)->const_item(); - DBUG_ASSERT((*reference)->type() == REF_ITEM); - mark_as_dependent(thd, last, current_sel, this, - ((type == REF_ITEM || type == FIELD_ITEM) ? - (Item_ident*) (*reference) : - 0)); - /* - view reference found, we substituted it instead of this - Item, so can quit - */ - return FALSE; - } - break; - } - } - - /* Reference is not found => depend on outer (or just error). */ - prev_subselect_item->used_tables_cache|= OUTER_REF_TABLE_BIT; - prev_subselect_item->const_item_cache= 0; - - if (outer_sel->master_unit()->first_select()->linkage == - DERIVED_TABLE_TYPE) - break; /* Do not consider derived tables. */ - } - - DBUG_ASSERT(ref != 0); - if (!from_field) - return TRUE; - if (ref == not_found_item && from_field == not_found_field) - { - my_error(ER_BAD_FIELD_ERROR, MYF(0), - this->full_name(), current_thd->where); - ref= 0; // Safety - return TRUE; - } - if (from_field != not_found_field) - { - /* - Set ref to 0 as we are replacing this item with the found item and - this will ensure we get an error if this item would be used - elsewhere - */ - ref= 0; // Safety - if (from_field != view_ref_found) - { - Item_field* fld; - if (!(fld= new Item_field(from_field))) - return TRUE; - thd->change_item_tree(reference, fld); - mark_as_dependent(thd, last, thd->lex->current_select, this, fld); - return FALSE; - } - /* - We can leave expression substituted from view for next PS/SP - re-execution (i.e. do not register this substitution for reverting - on cleanup() (register_item_tree_changing())), because this subtree - will be fix_field'ed during setup_tables()->setup_ancestor() - (i.e. before all other expressions of query, and references on - tables which do not present in query will not make problems. - - Also we suppose that view can't be changed during PS/SP life. - */ - } - else - { - /* Should be checked in resolve_ref_in_select_and_group(). */ - DBUG_ASSERT(*ref && (*ref)->fixed); - mark_as_dependent(thd, last, current_sel, this, this); - } - } - else { /* The current reference cannot be resolved in this query. */ my_error(ER_BAD_FIELD_ERROR,MYF(0), this->full_name(), current_thd->where); return TRUE; } + /* + If there is an outer select, and it is not a derived table (which do + not support the use of outer fields for now), try to resolve this + reference in the outer select(s). + + We treat each subselect as a separate namespace, so that different + subselects may contain columns with the same names. The subselects are + searched starting from the innermost. + */ + from_field= (Field*) not_found_field; + last= 0; + + /* The following loop will always be excuted at least once */ + for ( ; outer_sel ; + outer_sel= (prev_unit= outer_sel->master_unit())->outer_select()) + { + last= outer_sel; + Item_subselect *prev_subselect_item= prev_unit->item; + + /* Search in the SELECT and GROUP lists of the outer select. */ + if (outer_sel->resolve_mode == SELECT_LEX::SELECT_MODE) + { + if (!(ref= resolve_ref_in_select_and_group(thd, this, outer_sel))) + return TRUE; /* Some error occurred (e.g. ambiguous names). */ + if (ref != not_found_item) + { + DBUG_ASSERT(*ref && (*ref)->fixed); + prev_subselect_item->used_tables_cache|= (*ref)->used_tables(); + prev_subselect_item->const_item_cache&= (*ref)->const_item(); + break; + } + /* + Set ref to 0 to ensure that we get an error in case we replaced + this item with another item and still use this item in some + other place of the parse tree. + */ + ref= 0; + } + + /* Search in the tables of the FROM clause of the outer select. */ + table_list= outer_sel->get_table_list(); + if (outer_sel->resolve_mode == SELECT_LEX::INSERT_MODE && table_list) + { + /* + It is a primary INSERT st_select_lex => do not resolve against + the first table. + */ + table_list= table_list->next_local; + } + + place= prev_subselect_item->parsing_place; + /* + Check table fields only if the subquery is used somewhere out of + HAVING or the outer SELECT does not use grouping (i.e. tables are + accessible). + TODO: + Here we could first find the field anyway, and then test this + condition, so that we can give a better error message - + ER_WRONG_FIELD_WITH_GROUP, instead of the less informative + ER_BAD_FIELD_ERROR which we produce now. + */ + if ((place != IN_HAVING || + (!outer_sel->with_sum_func && + outer_sel->group_list.elements == 0))) + { + /* + In case of view, find_field_in_tables() write pointer to view + field expression to 'reference', i.e. it substitute that + expression instead of this Item_ref + */ + from_field= find_field_in_tables(thd, this, table_list, + reference, + IGNORE_EXCEPT_NON_UNIQUE, + TRUE); + if (! from_field) + return TRUE; + if (from_field == view_ref_found) + { + Item::Type type= (*reference)->type(); + prev_subselect_item->used_tables_cache|= + (*reference)->used_tables(); + prev_subselect_item->const_item_cache&= + (*reference)->const_item(); + DBUG_ASSERT((*reference)->type() == REF_ITEM); + mark_as_dependent(thd, last, current_sel, this, + ((type == REF_ITEM || type == FIELD_ITEM) ? + (Item_ident*) (*reference) : + 0)); + /* + view reference found, we substituted it instead of this + Item, so can quit + */ + return FALSE; + } + if (from_field != not_found_field) + { + prev_subselect_item->used_tables_cache|= from_field->table->map; + prev_subselect_item->const_item_cache= 0; + break; + } + } + DBUG_ASSERT(from_field == not_found_field); + + /* Reference is not found => depend on outer (or just error). */ + prev_subselect_item->used_tables_cache|= OUTER_REF_TABLE_BIT; + prev_subselect_item->const_item_cache= 0; + + if (outer_sel->master_unit()->first_select()->linkage == + DERIVED_TABLE_TYPE) + break; /* Do not consider derived tables. */ + } + + DBUG_ASSERT(from_field != 0 && from_field != view_ref_found); + if (from_field != not_found_field) + { + Item_field* fld; + if (!(fld= new Item_field(from_field))) + return TRUE; + thd->change_item_tree(reference, fld); + mark_as_dependent(thd, last, thd->lex->current_select, this, fld); + return FALSE; + } + if (ref == 0) + { + /* The item was not a table field and not a reference */ + my_error(ER_BAD_FIELD_ERROR, MYF(0), + this->full_name(), current_thd->where); + return TRUE; + } + /* Should be checked in resolve_ref_in_select_and_group(). */ + DBUG_ASSERT(*ref && (*ref)->fixed); + mark_as_dependent(thd, last, current_sel, this, this); } } + DBUG_ASSERT(*ref); /* Check if this is an incorrect reference in a group function or forward reference. Do not issue an error if this is an unnamed reference inside an @@ -3716,11 +3706,12 @@ bool Item_ref::fix_fields(THD *thd, TABLE_LIST *tables, Item **reference) set_properties(); - if (ref && (*ref)->check_cols(1)) - return 1; - return 0; + if ((*ref)->check_cols(1)) + return TRUE; + return FALSE; } + void Item_ref::set_properties() { max_length= (*ref)->max_length; From b9b69440c10296bce67bbdaab2c4dbc0d9ba9243 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 1 Mar 2005 13:54:48 +0200 Subject: [PATCH 34/42] InnoDB: Zero fill newly created pages and deleted records to remove old junk and to improve compression ratio. InnoDB: Make implicit type conversions explicit. (Bug #8826) innobase/btr/btr0cur.c: Add "offsets" parameter to page_cur_delete_rec() calls btr_cur_optimistic_delete(): Simplify the logic with a flag variable btr_cur_pessimistic_delete(): Compute "offsets" earlier innobase/include/page0cur.h: page_cur_delete_rec(): Add parameter "offsets" innobase/include/page0page.h: page_mem_free(): Replace parameter "index" with "offsets" innobase/include/page0page.ic: page_mem_free(): Replace parameter "index" with "offsets". Clear the data bytes of the freed record. (The "extra" bytes will be needed by free space management.) innobase/include/rem0rec.h: Remove unnecessary function rec_get_size() innobase/log/log0recv.c: Remove function rec_apply_log_recs_for_backup() unless #ifdef UNIV_HOTBACKUP. innobase/page/page0cur.c: Add parameter "offsets" to page_cur_delete_rec(). innobase/page/page0page.c: page_create(): Zero fill the data area. page_delete_rec_list_start(): Add parameter "offsets" to page_cur_delete_rec(). innobase/rem/rem0rec.c: Remove unnecessary function rec_get_size(). Fix compiler warnings about implicit type conversions. (Bug #8826) innobase/srv/srv0srv.c: Fix compiler warnings about implicit type conversions. (Bug #8826) innobase/sync/sync0sync.c: Fix compiler warnings about implicit type conversions. (Bug #8826) --- innobase/btr/btr0cur.c | 36 ++++++++++++++++++----------------- innobase/include/page0cur.h | 7 ++++--- innobase/include/page0page.h | 2 +- innobase/include/page0page.ic | 12 ++++++++++-- innobase/include/rem0rec.h | 9 --------- innobase/log/log0recv.c | 4 +++- innobase/page/page0cur.c | 17 ++++++++++++++--- innobase/page/page0page.c | 29 ++++++++++++++++++++-------- innobase/rem/rem0rec.c | 31 ++++-------------------------- innobase/srv/srv0srv.c | 11 +++++++---- innobase/sync/sync0sync.c | 4 ++-- 11 files changed, 85 insertions(+), 77 deletions(-) diff --git a/innobase/btr/btr0cur.c b/innobase/btr/btr0cur.c index 8559df16d08..91ba47224ef 100644 --- a/innobase/btr/btr0cur.c +++ b/innobase/btr/btr0cur.c @@ -1642,7 +1642,7 @@ btr_cur_optimistic_update( btr_search_update_hash_on_delete(cursor); - page_cur_delete_rec(page_cursor, index, mtr); + page_cur_delete_rec(page_cursor, index, offsets, mtr); page_cur_move_to_prev(page_cursor); @@ -1885,7 +1885,7 @@ btr_cur_pessimistic_update( btr_search_update_hash_on_delete(cursor); - page_cur_delete_rec(page_cursor, index, mtr); + page_cur_delete_rec(page_cursor, index, offsets, mtr); page_cur_move_to_prev(page_cursor); @@ -2401,6 +2401,7 @@ btr_cur_optimistic_delete( mem_heap_t* heap = NULL; ulint offsets_[100] = { 100, }; ulint* offsets = offsets_; + ibool no_compress_needed; ut_ad(mtr_memo_contains(mtr, buf_block_align(btr_cur_get_page(cursor)), MTR_MEMO_PAGE_X_FIX)); @@ -2414,9 +2415,11 @@ btr_cur_optimistic_delete( offsets = rec_get_offsets(rec, cursor->index, offsets, ULINT_UNDEFINED, &heap); - if (!rec_offs_any_extern(offsets) + no_compress_needed = !rec_offs_any_extern(offsets) && btr_cur_can_delete_without_compress( - cursor, rec_offs_size(offsets), mtr)) { + cursor, rec_offs_size(offsets), mtr); + + if (no_compress_needed) { lock_update_delete(rec); @@ -2425,20 +2428,17 @@ btr_cur_optimistic_delete( max_ins_size = page_get_max_insert_size_after_reorganize(page, 1); page_cur_delete_rec(btr_cur_get_page_cur(cursor), - cursor->index, mtr); + cursor->index, offsets, mtr); ibuf_update_free_bits_low(cursor->index, page, max_ins_size, mtr); - if (heap) { - mem_heap_free(heap); - } - return(TRUE); } if (heap) { mem_heap_free(heap); } - return(FALSE); + + return(no_compress_needed); } /***************************************************************** @@ -2478,6 +2478,7 @@ btr_cur_pessimistic_delete( ibool success; ibool ret = FALSE; mem_heap_t* heap; + ulint* offsets; page = btr_cur_get_page(cursor); tree = btr_cur_get_tree(cursor); @@ -2503,20 +2504,20 @@ btr_cur_pessimistic_delete( } } - heap = mem_heap_create(256); + heap = mem_heap_create(1024); rec = btr_cur_get_rec(cursor); + offsets = rec_get_offsets(rec, cursor->index, + NULL, ULINT_UNDEFINED, &heap); + /* Free externally stored fields if the record is neither a node pointer nor in two-byte format. - This avoids unnecessary calls to rec_get_offsets(). */ + This avoids an unnecessary loop. */ if (cursor->index->table->comp ? !rec_get_node_ptr_flag(rec) : !rec_get_1byte_offs_flag(rec)) { btr_rec_free_externally_stored_fields(cursor->index, - rec, rec_get_offsets(rec, cursor->index, - NULL, ULINT_UNDEFINED, &heap), - in_rollback, mtr); - mem_heap_empty(heap); + rec, offsets, in_rollback, mtr); } if ((page_get_n_recs(page) < 2) @@ -2568,7 +2569,8 @@ btr_cur_pessimistic_delete( btr_search_update_hash_on_delete(cursor); - page_cur_delete_rec(btr_cur_get_page_cur(cursor), cursor->index, mtr); + page_cur_delete_rec(btr_cur_get_page_cur(cursor), cursor->index, + offsets, mtr); ut_ad(btr_check_node_ptr(tree, page, mtr)); diff --git a/innobase/include/page0cur.h b/innobase/include/page0cur.h index 7c6ff081c6d..4fc62f37db7 100644 --- a/innobase/include/page0cur.h +++ b/innobase/include/page0cur.h @@ -182,9 +182,10 @@ next record after the deleted one. */ void page_cur_delete_rec( /*================*/ - page_cur_t* cursor, /* in: a page cursor */ - dict_index_t* index, /* in: record descriptor */ - mtr_t* mtr); /* in: mini-transaction handle */ + page_cur_t* cursor, /* in: a page cursor */ + dict_index_t* index, /* in: record descriptor */ + const ulint* offsets,/* in: rec_get_offsets(cursor->rec, index) */ + mtr_t* mtr); /* in: mini-transaction handle */ /******************************************************************** Searches the right position for a page cursor. */ UNIV_INLINE diff --git a/innobase/include/page0page.h b/innobase/include/page0page.h index d3ef8214eb6..144c297b811 100644 --- a/innobase/include/page0page.h +++ b/innobase/include/page0page.h @@ -528,7 +528,7 @@ page_mem_free( /*==========*/ page_t* page, /* in: index page */ rec_t* rec, /* in: pointer to the (origin of) record */ - dict_index_t* index); /* in: record descriptor */ + const ulint* offsets);/* in: array returned by rec_get_offsets() */ /************************************************************** The index page creation function. */ diff --git a/innobase/include/page0page.ic b/innobase/include/page0page.ic index a63b5ca4238..e3e93e9f756 100644 --- a/innobase/include/page0page.ic +++ b/innobase/include/page0page.ic @@ -777,20 +777,28 @@ page_mem_free( /*==========*/ page_t* page, /* in: index page */ rec_t* rec, /* in: pointer to the (origin of) record */ - dict_index_t* index) /* in: record descriptor */ + const ulint* offsets)/* in: array returned by rec_get_offsets() */ { rec_t* free; ulint garbage; + ut_ad(rec_offs_validate(rec, NULL, offsets)); free = page_header_get_ptr(page, PAGE_FREE); page_rec_set_next(rec, free); page_header_set_ptr(page, PAGE_FREE, rec); + /* Clear the data bytes of the deleted record in order to improve + the compression ratio of the page and to make it easier to read + page dumps in corruption reports. The extra bytes of the record + cannot be cleared, because page_mem_alloc() needs them in order + to determine the size of the deleted record. */ + memset(rec, 0, rec_offs_data_size(offsets)); + garbage = page_header_get_field(page, PAGE_GARBAGE); page_header_set_field(page, PAGE_GARBAGE, - garbage + rec_get_size(rec, index)); + garbage + rec_offs_size(offsets)); } #ifdef UNIV_MATERIALIZE diff --git a/innobase/include/rem0rec.h b/innobase/include/rem0rec.h index c430def684a..6721fa85f16 100644 --- a/innobase/include/rem0rec.h +++ b/innobase/include/rem0rec.h @@ -435,15 +435,6 @@ rec_offs_size( /* out: size */ const ulint* offsets);/* in: array returned by rec_get_offsets() */ /************************************************************** -Returns the total size of a physical record. */ - -ulint -rec_get_size( -/*=========*/ - /* out: size */ - rec_t* rec, /* in: physical record */ - dict_index_t* index); /* in: record descriptor */ -/************************************************************** Returns a pointer to the start of the record. */ UNIV_INLINE byte* diff --git a/innobase/log/log0recv.c b/innobase/log/log0recv.c index 40a7deee604..e1869a171b5 100644 --- a/innobase/log/log0recv.c +++ b/innobase/log/log0recv.c @@ -1435,9 +1435,10 @@ loop: mutex_exit(&(recv_sys->mutex)); } +#ifdef UNIV_HOTBACKUP /* This page is allocated from the buffer pool and used in the function below */ -page_t* recv_backup_application_page = NULL; +static page_t* recv_backup_application_page = NULL; /*********************************************************************** Applies log records in the hash table to a backup. */ @@ -1559,6 +1560,7 @@ skip_this_recv_addr: recv_sys_empty_hash(); } +#endif /* UNIV_HOTBACKUP */ #ifdef notdefined /*********************************************************************** diff --git a/innobase/page/page0cur.c b/innobase/page/page0cur.c index 488d2757115..d083cc26069 100644 --- a/innobase/page/page0cur.c +++ b/innobase/page/page0cur.c @@ -1267,9 +1267,18 @@ page_cur_parse_delete_rec( ut_a(offset <= UNIV_PAGE_SIZE); if (page) { - page_cur_position(page + offset, &cursor); + mem_heap_t* heap = NULL; + ulint offsets_[100] = { 100, }; + rec_t* rec = page + offset; - page_cur_delete_rec(&cursor, index, mtr); + page_cur_position(rec, &cursor); + + page_cur_delete_rec(&cursor, index, + rec_get_offsets(rec, index, offsets_, + ULINT_UNDEFINED, &heap), mtr); + if (heap) { + mem_heap_free(heap); + } } return(ptr); @@ -1284,6 +1293,7 @@ page_cur_delete_rec( /*================*/ page_cur_t* cursor, /* in: a page cursor */ dict_index_t* index, /* in: record descriptor */ + const ulint* offsets,/* in: rec_get_offsets(cursor->rec, index) */ mtr_t* mtr) /* in: mini-transaction handle */ { page_dir_slot_t* cur_dir_slot; @@ -1300,6 +1310,7 @@ page_cur_delete_rec( page = page_cur_get_page(cursor); current_rec = cursor->rec; + ut_ad(rec_offs_validate(current_rec, index, offsets)); /* The record must not be the supremum or infimum record. */ ut_ad(current_rec != page_get_supremum_rec(page)); @@ -1365,7 +1376,7 @@ page_cur_delete_rec( page_dir_slot_set_n_owned(cur_dir_slot, cur_n_owned - 1); /* 6. Free the memory occupied by the record */ - page_mem_free(page, current_rec, index); + page_mem_free(page, current_rec, offsets); /* 7. Now we have decremented the number of owned records of the slot. If the number drops below PAGE_DIR_SLOT_MIN_N_OWNED, we balance the diff --git a/innobase/page/page0page.c b/innobase/page/page0page.c index 901c8cd0831..b393f0c0ad7 100644 --- a/innobase/page/page0page.c +++ b/innobase/page/page0page.c @@ -416,7 +416,7 @@ page_create( mem_heap_free(heap); - /* 4. INITIALIZE THE PAGE HEADER */ + /* 4. INITIALIZE THE PAGE */ page_header_set_field(page, PAGE_N_DIR_SLOTS, 2); page_header_set_ptr(page, PAGE_HEAP_TOP, heap_top); @@ -428,7 +428,9 @@ page_create( page_header_set_field(page, PAGE_N_DIRECTION, 0); page_header_set_field(page, PAGE_N_RECS, 0); page_set_max_trx_id(page, ut_dulint_zero); - + memset(heap_top, 0, UNIV_PAGE_SIZE - PAGE_EMPTY_DIR_START + - (heap_top - page)); + /* 5. SET POINTERS IN RECORDS AND DIR SLOTS */ /* Set the slots to point to infimum and supremum. */ @@ -829,12 +831,18 @@ page_delete_rec_list_start( { page_cur_t cur1; ulint log_mode; + ulint offsets_[100] = { 100, }; + ulint* offsets = offsets_; + mem_heap_t* heap = NULL; + byte type; - page_delete_rec_list_write_log(page, rec, index, - index->table->comp - ? MLOG_COMP_LIST_START_DELETE - : MLOG_LIST_START_DELETE, - mtr); + if (index->table->comp) { + type = MLOG_COMP_LIST_START_DELETE; + } else { + type = MLOG_LIST_START_DELETE; + } + + page_delete_rec_list_write_log(page, rec, index, type, mtr); page_cur_set_before_first(page, &cur1); @@ -850,8 +858,13 @@ page_delete_rec_list_start( log_mode = mtr_set_log_mode(mtr, MTR_LOG_NONE); while (page_cur_get_rec(&cur1) != rec) { + offsets = rec_get_offsets(page_cur_get_rec(&cur1), index, + offsets, ULINT_UNDEFINED, &heap); + page_cur_delete_rec(&cur1, index, offsets, mtr); + } - page_cur_delete_rec(&cur1, index, mtr); + if (heap) { + mem_heap_free(heap); } /* Restore log mode */ diff --git a/innobase/rem/rem0rec.c b/innobase/rem/rem0rec.c index 30f98f457ea..904f91b8945 100644 --- a/innobase/rem/rem0rec.c +++ b/innobase/rem/rem0rec.c @@ -620,7 +620,7 @@ rec_set_nth_field_extern_bit_new( mlog_write_ulint(lens + 1, len, MLOG_1BYTE, mtr); } else { - lens[1] = len; + lens[1] = (byte) len; } return; } @@ -658,29 +658,6 @@ rec_set_field_extern_bits( } } -/************************************************************** -Returns the total size of a physical record. */ - -ulint -rec_get_size( -/*=========*/ - /* out: size */ - rec_t* rec, /* in: physical record */ - dict_index_t* index) /* in: record descriptor */ -{ - mem_heap_t* heap = NULL; - ulint offsets_[100 + REC_OFFS_HEADER_SIZE] - = { 100, }; - ulint* offsets = rec_get_offsets(rec, index, offsets_, - ULINT_UNDEFINED, &heap); - ulint size = rec_offs_size(offsets); - - if (heap) { - mem_heap_free(heap); - } - return(size); -} - /*************************************************************** Sets an old-style record field to SQL null. The physical size of the field is not changed. */ @@ -935,13 +912,13 @@ init: || dtype_get_mtype(type) == DATA_BLOB); if (len < 128 || (dtype_get_len(type) < 256 && dtype_get_mtype(type) != DATA_BLOB)) { - *lens-- = len; + *lens-- = (byte) len; } else { /* the extern bits will be set later */ ut_ad(len < 16384); - *lens-- = len >> 8 | 0x80; - *lens-- = len; + *lens-- = (byte) (len >> 8) | 0x80; + *lens-- = (byte) len; } } copy: diff --git a/innobase/srv/srv0srv.c b/innobase/srv/srv0srv.c index 55ce452772d..39bb5094053 100644 --- a/innobase/srv/srv0srv.c +++ b/innobase/srv/srv0srv.c @@ -1501,7 +1501,7 @@ srv_suspend_mysql_thread( ut_usectime(&sec, &ms); finish_time = (ib_longlong)sec * 1000000 + ms; - diff_time = finish_time - start_time; + diff_time = (ulint) (finish_time - start_time); srv_n_lock_wait_current_count--; srv_n_lock_wait_time = srv_n_lock_wait_time + diff_time; @@ -1799,9 +1799,12 @@ srv_export_innodb_status(void) export_vars.innodb_row_lock_waits= srv_n_lock_wait_count; export_vars.innodb_row_lock_current_waits= srv_n_lock_wait_current_count; export_vars.innodb_row_lock_time= srv_n_lock_wait_time / 10000; - export_vars.innodb_row_lock_time_avg= - (srv_n_lock_wait_count > 0) ? - (srv_n_lock_wait_time / 10000 / srv_n_lock_wait_count) : 0; + if (srv_n_lock_wait_count > 0) { + export_vars.innodb_row_lock_time_avg = (ulint) + (srv_n_lock_wait_time / 10000 / srv_n_lock_wait_count); + } else { + export_vars.innodb_row_lock_time_avg = 0; + } export_vars.innodb_row_lock_time_max= srv_n_lock_max_wait_time / 10000; export_vars.innodb_rows_read= srv_n_rows_read; export_vars.innodb_rows_inserted= srv_n_rows_inserted; diff --git a/innobase/sync/sync0sync.c b/innobase/sync/sync0sync.c index 98b91ed5211..e604912e996 100644 --- a/innobase/sync/sync0sync.c +++ b/innobase/sync/sync0sync.c @@ -369,11 +369,11 @@ mutex_spin_wait( { ulint index; /* index of the reserved wait cell */ ulint i; /* spin round count */ +#ifndef UNIV_HOTBACKUP ib_longlong lstart_time = 0, lfinish_time; /* for timing os_wait */ ulint ltime_diff; ulint sec; ulint ms; -#ifndef UNIV_HOTBACKUP uint timer_started = 0; #endif /* !UNIV_HOTBACKUP */ ut_ad(mutex); @@ -535,7 +535,7 @@ finish_timing: ut_usectime(&sec, &ms); lfinish_time= (ib_longlong)sec * 1000000 + ms; - ltime_diff= lfinish_time - lstart_time; + ltime_diff= (ulint) (lfinish_time - lstart_time); mutex->lspent_time += ltime_diff; if (mutex->lmax_spent_time < ltime_diff) { From 6b5ad3c14320ba71293929df4a11b508c79536d5 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 1 Mar 2005 05:54:48 -0600 Subject: [PATCH 35/42] my_mmap.c: Changed #warning pragma at the bottom to only occur when being used on non-Windows OS mysys/my_mmap.c: Changed #warning pragma at the bottom to only occur when being used on non-Windows OS --- mysys/my_mmap.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mysys/my_mmap.c b/mysys/my_mmap.c index ed662a38712..98243c9d5d8 100644 --- a/mysys/my_mmap.c +++ b/mysys/my_mmap.c @@ -85,9 +85,7 @@ int my_msync(int fd, void *addr, size_t len, int flags) #endif -#ifdef _WINDOWS -#pragma message "no mmap!" -#else +#ifndef __WIN__ #warning "no mmap!" #endif From 1a3ee0dec50cb71d314ed6935a012760f0cb72d4 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 1 Mar 2005 14:13:25 +0200 Subject: [PATCH 36/42] Fixed Bug#7906, "Cmdline help for mysqld --ansi option misses some info". --- sql/mysqld.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index f1b1d8a7d86..b5674d17901 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4173,7 +4173,7 @@ struct my_option my_long_options[] = (gptr*) &abort_slave_event_count, (gptr*) &abort_slave_event_count, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, #endif /* HAVE_REPLICATION */ - {"ansi", 'a', "Use ANSI SQL syntax instead of MySQL syntax.", 0, 0, 0, + {"ansi", 'a', "Use ANSI SQL syntax instead of MySQL syntax. This mode will also set transaction isolation level 'serializable'.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, {"basedir", 'b', "Path to installation directory. All paths are usually resolved relative to this.", From 39cf45fd54247ca02554a2ecfedcbfbd996b9060 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 1 Mar 2005 19:42:59 +0200 Subject: [PATCH 37/42] After review fixes. Fix bugs in TRUNCATE. innobase/dict/dict0crea.c: dict_truncate_index_tree(): Commit the mtr after deleting the index tree. Add diagnostics for error cases. Let the caller update SYS_INDEXES.PAGE_NO to the new root page number. Return the new root page number, or FIL_NULL on error. innobase/include/dict0crea.h: dict_truncate_index_tree(): Commit the mtr after deleting the index tree. Add diagnostics for error cases. Let the caller update SYS_INDEXES.PAGE_NO to the new root page number. Return the new root page number, or FIL_NULL on error. innobase/include/page0page.ic: page_mem_free(): Disable the memset() call, to make it possible to recover some data if someone accidentally deletes a large number of records from a table. innobase/log/log0recv.c: Do not disable InnoDB Hot Backup specific code in MySQL builds. innobase/row/row0mysql.c: row_truncate_table_for_mysql(): Remove an infinite loop in case a SYS_INDEXES record has been deleted. Avoid deadlocks by committing and restarting the mini-transaction. sql/ha_innodb.cc: ha_innobase::delete_all_rows(): set trx->active_trans = 1 --- innobase/dict/dict0crea.c | 43 +++++++++++++++++++++++++++++------ innobase/include/dict0crea.h | 7 ++++-- innobase/include/page0page.ic | 3 +++ innobase/log/log0recv.c | 2 -- innobase/row/row0mysql.c | 27 ++++++++++++++++++++-- sql/ha_innodb.cc | 2 ++ 6 files changed, 71 insertions(+), 13 deletions(-) diff --git a/innobase/dict/dict0crea.c b/innobase/dict/dict0crea.c index 4926797721c..fda09feca6f 100644 --- a/innobase/dict/dict0crea.c +++ b/innobase/dict/dict0crea.c @@ -729,14 +729,17 @@ dict_drop_index_tree( /*********************************************************************** Truncates the index tree associated with a row in SYS_INDEXES table. */ -void +ulint dict_truncate_index_tree( /*=====================*/ + /* out: new root page number, or + FIL_NULL on failure */ dict_table_t* table, /* in: the table the index belongs to */ rec_t* rec, /* in: record in the clustered index of SYS_INDEXES table */ mtr_t* mtr) /* in: mtr having the latch - on the record page */ + on the record page. The mtr may be + committed and restarted in this call. */ { ulint root_page_no; ulint space; @@ -761,7 +764,10 @@ dict_truncate_index_tree( if (root_page_no == FIL_NULL) { /* The tree has been freed. */ - return; + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB: Trying to TRUNCATE" + " a missing index of table %s!\n", table->name); + return(FIL_NULL); } ptr = rec_get_nth_field_old(rec, @@ -775,7 +781,10 @@ dict_truncate_index_tree( /* It is a single table tablespace and the .ibd file is missing: do nothing */ - return; + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB: Trying to TRUNCATE" + " a missing .ibd file of table %s!\n", table->name); + return(FIL_NULL); } ptr = rec_get_nth_field_old(rec, @@ -801,6 +810,20 @@ dict_truncate_index_tree( space, root_page_no, RW_X_LATCH, mtr)); btr_free_root(space, root_page_no, mtr); + /* We will temporarily write FIL_NULL to the PAGE_NO field + in SYS_INDEXES, so that the database will not get into an + inconsistent state in case it crashes between the mtr_commit() + below and the following mtr_commit() call. */ + page_rec_write_index_page_no(rec, DICT_SYS_INDEXES_PAGE_NO_FIELD, + FIL_NULL, mtr); + + /* We will need to commit the mini-transaction in order to avoid + deadlocks in the btr_create() call, because otherwise we would + be freeing and allocating pages in the same mini-transaction. */ + mtr_commit(mtr); + /* mtr_commit() will invalidate rec. */ + rec = NULL; + mtr_start(mtr); /* Find the index corresponding to this SYS_INDEXES record. */ for (index = UT_LIST_GET_FIRST(table->indexes); @@ -814,11 +837,17 @@ dict_truncate_index_tree( root_page_no = btr_create(type, space, index_id, comp, mtr); if (index) { index->tree->page = root_page_no; + } else { + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: Index %lu %lu of table %s is missing\n" + "InnoDB: from the data dictionary during TRUNCATE!\n", + ut_dulint_get_high(index_id), + ut_dulint_get_low(index_id), + table->name); } - page_rec_write_index_page_no(rec, - DICT_SYS_INDEXES_PAGE_NO_FIELD, - root_page_no, mtr); + return(root_page_no); } /************************************************************************* diff --git a/innobase/include/dict0crea.h b/innobase/include/dict0crea.h index 7164e53bceb..5dd571be59c 100644 --- a/innobase/include/dict0crea.h +++ b/innobase/include/dict0crea.h @@ -56,14 +56,17 @@ dict_create_index_step( /*********************************************************************** Truncates the index tree associated with a row in SYS_INDEXES table. */ -void +ulint dict_truncate_index_tree( /*=====================*/ + /* out: new root page number, or + FIL_NULL on failure */ dict_table_t* table, /* in: the table the index belongs to */ rec_t* rec, /* in: record in the clustered index of SYS_INDEXES table */ mtr_t* mtr); /* in: mtr having the latch - on the record page */ + on the record page. The mtr may be + committed and restarted in this call. */ /*********************************************************************** Drops the index tree associated with a row in SYS_INDEXES table. */ diff --git a/innobase/include/page0page.ic b/innobase/include/page0page.ic index e3e93e9f756..ac6b0263a7d 100644 --- a/innobase/include/page0page.ic +++ b/innobase/include/page0page.ic @@ -788,12 +788,15 @@ page_mem_free( page_rec_set_next(rec, free); page_header_set_ptr(page, PAGE_FREE, rec); +#if 0 /* It's better not to destroy the user's data. */ + /* Clear the data bytes of the deleted record in order to improve the compression ratio of the page and to make it easier to read page dumps in corruption reports. The extra bytes of the record cannot be cleared, because page_mem_alloc() needs them in order to determine the size of the deleted record. */ memset(rec, 0, rec_offs_data_size(offsets)); +#endif garbage = page_header_get_field(page, PAGE_GARBAGE); diff --git a/innobase/log/log0recv.c b/innobase/log/log0recv.c index e1869a171b5..ac230f6461b 100644 --- a/innobase/log/log0recv.c +++ b/innobase/log/log0recv.c @@ -1435,7 +1435,6 @@ loop: mutex_exit(&(recv_sys->mutex)); } -#ifdef UNIV_HOTBACKUP /* This page is allocated from the buffer pool and used in the function below */ static page_t* recv_backup_application_page = NULL; @@ -1560,7 +1559,6 @@ skip_this_recv_addr: recv_sys_empty_hash(); } -#endif /* UNIV_HOTBACKUP */ #ifdef notdefined /*********************************************************************** diff --git a/innobase/row/row0mysql.c b/innobase/row/row0mysql.c index 39c4b76f814..4c014d39f7a 100644 --- a/innobase/row/row0mysql.c +++ b/innobase/row/row0mysql.c @@ -2615,6 +2615,7 @@ do not allow the TRUNCATE. We also reserve the data dictionary latch. */ rec_t* rec; const byte* field; ulint len; + ulint root_page_no; if (!btr_pcur_is_on_user_rec(&pcur, &mtr)) { /* The end of SYS_INDEXES has been reached. */ @@ -2633,11 +2634,33 @@ do not allow the TRUNCATE. We also reserve the data dictionary latch. */ if (rec_get_deleted_flag(rec, FALSE)) { /* The index has been dropped. */ - continue; + goto next_rec; } - dict_truncate_index_tree(table, rec, &mtr); + btr_pcur_store_position(&pcur, &mtr); + /* This call may commit and restart mtr. */ + root_page_no = dict_truncate_index_tree(table, rec, &mtr); + + btr_pcur_restore_position(BTR_MODIFY_LEAF, &pcur, &mtr); + rec = btr_pcur_get_rec(&pcur); + + if (root_page_no != FIL_NULL) { + page_rec_write_index_page_no(rec, + DICT_SYS_INDEXES_PAGE_NO_FIELD, + root_page_no, &mtr); + /* We will need to commit and restart the + mini-transaction in order to avoid deadlocks. + The dict_truncate_index_tree() call has allocated + a page in this mini-transaction, and the rest of + this loop could latch another index page. */ + mtr_commit(&mtr); + mtr_start(&mtr); + btr_pcur_restore_position(BTR_MODIFY_LEAF, + &pcur, &mtr); + } + + next_rec: btr_pcur_move_to_next_user_rec(&pcur, &mtr); } diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index f68ad99ac44..c23b316f180 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -4250,6 +4250,8 @@ ha_innobase::delete_all_rows(void) /* Truncate the table in InnoDB */ + trx->active_trans = 1; + error = row_truncate_table_for_mysql(prebuilt->table, trx); if (error == DB_ERROR) { /* Cannot truncate; resort to ha_innobase::delete_row() */ From 0319b958b50a47a42c4b1b185b1b1310ce0017aa Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 1 Mar 2005 19:44:53 +0200 Subject: [PATCH 38/42] ha_innodb.cc: In TRUNCATE TABLE, MySQL may call innobase_xa_prepare() even though there is no active transaction because it was committed in ha_innobase::delete_all_rows(); in innobase_xa_prepare() InnoDB starts then a new transaction; let us adjust the trx->active_trans flag accordingly sql/ha_innodb.cc: In TRUNCATE TABLE, MySQL may call innobase_xa_prepare() even though there is no active transaction because it was committed in ha_innobase::delete_all_rows(); in innobase_xa_prepare() InnoDB starts then a new transaction; let us adjust the trx->active_trans flag accordingly --- sql/ha_innodb.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index f68ad99ac44..121dbdccc12 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -6313,6 +6313,14 @@ innobase_xa_prepare( /* We were instructed to prepare the whole transaction, or this is an SQL statement end and autocommit is on */ + /* If there is no active InnoDB transaction, + trx_prepare_for_mysql() will (temporarily) start one */ + + if (trx->active_trans == 0) { + + trx->active_trans = 1; + } + error = trx_prepare_for_mysql(trx); } else { /* We just mark the SQL statement ended and do not do a From c0b1255317226e0a92f5c5d757fec14aa9de5b72 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 1 Mar 2005 20:56:39 +0200 Subject: [PATCH 39/42] ha_innodb.cc: delete_all_rows(): Revert previous change. The transaction has been marked started already in external_lock(). sql/ha_innodb.cc: delete_all_rows(): Revert previous change. The transaction has been marked started already in external_lock(). --- sql/ha_innodb.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 83831858360..121dbdccc12 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -4250,8 +4250,6 @@ ha_innobase::delete_all_rows(void) /* Truncate the table in InnoDB */ - trx->active_trans = 1; - error = row_truncate_table_for_mysql(prebuilt->table, trx); if (error == DB_ERROR) { /* Cannot truncate; resort to ha_innobase::delete_row() */ From 53bfeb48442ed04c8498cb4118b3715fd1dbeb42 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 1 Mar 2005 22:19:19 +0200 Subject: [PATCH 40/42] Fix for Bug#5615 and merge from 4.1 mysql-test/r/func_group.result: Added some tests and fix for Bug#5615. mysql-test/r/show_check.result: Fix for Bug#5615. mysql-test/t/func_group.test: Added some tests. sql/item_sum.cc: Fix for Bug#5615. sql/item_sum.h: Fix for Bug#5615. sql/sql_select.cc: Fix for Bug#5615. sql/sql_select.h: Fix for Bug#5615. --- mysql-test/r/func_group.result | 16 ++++++++++++++-- mysql-test/r/show_check.result | 4 +--- mysql-test/t/func_group.test | 4 ++++ sql/item_sum.cc | 18 ++++++++++++++++-- sql/item_sum.h | 3 ++- sql/sql_select.cc | 8 ++++---- sql/sql_select.h | 4 ++++ 7 files changed, 45 insertions(+), 12 deletions(-) diff --git a/mysql-test/r/func_group.result b/mysql-test/r/func_group.result index 6aadcd359b7..ea299455364 100644 --- a/mysql-test/r/func_group.result +++ b/mysql-test/r/func_group.result @@ -669,12 +669,24 @@ select charset(max(a)), coercibility(max(a)), charset(min(a)), coercibility(min(a)) from t1; charset(max(a)) coercibility(max(a)) charset(min(a)) coercibility(min(a)) latin2 2 latin2 2 +show create table t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` char(1) character set latin2 default NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 create table t2 select max(a),min(a) from t1; show create table t2; Table Create Table t2 CREATE TABLE `t2` ( - `max(a)` varchar(1) character set latin2 default NULL, - `min(a)` varchar(1) character set latin2 default NULL + `max(a)` char(1) character set latin2 default NULL, + `min(a)` char(1) character set latin2 default NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1 +drop table t2; +create table t2 select concat(a) from t1; +show create table t2; +Table Create Table +t2 CREATE TABLE `t2` ( + `concat(a)` varchar(1) character set latin2 default NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 drop table t2,t1; create table t1 (a int); diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result index a2b2eebf628..15cae7646f9 100644 --- a/mysql-test/r/show_check.result +++ b/mysql-test/r/show_check.result @@ -252,11 +252,9 @@ type_bool type_tiny type_short type_mediumint type_bigint type_decimal type_nume drop table t1; create table t1 (a int not null); create table t2 select max(a) from t1; -Warnings: -Warning 1263 Data truncated; NULL supplied to NOT NULL column 'max(a)' at row 1 show columns from t2; Field Type Null Key Default Extra -max(a) int(11) 0 +max(a) int(11) YES NULL drop table t1,t2; create table t1 (c decimal, d double, f float, r real); show columns from t1; diff --git a/mysql-test/t/func_group.test b/mysql-test/t/func_group.test index 51528be8952..96fd297dc20 100644 --- a/mysql-test/t/func_group.test +++ b/mysql-test/t/func_group.test @@ -395,8 +395,12 @@ create table t1 (a char character set latin2); insert into t1 values ('a'),('b'); select charset(max(a)), coercibility(max(a)), charset(min(a)), coercibility(min(a)) from t1; +show create table t1; create table t2 select max(a),min(a) from t1; show create table t2; +drop table t2; +create table t2 select concat(a) from t1; +show create table t2; drop table t2,t1; # diff --git a/sql/item_sum.cc b/sql/item_sum.cc index b4a53ba0f80..dbba02c3cf7 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -22,6 +22,7 @@ #endif #include "mysql_priv.h" +#include "sql_select.h" Item_sum::Item_sum(List &list) :arg_count(list.elements) @@ -303,6 +304,21 @@ Item_sum_hybrid::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) return FALSE; } +Field *Item_sum_hybrid::create_tmp_field(bool group, TABLE *table, + uint convert_blob_length) +{ + if (args[0]->type() == Item::FIELD_ITEM) + { + Field *field= ((Item_field*) args[0])->field; + + if ((field= create_tmp_field_from_field(current_thd, field, this, table, + 0, convert_blob_length))) + field->flags&= ~NOT_NULL_FLAG; + return field; + } + return Item_sum::create_tmp_field(group, table, convert_blob_length); +} + /*********************************************************************** ** reset and add of sum_func @@ -2075,8 +2091,6 @@ my_decimal *Item_variance_field::val_decimal(my_decimal *dec_buf) ** COUNT(DISTINCT ...) ****************************************************************************/ -#include "sql_select.h" - int simple_str_key_cmp(void* arg, byte* key1, byte* key2) { Item_sum_count_distinct* item = (Item_sum_count_distinct*)arg; diff --git a/sql/item_sum.h b/sql/item_sum.h index 81f5d45e42e..fead45f1881 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -94,7 +94,6 @@ public: Item *get_tmp_table_item(THD *thd); virtual Field *create_tmp_field(bool group, TABLE *table, uint convert_blob_length); - bool walk (Item_processor processor, byte *argument); }; @@ -525,6 +524,8 @@ protected: void cleanup(); bool any_value() { return was_values; } void no_rows_in_result(); + Field *create_tmp_field(bool group, TABLE *table, + uint convert_blob_length); }; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index cbe7937355c..e886c658444 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -7593,10 +7593,10 @@ const_expression_in_where(COND *cond, Item *comp_item, Item **const_item) new_created field */ -static Field* create_tmp_field_from_field(THD *thd, Field* org_field, - Item *item, TABLE *table, - bool modify_item, - uint convert_blob_length) +Field* create_tmp_field_from_field(THD *thd, Field* org_field, + Item *item, TABLE *table, + bool modify_item, + uint convert_blob_length) { Field *new_field; diff --git a/sql/sql_select.h b/sql/sql_select.h index 0f26207b391..1b7893dbc7c 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -399,6 +399,10 @@ void copy_funcs(Item **func_ptr); bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param, int error, bool ignore_last_dupp_error); uint find_shortest_key(TABLE *table, const key_map *usable_keys); +Field* create_tmp_field_from_field(THD *thd, Field* org_field, + Item *item, TABLE *table, + bool modify_item, + uint convert_blob_length); /* functions from opt_sum.cc */ bool simple_pred(Item_func *func_item, Item **args, bool *inv_order); From af529fd14c871e78acac4d6c0dd6091086692675 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 2 Mar 2005 00:25:16 +0100 Subject: [PATCH 41/42] Makefile.am: updated ndb test makefile to make src dist complete ndb/test/ndbapi/Makefile.am: updated ndb test makefile to make src dist complete --- ndb/test/ndbapi/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ndb/test/ndbapi/Makefile.am b/ndb/test/ndbapi/Makefile.am index 5eb3c215ee5..9a1726ae11a 100644 --- a/ndb/test/ndbapi/Makefile.am +++ b/ndb/test/ndbapi/Makefile.am @@ -74,8 +74,8 @@ testReadPerf_SOURCES = testReadPerf.cpp testLcp_SOURCES = testLcp.cpp testPartitioning_SOURCES = testPartitioning.cpp testBitfield_SOURCES = testBitfield.cpp -DbCreate_SOURCES= bench/mainPopulate.cpp bench/dbPopulate.cpp bench/userInterface.cpp -DbAsyncGenerator_SOURCES= bench/mainAsyncGenerator.cpp bench/asyncGenerator.cpp bench/ndb_async2.cpp +DbCreate_SOURCES = bench/mainPopulate.cpp bench/dbPopulate.cpp bench/userInterface.cpp bench/dbPopulate.h bench/userInterface.h bench/testData.h bench/testDefinitions.h bench/ndb_schema.hpp bench/ndb_error.hpp +DbAsyncGenerator_SOURCES = bench/mainAsyncGenerator.cpp bench/asyncGenerator.cpp bench/ndb_async2.cpp bench/dbGenerator.h bench/macros.h bench/userInterface.h bench/testData.h bench/testDefinitions.h bench/ndb_schema.hpp bench/ndb_error.hpp INCLUDES_LOC = -I$(top_srcdir)/ndb/include/kernel From 67457597b80a0f636a8477cffcca37bf64c7c1d0 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 2 Mar 2005 01:25:17 +0200 Subject: [PATCH 42/42] ha_innodb.cc: Correct indentation + some small style corrections sql/ha_innodb.cc: Correct indentation + some small style corrections --- sql/ha_innodb.cc | 215 ++++++++++++++++++++++++++--------------------- 1 file changed, 118 insertions(+), 97 deletions(-) diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc index 121dbdccc12..35f60d1daf9 100644 --- a/sql/ha_innodb.cc +++ b/sql/ha_innodb.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & Innobase Oy +/* Copyright (C) 2000-2005 MySQL AB & Innobase Oy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -336,14 +336,18 @@ innobase_release_temporary_latches( /*===============================*/ THD *thd) { + trx_t* trx; + if (!innodb_inited) { return; } - trx_t *trx= (trx_t*) thd->ha_data[innobase_hton.slot]; - if (trx) - innobase_release_stat_resources(trx); + trx = (trx_t*) thd->ha_data[innobase_hton.slot]; + + if (trx) { + innobase_release_stat_resources(trx); + } } /************************************************************************ @@ -743,14 +747,15 @@ transaction internally. */ static void register_trans( -/*============*/ +/*===========*/ THD* thd) /* in: thd to use the handle */ { - /* register the start of the statement */ + /* Register the start of the statement */ trans_register_ha(thd, FALSE, &innobase_hton); + if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) { - /* no autocommit mode, register for a transaction */ + /* No autocommit mode, register for a transaction */ trans_register_ha(thd, TRUE, &innobase_hton); } } @@ -1051,7 +1056,7 @@ ha_innobase::init_table_handle_for_HANDLER(void) /************************************************************************* Opens an InnoDB database. */ -handlerton * +handlerton* innobase_init(void) /*===============*/ /* out: TRUE if error */ @@ -1220,7 +1225,7 @@ innobase_init(void) srv_print_verbose_log = mysqld_embedded ? 0 : 1; - /* Store the default charset-collation number of this MySQL + /* Store the default charset-collation number of this MySQL installation */ data_mysql_default_charset_coll = (ulint)default_charset_info->number; @@ -1346,14 +1351,16 @@ innobase_commit_low( return; } +/* The following will be enabled later when we put the 4.1 functionality back +to 5.0. */ #ifdef DISABLE_HAVE_REPLICATION if (current_thd->slave_thread) { /* Update the replication position info inside InnoDB */ trx->mysql_master_log_file_name = active_mi->rli.group_master_log_name; - trx->mysql_master_log_pos= ((ib_longlong) - active_mi->rli.future_group_master_log_pos); + trx->mysql_master_log_pos = ((ib_longlong) + active_mi->rli.future_group_master_log_pos); } #endif /* HAVE_REPLICATION */ @@ -1456,7 +1463,8 @@ innobase_commit( "InnoDB: but trx->conc_state != TRX_NOT_STARTED\n"); } - if (all || (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) { + if (all + || (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) { /* We were instructed to commit the whole transaction, or this is an SQL statement end and autocommit is on */ @@ -1489,10 +1497,9 @@ innobase_commit( DBUG_RETURN(0); } -/* - don't delete it - it may be re-enabled later - as an optimization for the most common case InnoDB+binlog -*/ +/* The following defined-out code will be enabled later when we put the +MySQL-4.1 functionality back to 5.0. This is needed to get InnoDB Hot Backup +to work. */ #if 0 /********************************************************************* This is called when MySQL writes the binlog entry for the current @@ -1627,7 +1634,8 @@ innobase_rollback( row_unlock_table_autoinc_for_mysql(trx); } - if (all || (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) { + if (all + || (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) { error = trx_rollback_for_mysql(trx); trx->active_trans = 0; @@ -1686,6 +1694,7 @@ innobase_rollback_to_savepoint( ib_longlong mysql_binlog_cache_pos; int error = 0; trx_t* trx; + char name[64]; DBUG_ENTER("innobase_rollback_to_savepoint"); @@ -1698,8 +1707,8 @@ innobase_rollback_to_savepoint( innobase_release_stat_resources(trx); /* TODO: use provided savepoint data area to store savepoint data */ - char name[64]; - longlong2str((ulonglong)savepoint,name,36); + + longlong2str((ulonglong)savepoint, name, 36); error = trx_rollback_to_savepoint_for_mysql(trx, name, &mysql_binlog_cache_pos); @@ -1708,26 +1717,27 @@ innobase_rollback_to_savepoint( /********************************************************************* Release transaction savepoint name. */ - -static int +static +int innobase_release_savepoint( -/*===========================*/ +/*=======================*/ /* out: 0 if success, HA_ERR_NO_SAVEPOINT if no savepoint with the given name */ THD* thd, /* in: handle to the MySQL thread of the user whose transaction should be rolled back */ - void *savepoint) /* in: savepoint data */ + void* savepoint) /* in: savepoint data */ { int error = 0; trx_t* trx; + char name[64]; DBUG_ENTER("innobase_release_savepoint"); trx = check_trx_exists(thd); /* TODO: use provided savepoint data area to store savepoint data */ - char name[64]; - longlong2str((ulonglong)savepoint,name,36); + + longlong2str((ulonglong)savepoint, name, 36); error = trx_release_savepoint_for_mysql(trx, name); @@ -1736,13 +1746,13 @@ innobase_release_savepoint( /********************************************************************* Sets a transaction savepoint. */ - -static int +static +int innobase_savepoint( /*===============*/ /* out: always 0, that is, always succeeds */ THD* thd, /* in: handle to the MySQL thread */ - void *savepoint) /* in: savepoint data */ + void* savepoint) /* in: savepoint data */ { int error = 0; trx_t* trx; @@ -1911,7 +1921,8 @@ ha_innobase::open( fields when packed actually became 1 byte longer, when we also stored the string length as the first byte. */ - upd_and_key_val_buff_len = table->s->reclength + table->s->max_key_length + upd_and_key_val_buff_len = + table->s->reclength + table->s->max_key_length + MAX_REF_PARTS * 3; if (!(mysql_byte*) my_multi_malloc(MYF(MY_WME), &upd_buff, upd_and_key_val_buff_len, @@ -1963,7 +1974,8 @@ ha_innobase::open( innobase_prebuilt = row_create_prebuilt(ib_table); - ((row_prebuilt_t*)innobase_prebuilt)->mysql_row_len = table->s->reclength; + ((row_prebuilt_t*)innobase_prebuilt)->mysql_row_len = + table->s->reclength; /* Looks like MySQL-3.23 sometimes has primary key number != 0 */ @@ -1985,13 +1997,11 @@ ha_innobase::open( ((row_prebuilt_t*)innobase_prebuilt) ->clust_index_was_generated = FALSE; - /* - MySQL allocates the buffer for ref. key_info->key_length - includes space for all key columns + one byte for each column - that may be NULL. ref_length must be as exact as possible to - save space, because all row reference buffers are allocated - based on ref_length. - */ + /* MySQL allocates the buffer for ref. key_info->key_length + includes space for all key columns + one byte for each column + that may be NULL. ref_length must be as exact as possible to + save space, because all row reference buffers are allocated + based on ref_length. */ ref_length = table->key_info[primary_key].key_length; } else { @@ -2013,15 +2023,13 @@ ha_innobase::open( ref_length = DATA_ROW_ID_LEN; - /* - If we automatically created the clustered index, then - MySQL does not know about it, and MySQL must NOT be aware - of the index used on scan, to make it avoid checking if we - update the column of the index. That is why we assert below - that key_used_on_scan is the undefined value MAX_KEY. - The column is the row id in the automatical generation case, - and it will never be updated anyway. - */ + /* If we automatically created the clustered index, then + MySQL does not know about it, and MySQL must NOT be aware + of the index used on scan, to make it avoid checking if we + update the column of the index. That is why we assert below + that key_used_on_scan is the undefined value MAX_KEY. + The column is the row id in the automatical generation case, + and it will never be updated anyway. */ if (key_used_on_scan != MAX_KEY) { fprintf(stderr, @@ -2611,7 +2619,8 @@ ha_innobase::write_row( "InnoDB: Dump of 200 bytes around transaction.all: ", stderr); ut_print_buf(stderr, - ((byte*)(&(current_thd->ha_data[innobase_hton.slot]))) - 100, 200); + ((byte*)(&(current_thd->ha_data[innobase_hton.slot]))) - 100, + 200); putc('\n', stderr); ut_error; } @@ -2646,7 +2655,7 @@ ha_innobase::write_row( src_table = lock_get_src_table( prebuilt->trx, prebuilt->table, &mode); if (!src_table) { - no_commit: +no_commit: /* Unknown situation: do not commit */ /* ut_print_timestamp(stderr); @@ -2669,6 +2678,7 @@ ha_innobase::write_row( } else { /* Ensure that there are no other table locks than LOCK_IX and LOCK_AUTO_INC on the destination table. */ + if (!lock_is_table_exclusive(prebuilt->table, prebuilt->trx)) { goto no_commit; @@ -2746,11 +2756,11 @@ ha_innobase::write_row( if (error == DB_SUCCESS && auto_inc_used) { - /* Fetch the value that was set in the autoincrement field */ + /* Fetch the value that was set in the autoincrement field */ - auto_inc = table->next_number_field->val_int(); + auto_inc = table->next_number_field->val_int(); - if (auto_inc != 0) { + if (auto_inc != 0) { /* This call will calculate the max of the current value and the value supplied by the user and update the counter accordingly */ @@ -2762,15 +2772,15 @@ ha_innobase::write_row( The lock is released at each SQL statement's end. */ - error = row_lock_table_autoinc_for_mysql(prebuilt); + error = row_lock_table_autoinc_for_mysql(prebuilt); - if (error != DB_SUCCESS) { - - error = convert_error_code_to_mysql(error, user_thd); - goto func_exit; - } - dict_table_autoinc_update(prebuilt->table, auto_inc); - } + if (error != DB_SUCCESS) { + error = convert_error_code_to_mysql(error, + user_thd); + goto func_exit; + } + dict_table_autoinc_update(prebuilt->table, auto_inc); + } } innodb_srv_conc_exit_innodb(prebuilt->trx); @@ -2785,7 +2795,6 @@ func_exit: DBUG_RETURN(error); } - /****************************************************************** Converts field data for storage in an InnoDB update vector. */ inline @@ -4514,10 +4523,10 @@ ha_innobase::records_in_range( dict_index_t* index; mysql_byte* key_val_buff2 = (mysql_byte*) my_malloc( table->s->reclength - + table->s->max_key_length + 100, + + table->s->max_key_length + 100, MYF(MY_WME)); ulint buff2_len = table->s->reclength - + table->s->max_key_length + 100; + + table->s->max_key_length + 100; dtuple_t* range_start; dtuple_t* range_end; ib_longlong n_rows; @@ -4674,21 +4683,27 @@ ha_innobase::read_time( ha_rows total_rows; double time_for_scan; - if (index != table->s->primary_key) - return handler::read_time(index, ranges, rows); // Not clustered + if (index != table->s->primary_key) { + /* Not clustered */ + return(handler::read_time(index, ranges, rows)); + } - if (rows <= 2) - return (double) rows; + if (rows <= 2) { + + return((double) rows); + } /* Assume that the read time is proportional to the scan time for all rows + at most one seek per range. */ time_for_scan = scan_time(); - if ((total_rows = estimate_rows_upper_bound()) < rows) - return time_for_scan; + if ((total_rows = estimate_rows_upper_bound()) < rows) { - return (ranges + (double) rows / (double) total_rows * time_for_scan); + return(time_for_scan); + } + + return(ranges + (double) rows / (double) total_rows * time_for_scan); } /************************************************************************* @@ -5106,7 +5121,7 @@ ha_innobase::get_foreign_key_list(THD *thd, List *f_key_list) tmp_buff, i, 1); tmp_buff+= i + 1; f_key_info.referenced_table= make_lex_string(thd, 0, - tmp_buff, strlen(tmp_buff), 1); + tmp_buff, strlen(tmp_buff), 1); for (i= 0;;) { @@ -5624,7 +5639,6 @@ innodb_export_status(void) srv_export_innodb_status(); } - /**************************************************************************** Implements the SHOW INNODB STATUS command. Sends the output of the InnoDB Monitor to the client. */ @@ -5636,6 +5650,8 @@ innodb_show_status( { Protocol *protocol= thd->protocol; trx_t* trx; + long flen; + char* str; DBUG_ENTER("innodb_show_status"); @@ -5652,14 +5668,13 @@ innodb_show_status( /* We let the InnoDB Monitor to output at most 64000 bytes of text. */ - long flen; - char* str; - mutex_enter_noninline(&srv_monitor_file_mutex); rewind(srv_monitor_file); + srv_printf_innodb_monitor(srv_monitor_file); flen = ftell(srv_monitor_file); os_file_set_eof(srv_monitor_file); + if (flen < 0) { flen = 0; } else if (flen > 64000 - 1) { @@ -5669,10 +5684,10 @@ innodb_show_status( /* allocate buffer for the string, and read the contents of the temporary file */ - if (!(str = my_malloc(flen + 1, MYF(0)))) - { - mutex_exit_noninline(&srv_monitor_file_mutex); - DBUG_RETURN(TRUE); + if (!(str = my_malloc(flen + 1, MYF(0)))) { + mutex_exit_noninline(&srv_monitor_file_mutex); + + DBUG_RETURN(TRUE); } rewind(srv_monitor_file); @@ -5686,7 +5701,6 @@ innodb_show_status( if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) { - my_free(str, MYF(0)); DBUG_RETURN(TRUE); @@ -5696,10 +5710,12 @@ innodb_show_status( protocol->store(str, flen, system_charset_info); my_free(str, MYF(0)); - if (protocol->write()) - DBUG_RETURN(TRUE); + if (protocol->write()) { + DBUG_RETURN(TRUE); + } send_eof(thd); + DBUG_RETURN(FALSE); } @@ -6093,7 +6109,7 @@ ha_innobase::get_auto_increment() if (error) { - return(~(ulonglong) 0); + return(~(ulonglong) 0); } return((ulonglong) nr); @@ -6115,7 +6131,8 @@ ha_innobase::cmp_ref( /* Do type-aware comparison of Primary Key members. PK members are always NOT NULL, so no checks for NULL are performed */ - KEY_PART_INFO *key_part= table->key_info[table->s->primary_key].key_part; + KEY_PART_INFO *key_part= + table->key_info[table->s->primary_key].key_part; KEY_PART_INFO *key_part_end= key_part + table->key_info[table->s->primary_key].key_parts; for (; key_part != key_part_end; ++key_part) { @@ -6260,19 +6277,21 @@ innobase_query_is_update(void) thd = (THD *)innobase_current_thd(); - if ( thd->lex->sql_command == SQLCOM_REPLACE || - thd->lex->sql_command == SQLCOM_REPLACE_SELECT || - ( thd->lex->sql_command == SQLCOM_LOAD && - thd->lex->duplicates == DUP_REPLACE )) { - return true; + if (thd->lex->sql_command == SQLCOM_REPLACE || + thd->lex->sql_command == SQLCOM_REPLACE_SELECT || + (thd->lex->sql_command == SQLCOM_LOAD && + thd->lex->duplicates == DUP_REPLACE)) { + + return(1); } - if ( thd->lex->sql_command == SQLCOM_INSERT && - thd->lex->duplicates == DUP_UPDATE ) { - return true; + if (thd->lex->sql_command == SQLCOM_INSERT && + thd->lex->duplicates == DUP_UPDATE) { + + return(1); } - return false; + return(0); } } @@ -6308,7 +6327,8 @@ innobase_xa_prepare( "InnoDB: but trx->conc_state != TRX_NOT_STARTED\n"); } - if (all || (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) { + if (all + || (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) { /* We were instructed to prepare the whole transaction, or this is an SQL statement end and autocommit is on */ @@ -6359,10 +6379,11 @@ innobase_xa_recover( uint len) /* in: number of slots in xid_list */ { if (len == 0 || xid_list == NULL) { - return 0; + + return(0); } - return (trx_recover_for_mysql(xid_list, len)); + return(trx_recover_for_mysql(xid_list, len)); } /*********************************************************************** @@ -6373,7 +6394,7 @@ int innobase_commit_by_xid( /*===================*/ /* out: 0 or error number */ - XID* xid) /* in: X/Open XA Transaction Identification */ + XID* xid) /* in: X/Open XA transaction identification */ { trx_t* trx; @@ -6396,7 +6417,7 @@ int innobase_rollback_by_xid( /*=====================*/ /* out: 0 or error number */ - XID *xid) /* in : X/Open XA Transaction Idenfification */ + XID *xid) /* in: X/Open XA transaction idenfification */ { trx_t* trx;