From 970aa54bd68ea425923248295be8d52f1f52b039 Mon Sep 17 00:00:00 2001 From: "jimw@mysql.com" <> Date: Wed, 1 Feb 2006 12:28:39 -0800 Subject: [PATCH 01/20] Fix mysqldump crash when encountering a VIEW (when used against a 5.0 or later server, obviously). (Bug #16389) --- client/mysqldump.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/client/mysqldump.c b/client/mysqldump.c index 7ff9504607f..64629bcf608 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -2557,8 +2557,11 @@ static const char *check_if_ignore_table(const char *table_name) mysql_free_result(res); return 0; /* assume table is ok */ } - if (strcmp(row[1], (result= "MRG_MyISAM")) && - strcmp(row[1], (result= "MRG_ISAM"))) + /* Some forward-compatibility: don't dump data from a VIEW */ + if (!row[1]) + result= "VIEW"; + else if (strcmp(row[1], (result= "MRG_MyISAM")) && + strcmp(row[1], (result= "MRG_ISAM"))) result= 0; mysql_free_result(res); return result; From 4217699e56828f5a7af66f188bcc59ed6919623d Mon Sep 17 00:00:00 2001 From: "msvensson@neptunus.(none)" <> Date: Fri, 10 Feb 2006 12:11:16 +0100 Subject: [PATCH 02/20] Bug#17280 mysqltest, --echo sometimes does not expand $variables - Evaluate all variables in the text before printing it to result file --- client/mysqltest.c | 28 +++++++++++++++------------- mysql-test/r/mysqltest.result | 19 +++++++++++++++++-- mysql-test/t/mysqltest.test | 13 +++++++++++++ 3 files changed, 45 insertions(+), 15 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index 6a2a7b072de..8c712541fb5 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -1384,38 +1384,40 @@ int do_system(struct st_query *q) /* Print the content between echo and to result file. - If content is a variable, the variable value will be retrieved + Evaluate all variables in the string before printing, allow + for variable names to be escaped using \ SYNOPSIS do_echo() q called command DESCRIPTION - Usage 1: echo text Print the text after echo until end of command to result file - Usage 2: echo $ Print the content of the variable to result file + echo Some text $ + Print "Some text" plus the content of the variable to + result file + + echo Some text \$ + Print "Some text" plus $ to result file */ -int do_echo(struct st_query *q) +int do_echo(struct st_query *command) { - char *p= q->first_argument; - DYNAMIC_STRING *ds; - VAR v; - var_init(&v,0,0,0,0); + DYNAMIC_STRING *ds, ds_echo; ds= &ds_res; - eval_expr(&v, p, 0); /* NULL terminated */ - if (v.str_val_len) - dynstr_append_mem(ds, v.str_val, v.str_val_len); + init_dynamic_string(&ds_echo, "", 256, 256); + do_eval(&ds_echo, command->first_argument); + dynstr_append_mem(ds, ds_echo.str, ds_echo.length); dynstr_append_mem(ds, "\n", 1); - var_free(&v); - q->last_argument= q->end; + dynstr_free(&ds_echo); + command->last_argument= command->end; return 0; } diff --git a/mysql-test/r/mysqltest.result b/mysql-test/r/mysqltest.result index 067054510c2..53141a8d266 100644 --- a/mysql-test/r/mysqltest.result +++ b/mysql-test/r/mysqltest.result @@ -201,8 +201,14 @@ source database - world''s most -- popular open # source database -'$message' -"$message" +'# MySQL: The +- world''s most +-- popular open +# source database' +"# MySQL: The +- world''s most +-- popular open +# source database" hej hej hej @@ -222,6 +228,15 @@ mysqltest: At line 1: Missing arguments to let mysqltest: At line 1: Missing variable name in let mysqltest: At line 1: Variable name in =hi does not start with '$' mysqltest: At line 1: Missing assignment operator in let +# Execute: --echo # success: $success +# success: 1 +# Execute: echo # success: $success ; +# success: 1 +# The next two variants work fine and expand the content of $success +# Execute: --echo $success +1 +# Execute: echo $success ; +1 mysqltest: At line 1: Missing file name in source mysqltest: At line 1: Could not open file ./non_existingFile mysqltest: In included file "./var/tmp/recursive.sql": At line 1: Source directives are nesting too deep diff --git a/mysql-test/t/mysqltest.test b/mysql-test/t/mysqltest.test index 5cf49185c30..caedbfab4a6 100644 --- a/mysql-test/t/mysqltest.test +++ b/mysql-test/t/mysqltest.test @@ -539,6 +539,19 @@ echo $novar1; --error 1 --exec echo "let hi;" | $MYSQL_TEST 2>&1 +# More advanced test for bug#17280 +let $success= 1; +--echo # Execute: --echo # success: \$success +--echo # success: $success +--echo # Execute: echo # success: \$success ; +echo # success: $success ; + +--echo # The next two variants work fine and expand the content of \$success +--echo # Execute: --echo \$success +--echo $success +--echo # Execute: echo \$success ; +echo $success ; + # ---------------------------------------------------------------------------- # Test to assign let from query # let $=``; From 5000951ab48b3f995e5eb6e65a50820cc9ebd0c0 Mon Sep 17 00:00:00 2001 From: "msvensson@neptunus.(none)" <> Date: Fri, 10 Feb 2006 14:50:29 +0100 Subject: [PATCH 03/20] Bug#14013 mysql_stmt_store_result() bombs if a cursor is open - Add code to 'mysql_stmt_store_result' to allow it to be called on a prepared statement with open server side cursor. - Add tests to mysql_client_test that uses 'mysql_stmt_store_result' --- client/mysqltest.c | 40 ++++++++++++++++++++++----------------- libmysql/libmysql.c | 35 ++++++++++++++++++++++++++++++++-- tests/mysql_client_test.c | 20 +++++++++++++++++--- 3 files changed, 73 insertions(+), 22 deletions(-) diff --git a/client/mysqltest.c b/client/mysqltest.c index 24be5de8021..3851a922e13 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -3167,7 +3167,7 @@ static void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt, if (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) die("fetch didn't end with MYSQL_NO_DATA from statement: %d %s", - mysql_stmt_error(stmt), mysql_stmt_errno(stmt)); + mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); free_replace_column(); @@ -3632,7 +3632,7 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command, if (mysql_stmt_prepare(stmt, query, query_len)) { handle_error(query, command, mysql_stmt_errno(stmt), - mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); + mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); goto end; } @@ -3648,29 +3648,34 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command, parameter markers. */ -#ifdef BUG14013_FIXED - /* - Use cursor when retrieving result - */ if (cursor_protocol_enabled) { + /* + Use cursor when retrieving result + */ ulong type= CURSOR_TYPE_READ_ONLY; if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type)) die("mysql_stmt_attr_set(STMT_ATTR_CURSOR_TYPE) failed': %d %s", - mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); + mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); } -#endif /* Execute the query - */ + */ if (mysql_stmt_execute(stmt)) { handle_error(query, command, mysql_stmt_errno(stmt), - mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); + mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); goto end; } + /* + When running in cursor_protocol get the warnings from execute here + and keep them in a separate string for later. + */ + if (cursor_protocol_enabled && !disable_warnings) + append_warnings(&ds_execute_warnings, mysql); + /* We instruct that we want to update the "max_length" field in mysql_stmt_store_result(), this is our only way to know how much @@ -3680,7 +3685,7 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command, my_bool one= 1; if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*) &one)) die("mysql_stmt_attr_set(STMT_ATTR_UPDATE_MAX_LENGTH) failed': %d %s", - mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); + mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); } /* @@ -3690,7 +3695,7 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command, if (mysql_stmt_store_result(stmt)) { handle_error(query, command, mysql_stmt_errno(stmt), - mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); + mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); goto end; } @@ -3711,10 +3716,10 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command, uint num_fields= mysql_num_fields(res); if (display_metadata) - append_metadata(ds, fields, num_fields); + append_metadata(ds, fields, num_fields); if (!display_result_vertically) - append_table_headings(ds, fields, num_fields); + append_table_headings(ds, fields, num_fields); append_stmt_result(ds, stmt, fields, num_fields); @@ -3736,10 +3741,11 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command, /* Append warnings to ds - if there are any */ if (append_warnings(&ds_execute_warnings, mysql) || - ds_prepare_warnings.length || - ds_warnings->length) + ds_execute_warnings.length || + ds_prepare_warnings.length || + ds_warnings->length) { - dynstr_append_mem(ds, "Warnings:\n", 10); + dynstr_append_mem(ds, "Warnings:\n", 10); if (ds_warnings->length) dynstr_append_mem(ds, ds_warnings->str, ds_warnings->length); diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 11ee7284cbf..30eecf809c5 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -4757,12 +4757,39 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt) if (!stmt->field_count) DBUG_RETURN(0); - if ((int) stmt->state < (int) MYSQL_STMT_EXECUTE_DONE || - mysql->status != MYSQL_STATUS_GET_RESULT) + + if ((int) stmt->state < (int) MYSQL_STMT_EXECUTE_DONE) { set_stmt_error(stmt, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); DBUG_RETURN(1); } + + if (mysql->status == MYSQL_STATUS_READY && + stmt->server_status & SERVER_STATUS_CURSOR_EXISTS) + { + /* + Server side cursor exist, tell server to start sending the rows + */ + NET *net= &mysql->net; + char buff[4 /* statement id */ + + 4 /* number of rows to fetch */]; + + /* Send row request to the server */ + int4store(buff, stmt->stmt_id); + int4store(buff + 4, (int)~0); /* number of rows to fetch */ + if (cli_advanced_command(mysql, COM_STMT_FETCH, buff, sizeof(buff), + NullS, 0, 1)) + { + set_stmt_errmsg(stmt, net->last_error, net->last_errno, net->sqlstate); + DBUG_RETURN(1); + } + } + else if (mysql->status != MYSQL_STATUS_GET_RESULT) + { + set_stmt_error(stmt, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); + DBUG_RETURN(1); + } + if (result->data) { free_root(&result->alloc, MYF(MY_KEEP_PREALLOC)); @@ -4803,6 +4830,10 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt) DBUG_RETURN(1); } + /* Assert that if there was a cursor, all rows have been fetched */ + DBUG_ASSERT(mysql->status != MYSQL_STATUS_READY || + (mysql->server_status & SERVER_STATUS_LAST_ROW_SENT)); + if (stmt->update_max_length) { MYSQL_ROWS *cur= result->data; diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 216961b3a80..25729e9f47c 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -1049,7 +1049,10 @@ void stmt_fetch_close(Stmt_fetch *fetch) reading from the rest. */ -my_bool fetch_n(const char **query_list, unsigned query_count) +enum fetch_type { USE_ROW_BY_ROW_FETCH= 0, USE_STORE_RESULT= 1 }; + +my_bool fetch_n(const char **query_list, unsigned query_count, + enum fetch_type fetch_type) { unsigned open_statements= query_count; int rc, error_count= 0; @@ -1065,6 +1068,15 @@ my_bool fetch_n(const char **query_list, unsigned query_count) query_list[fetch - fetch_array]); } + if (fetch_type == USE_STORE_RESULT) + { + for (fetch= fetch_array; fetch < fetch_array + query_count; ++fetch) + { + rc= mysql_stmt_store_result(fetch->handle); + check_execute(fetch->handle, rc); + } + } + while (open_statements) { for (fetch= fetch_array; fetch < fetch_array + query_count; ++fetch) @@ -11867,7 +11879,8 @@ static void test_basic_cursors() fill_tables(basic_tables, sizeof(basic_tables)/sizeof(*basic_tables)); - fetch_n(queries, sizeof(queries)/sizeof(*queries)); + fetch_n(queries, sizeof(queries)/sizeof(*queries), USE_ROW_BY_ROW_FETCH); + fetch_n(queries, sizeof(queries)/sizeof(*queries), USE_STORE_RESULT); DBUG_VOID_RETURN; } @@ -11880,7 +11893,8 @@ static void test_cursors_with_union() "SELECT t1.id FROM t1 WHERE t1.id < 5" }; myheader("test_cursors_with_union"); - fetch_n(queries, sizeof(queries)/sizeof(*queries)); + fetch_n(queries, sizeof(queries)/sizeof(*queries), USE_ROW_BY_ROW_FETCH); + fetch_n(queries, sizeof(queries)/sizeof(*queries), USE_STORE_RESULT); } /* From 3e89e1ccb2030705caa1d7d55556dbf3b83176be Mon Sep 17 00:00:00 2001 From: "andrey@lmy004." <> Date: Fri, 10 Feb 2006 15:02:57 +0100 Subject: [PATCH 04/20] fix for bug #17289 Events: missing privilege check for drop database Events were executed with all privileges possible on planet Earth :( WL#1034 --- mysql-test/r/events.result | 27 +++++++++++- mysql-test/t/events.test | 33 +++++++++++++++ sql/event.h | 6 +++ sql/event_executor.cc | 86 ++++++++++++++++++++++++++++---------- sql/event_timed.cc | 84 +++++++++++++++++++++++++++++++++++-- sql/sql_error.cc | 13 ++++-- 6 files changed, 219 insertions(+), 30 deletions(-) diff --git a/mysql-test/r/events.result b/mysql-test/r/events.result index b5b1b76f1a8..9b49ea24f26 100644 --- a/mysql-test/r/events.result +++ b/mysql-test/r/events.result @@ -1,5 +1,28 @@ create database if not exists events_test; use events_test; +CREATE USER pauline@localhost; +CREATE DATABASE db_x; +GRANT EVENT ON db_x.* TO pauline@localhost; +USE db_x; +CREATE TABLE x_table(a int); +CREATE EVENT e_x1 ON SCHEDULE EVERY 1 SECOND DO DROP DATABASE db_x; +CREATE EVENT e_x2 ON SCHEDULE EVERY 1 SECOND DO DROP TABLE x_table; +SHOW DATABASES LIKE 'db_x'; +Database (db_x) +db_x +SET GLOBAL event_scheduler=1; +SHOW DATABASES LIKE 'db_x'; +Database (db_x) +db_x +SHOW TABLES FROM db_x; +Tables_in_db_x +x_table +SET GLOBAL event_scheduler=0; +DROP EVENT e_x1; +DROP EVENT e_x2; +DROP DATABASE db_x; +DROP USER pauline@localhost; +USE events_test; drop event if exists event1; Warnings: Note 1305 Event event1 does not exist @@ -166,7 +189,7 @@ show processlist; Id User Host db Command Time State Info # root localhost events_test Query # NULL show processlist # event_scheduler NULL Connect # Sleeping NULL -# root events_test Connect # User lock select get_lock("test_lock2", 20) +# root localhost events_test Connect # User lock select get_lock("test_lock2", 20) "Release the mutex, the event worker should finish." select release_lock("test_lock2"); release_lock("test_lock2") @@ -184,6 +207,8 @@ set global event_scheduler=0; show processlist; Id User Host db Command Time State Info # root localhost events_test Query # NULL show processlist +# event_scheduler NULL Connect # Sleeping NULL +# root localhost events_test Connect # User lock select get_lock("test_lock2_1", 20) "Release the lock so the child process should finish. Hence the scheduler also" select release_lock("test_lock2_1"); release_lock("test_lock2_1") diff --git a/mysql-test/t/events.test b/mysql-test/t/events.test index aa759d5ca6f..5a58d29e0ff 100644 --- a/mysql-test/t/events.test +++ b/mysql-test/t/events.test @@ -1,5 +1,38 @@ create database if not exists events_test; use events_test; + +# +# START: BUG #17289 Events: missing privilege check for drop database +# +CREATE USER pauline@localhost; +CREATE DATABASE db_x; +GRANT EVENT ON db_x.* TO pauline@localhost; +USE db_x; +CREATE TABLE x_table(a int); +connect (priv_conn,localhost,pauline,,db_x); +CREATE EVENT e_x1 ON SCHEDULE EVERY 1 SECOND DO DROP DATABASE db_x; +CREATE EVENT e_x2 ON SCHEDULE EVERY 1 SECOND DO DROP TABLE x_table; +connection default; +SHOW DATABASES LIKE 'db_x'; +SET GLOBAL event_scheduler=1; +--sleep 2 +SHOW DATABASES LIKE 'db_x'; +SHOW TABLES FROM db_x; +SET GLOBAL event_scheduler=0; +--sleep 1 +connection priv_conn; +DROP EVENT e_x1; +DROP EVENT e_x2; +disconnect priv_conn; +connection default; +DROP DATABASE db_x; +DROP USER pauline@localhost; +USE events_test; +--sleep 1 +# +# END: BUG #17289 Events: missing privilege check for drop database +# + drop event if exists event1; create event event1 on schedule every 15 minute starts now() ends date_add(now(), interval 5 hour) DO begin end; alter event event1 rename to event2 enable; diff --git a/sql/event.h b/sql/event.h index 1fe5c8e5713..bff8c1d3d8e 100644 --- a/sql/event.h +++ b/sql/event.h @@ -203,6 +203,12 @@ public: delete sphead; sphead= 0; } +protected: + bool + change_security_context(THD *thd, Security_context **backup); + + void + restore_security_context(THD *thd, Security_context *backup); }; diff --git a/sql/event_executor.cc b/sql/event_executor.cc index d06e3e57a1e..d073b4a91bc 100644 --- a/sql/event_executor.cc +++ b/sql/event_executor.cc @@ -49,7 +49,8 @@ static uint workers_count; static int evex_load_events_from_db(THD *thd); - +bool +evex_print_warnings(THD *thd, event_timed *et); /* TODO Andrey: Check for command line option whether to start @@ -135,7 +136,8 @@ init_event_thread(THD* thd) { DBUG_ENTER("init_event_thread"); thd->client_capabilities= 0; - thd->security_ctx->skip_grants(); + thd->security_ctx->master_access= 0; + thd->security_ctx->db_access= 0; thd->security_ctx->host= (char*)my_localhost; my_net_init(&thd->net, 0); thd->net.read_timeout = slave_net_timeout; @@ -204,7 +206,7 @@ event_executor_main(void *arg) if (init_event_thread(thd)) goto err; - + thd->security_ctx->skip_grants(); // make this thread invisible it has no vio -> show processlist won't see thd->system_thread= 1; @@ -481,21 +483,8 @@ event_executor_worker(void *event_void) thd= current_thd; #endif - // thd->security_ctx->priv_host is char[MAX_HOSTNAME] - - strxnmov(thd->security_ctx->priv_host, sizeof(thd->security_ctx->priv_host), - event->definer_host.str, NullS); - - thd->security_ctx->user= thd->security_ctx->priv_user= - my_strdup(event->definer_user.str, MYF(0)); - - thd->db= event->dbname.str; - if (!check_access(thd, EVENT_ACL, event->dbname.str, 0, 0, 0, - is_schema_db(event->dbname.str))) { int ret; - DBUG_PRINT("info", (" EVEX EXECUTING event %s.%s [EXPR:%d]", - event->dbname.str, event->name.str,(int) event->expression)); sql_print_information(" EVEX EXECUTING event %s.%s [EXPR:%d]", event->dbname.str, event->name.str,(int) event->expression); @@ -507,10 +496,7 @@ event_executor_worker(void *event_void) if (ret == EVEX_COMPILE_ERROR) sql_print_information(" EVEX COMPILE ERROR for event %s.%s", event->dbname.str, event->name.str); - - DBUG_PRINT("info", (" EVEX EXECUTED event %s.%s [EXPR:%d]. RetCode=%d", - event->dbname.str, event->name.str, - (int) event->expression, ret)); + evex_print_warnings(thd, event); } if ((event->flags & EVENT_EXEC_NO_MORE) || event->status==MYSQL_EVENT_DISABLED) { @@ -521,7 +507,6 @@ event_executor_worker(void *event_void) delete event; } - thd->db= 0; err: VOID(pthread_mutex_lock(&LOCK_thread_count)); @@ -666,3 +651,62 @@ bool sys_var_event_executor::update(THD *thd, set_var *var) DBUG_RETURN(0); } +extern LEX_STRING warning_level_names[]; + +/* + Prints the stack of infos, warnings, errors from thd to + the console so it can be fetched by the logs-into-tables and + checked later. + + Synopsis + evex_print_warnings + thd - thread used during the execution of the event + et - the event itself + + Returns + 0 - OK (always) + +*/ + +bool +evex_print_warnings(THD *thd, event_timed *et) +{ + MYSQL_ERROR *err; + DBUG_ENTER("evex_show_warnings"); + char msg_buf[1024]; + char prefix_buf[512]; + String prefix(prefix_buf, sizeof(prefix_buf), system_charset_info); + prefix.length(0); + + List_iterator_fast it(thd->warn_list); + while ((err= it++)) + { + String err_msg(msg_buf, sizeof(msg_buf), system_charset_info); + err_msg.length(0);// set it to 0 or we start adding at the end + if (!prefix.length()) + { + prefix.append("SCHEDULER: ["); + + append_identifier(thd,&prefix,et->definer_user.str,et->definer_user.length); + prefix.append('@'); + append_identifier(thd,&prefix,et->definer_host.str,et->definer_host.length); + prefix.append("][", 2); + append_identifier(thd,&prefix, et->dbname.str, et->dbname.length); + prefix.append('.'); + append_identifier(thd,&prefix, et->name.str, et->name.length); + prefix.append("] ", 2); + } + + err_msg.append(prefix); + err_msg.append('['); + err_msg.append(warning_level_names[err->level].str, + warning_level_names[err->level].length, system_charset_info); + err_msg.append("] ["); + err_msg.append(err->msg, strlen(err->msg), system_charset_info); + err_msg.append("]"); + sql_print_information("%*s", err_msg.length(), err_msg.c_ptr()); + } + + + DBUG_RETURN(FALSE); +} diff --git a/sql/event_timed.cc b/sql/event_timed.cc index e585f6252ca..62fcf4cbf28 100644 --- a/sql/event_timed.cc +++ b/sql/event_timed.cc @@ -982,12 +982,13 @@ event_timed::get_show_create_event(THD *thd, uint32 *length) Executes the event (the underlying sp_head object); SYNOPSIS - evex_fill_row() + event_timed::execute() thd THD mem_root If != NULL use it to compile the event on it Returns 0 - success + -99 - No access to the database. -100 - event in execution (parallel execution is impossible) others - retcodes of sp_head::execute_procedure() @@ -996,10 +997,12 @@ event_timed::get_show_create_event(THD *thd, uint32 *length) int event_timed::execute(THD *thd, MEM_ROOT *mem_root) { - List empty_item_list; + Security_context *save_ctx; int ret= 0; DBUG_ENTER("event_timed::execute"); + DBUG_PRINT("info", (" EVEX EXECUTING event %s.%s [EXPR:%d]", + dbname.str, name.str, (int) expression)); VOID(pthread_mutex_lock(&this->LOCK_running)); if (running) @@ -1011,12 +1014,35 @@ event_timed::execute(THD *thd, MEM_ROOT *mem_root) VOID(pthread_mutex_unlock(&this->LOCK_running)); // TODO Andrey : make this as member variable and delete in destructor - empty_item_list.empty(); if (!sphead && (ret= compile(thd, mem_root))) goto done; - ret= sphead->execute_procedure(thd, &empty_item_list); + thd->db= dbname.str; + thd->db_length= dbname.length; + + DBUG_PRINT("info", ("master_access=%d db_access=%d", + thd->security_ctx->master_access, thd->security_ctx->db_access)); + change_security_context(thd, &save_ctx); + DBUG_PRINT("info", ("master_access=%d db_access=%d", + thd->security_ctx->master_access, thd->security_ctx->db_access)); +// if (mysql_change_db(thd, dbname.str, 0)) + if (!check_access(thd, EVENT_ACL,dbname.str, 0, 0, 0,is_schema_db(dbname.str))) + { + List empty_item_list; + empty_item_list.empty(); + ret= sphead->execute_procedure(thd, &empty_item_list); + } + else + { + DBUG_PRINT("error", ("%s@%s has no rights on %s", definer_user.str, + definer_host.str, dbname.str)); + ret= -99; + } + restore_security_context(thd, save_ctx); + DBUG_PRINT("info", ("master_access=%d db_access=%d", + thd->security_ctx->master_access, thd->security_ctx->db_access)); + thd->db= 0; VOID(pthread_mutex_lock(&this->LOCK_running)); running= false; @@ -1029,11 +1055,61 @@ done: delete sphead; sphead= 0; } + DBUG_PRINT("info", (" EVEX EXECUTED event %s.%s [EXPR:%d]. RetCode=%d", + dbname.str, name.str, (int) expression, ret)); DBUG_RETURN(ret); } +/* + Switches the security context + Synopsis + event_timed::change_security_context() + thd - thread + backup - where to store the old context + + RETURN + 0 - OK + 1 - Error (generates error too) +*/ +bool +event_timed::change_security_context(THD *thd, Security_context **backup) +{ + DBUG_ENTER("event_timed::change_security_context"); + DBUG_PRINT("info",("%s@%s@%s",definer_user.str,definer_host.str, dbname.str)); + *backup= 0; + if (acl_getroot_no_password(&sphead->m_security_ctx, definer_user.str, + definer_host.str, definer_host.str, dbname.str)) + { + my_error(ER_NO_SUCH_USER, MYF(0), definer_user.str, definer_host.str); + DBUG_RETURN(TRUE); + } + *backup= thd->security_ctx; + thd->security_ctx= &sphead->m_security_ctx; + + DBUG_RETURN(FALSE); +} + + +/* + Restores the security context + Synopsis + event_timed::restore_security_context() + thd - thread + backup - switch to this context + */ + +void +event_timed::restore_security_context(THD *thd, Security_context *backup) +{ + DBUG_ENTER("event_timed::change_security_context"); + if (backup) + thd->security_ctx= backup; + DBUG_VOID_RETURN; +} + + /* Returns 0 - Success diff --git a/sql/sql_error.cc b/sql/sql_error.cc index 191a6e0a1fd..74df3b68a0d 100644 --- a/sql/sql_error.cc +++ b/sql/sql_error.cc @@ -211,8 +211,13 @@ void push_warning_printf(THD *thd, MYSQL_ERROR::enum_warning_level level, TRUE Error sending data to client */ -static const char *warning_level_names[]= {"Note", "Warning", "Error", "?"}; -static int warning_level_length[]= { 4, 7, 5, 1 }; +LEX_STRING warning_level_names[]= +{ + {(char*) STRING_WITH_LEN("Note")}, + {(char*) STRING_WITH_LEN("Warning")}, + {(char*) STRING_WITH_LEN("Error")}, + {(char*) STRING_WITH_LEN("?")} +}; bool mysqld_show_warnings(THD *thd, ulong levels_to_show) { @@ -246,8 +251,8 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show) if (idx > unit->select_limit_cnt) break; protocol->prepare_for_resend(); - protocol->store(warning_level_names[err->level], - warning_level_length[err->level], system_charset_info); + protocol->store(warning_level_names[err->level].str, + warning_level_names[err->level].length, system_charset_info); protocol->store((uint32) err->code); protocol->store(err->msg, strlen(err->msg), system_charset_info); if (protocol->write()) From 7cdfacde683778c13296f148251b5e8e52593ff7 Mon Sep 17 00:00:00 2001 From: "jimw@mysql.com" <> Date: Mon, 13 Feb 2006 10:15:42 -0800 Subject: [PATCH 05/20] Bug #16775: DROP TABLE of subpartitioned table can fail if default engine used. The problem is that the actual engine was not stored in the .par file, causing confusion when the default engine was changed. Now the actual storage engine is stored for subpartitions, as was intended. --- mysql-test/r/partition.result | 8 ++++++++ mysql-test/t/partition.test | 12 ++++++++++++ sql/ha_partition.cc | 5 +++-- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/partition.result b/mysql-test/r/partition.result index a638babc365..77efcf847e9 100644 --- a/mysql-test/r/partition.result +++ b/mysql-test/r/partition.result @@ -278,3 +278,11 @@ partition p1 values in (14) insert into t1 values (10,1); ERROR HY000: Table has no partition for value 11 drop table t1; +set session storage_engine= 'memory'; +create table t1 (f_int1 int(11) default null) engine = memory +partition by range (f_int1) subpartition by hash (f_int1) +(partition part1 values less than (1000) +(subpartition subpart11 engine = memory)); +set session storage_engine='myisam'; +drop table t1; +End of 5.1 tests diff --git a/mysql-test/t/partition.test b/mysql-test/t/partition.test index 301c1971c91..c4497443ee9 100644 --- a/mysql-test/t/partition.test +++ b/mysql-test/t/partition.test @@ -353,3 +353,15 @@ insert into t1 values (10,1); drop table t1; +# +# Bug #16775: Wrong engine type stored for subpartition +# +set session storage_engine= 'memory'; +create table t1 (f_int1 int(11) default null) engine = memory + partition by range (f_int1) subpartition by hash (f_int1) + (partition part1 values less than (1000) + (subpartition subpart11 engine = memory)); +set session storage_engine='myisam'; +drop table t1; + +--echo End of 5.1 tests diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 2bc4a106536..babc8a0e969 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -1937,7 +1937,8 @@ bool ha_partition::create_handler_file(const char *name) name_buffer_ptr+= name_add(name_buffer_ptr, part_name, subpart_name); - *engine_array= (uchar) ha_legacy_type(part_elem->engine_type); + *engine_array= (uchar) ha_legacy_type(subpart_elem->engine_type); + DBUG_PRINT("info", ("engine: %u", *engine_array)); engine_array++; } } @@ -1954,7 +1955,7 @@ bool ha_partition::create_handler_file(const char *name) Create and write and close file to be used at open, delete_table and rename_table */ - fn_format(file_name, name, "", ".par", MY_APPEND_EXT); + fn_format(file_name, name, "", ha_par_ext, MY_APPEND_EXT); if ((file= my_create(file_name, CREATE_MODE, O_RDWR | O_TRUNC, MYF(MY_WME))) >= 0) { From 52183ddd788c790205c24622fa87b7105ae2f9e2 Mon Sep 17 00:00:00 2001 From: "msvensson@neptunus.(none)" <> Date: Tue, 14 Feb 2006 17:15:24 +0100 Subject: [PATCH 06/20] Change from std_data to std_data_ln --- mysql-test/r/ndb_load.result | 4 ++-- mysql-test/t/ndb_load.test | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/ndb_load.result b/mysql-test/r/ndb_load.result index 76da5b2a215..416a350066b 100644 --- a/mysql-test/r/ndb_load.result +++ b/mysql-test/r/ndb_load.result @@ -1,10 +1,10 @@ DROP TABLE IF EXISTS t1; CREATE TABLE t1 (word CHAR(20) NOT NULL PRIMARY KEY) ENGINE=NDB; -LOAD DATA INFILE '../../std_data/words.dat' INTO TABLE t1 ; +LOAD DATA INFILE '../std_data_ln/words.dat' INTO TABLE t1 ; ERROR 23000: Can't write; duplicate key in table 't1' DROP TABLE t1; CREATE TABLE t1 (word CHAR(20) NOT NULL) ENGINE=NDB; -LOAD DATA INFILE '../../std_data/words.dat' INTO TABLE t1 ; +LOAD DATA INFILE '../std_data_ln/words.dat' INTO TABLE t1 ; SELECT * FROM t1 ORDER BY word; word Aarhus diff --git a/mysql-test/t/ndb_load.test b/mysql-test/t/ndb_load.test index 72a5b53eaad..af2df70b74e 100644 --- a/mysql-test/t/ndb_load.test +++ b/mysql-test/t/ndb_load.test @@ -12,12 +12,12 @@ DROP TABLE IF EXISTS t1; # should give duplicate key CREATE TABLE t1 (word CHAR(20) NOT NULL PRIMARY KEY) ENGINE=NDB; --error 1022 -LOAD DATA INFILE '../../std_data/words.dat' INTO TABLE t1 ; +LOAD DATA INFILE '../std_data_ln/words.dat' INTO TABLE t1 ; DROP TABLE t1; # now without a primary key we should be ok CREATE TABLE t1 (word CHAR(20) NOT NULL) ENGINE=NDB; -LOAD DATA INFILE '../../std_data/words.dat' INTO TABLE t1 ; +LOAD DATA INFILE '../std_data_ln/words.dat' INTO TABLE t1 ; SELECT * FROM t1 ORDER BY word; DROP TABLE t1; From dc5bb004678a1a2a0cd8ac378158f52621bef574 Mon Sep 17 00:00:00 2001 From: "msvensson@neptunus.(none)" <> Date: Tue, 14 Feb 2006 17:21:18 +0100 Subject: [PATCH 07/20] Enable ndb_load test case --- mysql-test/t/disabled.def | 1 - 1 file changed, 1 deletion(-) diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index 09a11578096..46f15983dc3 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -12,4 +12,3 @@ sp-goto : GOTO is currently is disabled - will be fixed in the future subselect : Bug#15706 -ndb_load : Bug #17233 From 446c791e567ae00c70fe51fb96d2481e41f4be76 Mon Sep 17 00:00:00 2001 From: "andrey@lmy004." <> Date: Tue, 14 Feb 2006 17:51:22 +0100 Subject: [PATCH 08/20] post-merge fixes --- sql/event_executor.cc | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/sql/event_executor.cc b/sql/event_executor.cc index d073b4a91bc..970814f291b 100644 --- a/sql/event_executor.cc +++ b/sql/event_executor.cc @@ -206,8 +206,11 @@ event_executor_main(void *arg) if (init_event_thread(thd)) goto err; - thd->security_ctx->skip_grants(); - // make this thread invisible it has no vio -> show processlist won't see + + /* + make this thread visible it has no vio -> show processlist won't see it + unless it's marked as system thread + */ thd->system_thread= 1; VOID(pthread_mutex_lock(&LOCK_thread_count)); @@ -653,6 +656,14 @@ bool sys_var_event_executor::update(THD *thd, set_var *var) extern LEX_STRING warning_level_names[]; +typedef void (*sql_print_xxx_func)(const char *format, ...); +static sql_print_xxx_func sql_print_xxx_handlers[3] = +{ + sql_print_information, + sql_print_warning, + sql_print_error +}; + /* Prints the stack of infos, warnings, errors from thd to the console so it can be fetched by the logs-into-tables and @@ -704,7 +715,9 @@ evex_print_warnings(THD *thd, event_timed *et) err_msg.append("] ["); err_msg.append(err->msg, strlen(err->msg), system_charset_info); err_msg.append("]"); - sql_print_information("%*s", err_msg.length(), err_msg.c_ptr()); + DBUG_ASSERT(err->level < 3); + (sql_print_xxx_handlers[err->level])("%*s", err_msg.length(), err_msg.c_ptr()); +// sql_print_information("%*s", err_msg.length(), err_msg.c_ptr()); } From e50298980c9fd9e9c75102c16c56d301a117295e Mon Sep 17 00:00:00 2001 From: "stewart@mysql.com" <> Date: Wed, 15 Feb 2006 16:37:09 +1100 Subject: [PATCH 09/20] BUG#17411 cannot have path longer than 128 characters Use the POSIX limits.h define of PATH_MAX for maximum path length. This has been being hit in pushbuild --- ndb/include/util/File.hpp | 4 +--- ndb/src/common/portlib/NdbConfig.c | 34 +++++++++++++++--------------- ndb/src/common/util/File.cpp | 4 ++-- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/ndb/include/util/File.hpp b/ndb/include/util/File.hpp index 3ed0ad7a6f9..53ea88e0f52 100644 --- a/ndb/include/util/File.hpp +++ b/ndb/include/util/File.hpp @@ -191,10 +191,8 @@ public: int flush() const; private: - STATIC_CONST( MAX_FILE_NAME_SIZE = 128 ); - FILE* m_file; - char m_fileName[MAX_FILE_NAME_SIZE]; + char m_fileName[PATH_MAX]; const char* m_fileMode; /* Prohibit */ File_class (const File_class& aCopy); diff --git a/ndb/src/common/portlib/NdbConfig.c b/ndb/src/common/portlib/NdbConfig.c index b275143646f..c3f37727024 100644 --- a/ndb/src/common/portlib/NdbConfig.c +++ b/ndb/src/common/portlib/NdbConfig.c @@ -64,11 +64,11 @@ NdbConfig_NdbCfgName(int with_ndb_home){ int len= 0; if (with_ndb_home) { - buf= NdbConfig_AllocHomePath(128); + buf= NdbConfig_AllocHomePath(PATH_MAX); len= strlen(buf); } else - buf= NdbMem_Allocate(128); - basestring_snprintf(buf+len, 128, "Ndb.cfg"); + buf= NdbMem_Allocate(PATH_MAX); + basestring_snprintf(buf+len, PATH_MAX, "Ndb.cfg"); return buf; } @@ -90,56 +90,56 @@ char *get_prefix_buf(int len, int node_id) char* NdbConfig_ErrorFileName(int node_id){ - char *buf= get_prefix_buf(128, node_id); + char *buf= get_prefix_buf(PATH_MAX, node_id); int len= strlen(buf); - basestring_snprintf(buf+len, 128, "_error.log"); + basestring_snprintf(buf+len, PATH_MAX, "_error.log"); return buf; } char* NdbConfig_ClusterLogFileName(int node_id){ - char *buf= get_prefix_buf(128, node_id); + char *buf= get_prefix_buf(PATH_MAX, node_id); int len= strlen(buf); - basestring_snprintf(buf+len, 128, "_cluster.log"); + basestring_snprintf(buf+len, PATH_MAX, "_cluster.log"); return buf; } char* NdbConfig_SignalLogFileName(int node_id){ - char *buf= get_prefix_buf(128, node_id); + char *buf= get_prefix_buf(PATH_MAX, node_id); int len= strlen(buf); - basestring_snprintf(buf+len, 128, "_signal.log"); + basestring_snprintf(buf+len, PATH_MAX, "_signal.log"); return buf; } char* NdbConfig_TraceFileName(int node_id, int file_no){ - char *buf= get_prefix_buf(128, node_id); + char *buf= get_prefix_buf(PATH_MAX, node_id); int len= strlen(buf); - basestring_snprintf(buf+len, 128, "_trace.log.%u", file_no); + basestring_snprintf(buf+len, PATH_MAX, "_trace.log.%u", file_no); return buf; } char* NdbConfig_NextTraceFileName(int node_id){ - char *buf= get_prefix_buf(128, node_id); + char *buf= get_prefix_buf(PATH_MAX, node_id); int len= strlen(buf); - basestring_snprintf(buf+len, 128, "_trace.log.next"); + basestring_snprintf(buf+len, PATH_MAX, "_trace.log.next"); return buf; } char* NdbConfig_PidFileName(int node_id){ - char *buf= get_prefix_buf(128, node_id); + char *buf= get_prefix_buf(PATH_MAX, node_id); int len= strlen(buf); - basestring_snprintf(buf+len, 128, ".pid"); + basestring_snprintf(buf+len, PATH_MAX, ".pid"); return buf; } char* NdbConfig_StdoutFileName(int node_id){ - char *buf= get_prefix_buf(128, node_id); + char *buf= get_prefix_buf(PATH_MAX, node_id); int len= strlen(buf); - basestring_snprintf(buf+len, 128, "_out.log"); + basestring_snprintf(buf+len, PATH_MAX, "_out.log"); return buf; } diff --git a/ndb/src/common/util/File.cpp b/ndb/src/common/util/File.cpp index e514ad8e122..52ad3a4a51e 100644 --- a/ndb/src/common/util/File.cpp +++ b/ndb/src/common/util/File.cpp @@ -67,7 +67,7 @@ File_class::File_class(const char* aFileName, const char* mode) : m_file(NULL), m_fileMode(mode) { - BaseString::snprintf(m_fileName, MAX_FILE_NAME_SIZE, aFileName); + BaseString::snprintf(m_fileName, PATH_MAX, aFileName); } bool @@ -83,7 +83,7 @@ File_class::open(const char* aFileName, const char* mode) /** * Only copy if it's not the same string */ - BaseString::snprintf(m_fileName, MAX_FILE_NAME_SIZE, aFileName); + BaseString::snprintf(m_fileName, PATH_MAX, aFileName); } m_fileMode = mode; bool rc = true; From a01ed154195ea67269045fe3f9665add913e2c19 Mon Sep 17 00:00:00 2001 From: "andrey@lmy004." <> Date: Wed, 15 Feb 2006 12:14:23 +0100 Subject: [PATCH 10/20] cosmetic changes of the fix for bug#17289 (no privileges checks in EVENTS), before push --- sql/event_executor.cc | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/sql/event_executor.cc b/sql/event_executor.cc index c0c3bca4375..32584b3ea33 100644 --- a/sql/event_executor.cc +++ b/sql/event_executor.cc @@ -638,18 +638,18 @@ event_executor_worker(void *event_void) { int ret; - sql_print_information(" EVEX EXECUTING event %s.%s [EXPR:%d]", + sql_print_information("SCHEDULER: Executing event %s.%s [EXPR:%d]", event->dbname.str, event->name.str,(int) event->expression); ret= event->execute(thd, &worker_mem_root); - sql_print_information(" EVEX EXECUTED event %s.%s [EXPR:%d]. RetCode=%d", + evex_print_warnings(thd, event); + sql_print_information("SCHEDULER: Executed event %s.%s [EXPR:%d]. RetCode=%d", event->dbname.str, event->name.str, (int) event->expression, ret); if (ret == EVEX_COMPILE_ERROR) - sql_print_information(" EVEX COMPILE ERROR for event %s.%s", + sql_print_information("SCHEDULER: COMPILE ERROR for event %s.%s", event->dbname.str, event->name.str); - evex_print_warnings(thd, event); } if ((event->flags & EVENT_EXEC_NO_MORE) || event->status==MYSQL_EVENT_DISABLED) { @@ -837,6 +837,7 @@ sys_var_event_executor::update(THD *thd, set_var *var) DBUG_RETURN(0); } + extern LEX_STRING warning_level_names[]; typedef void (*sql_print_xxx_func)(const char *format, ...); @@ -847,6 +848,7 @@ static sql_print_xxx_func sql_print_xxx_handlers[3] = sql_print_error }; + /* Prints the stack of infos, warnings, errors from thd to the console so it can be fetched by the logs-into-tables and @@ -892,15 +894,10 @@ evex_print_warnings(THD *thd, event_timed *et) } err_msg.append(prefix); - err_msg.append('['); - err_msg.append(warning_level_names[err->level].str, - warning_level_names[err->level].length, system_charset_info); - err_msg.append("] ["); err_msg.append(err->msg, strlen(err->msg), system_charset_info); err_msg.append("]"); DBUG_ASSERT(err->level < 3); (sql_print_xxx_handlers[err->level])("%*s", err_msg.length(), err_msg.c_ptr()); -// sql_print_information("%*s", err_msg.length(), err_msg.c_ptr()); } From eb978186277614960d82c1924449b9806655768a Mon Sep 17 00:00:00 2001 From: "msvensson@devsrv-b.mysql.com" <> Date: Wed, 15 Feb 2006 13:45:03 +0100 Subject: [PATCH 11/20] Bug#16143 mysql_stmt_sqlstate returns an empty string instead of '00000' - Init sql_state in mysql_stmt_init --- libmysql/libmysql.c | 1 + tests/mysql_client_test.c | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 9825f1ecdfa..3ff4cfb4c50 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -2008,6 +2008,7 @@ mysql_stmt_init(MYSQL *mysql) stmt->mysql= mysql; stmt->read_row_func= stmt_read_row_no_result_set; stmt->prefetch_rows= DEFAULT_PREFETCH_ROWS; + strmov(stmt->sqlstate, not_error_sqlstate); /* The rest of statement members was bzeroed inside malloc */ DBUG_RETURN(stmt); diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 666c60391da..241a994d07c 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -14729,6 +14729,21 @@ static void test_bug12744() client_connect(0); } +/* Bug #16143: mysql_stmt_sqlstate returns an empty string instead of '00000' */ + +static void test_bug16143() +{ + MYSQL_STMT *stmt; + myheader("test_bug16143"); + + stmt= mysql_stmt_init(mysql); + /* Check mysql_stmt_sqlstate return "no error" */ + DIE_UNLESS(strcmp(mysql_stmt_sqlstate(stmt), "00000") == 0); + + mysql_stmt_close(stmt); +} + + /* Bug #16144: mysql_stmt_attr_get type error */ static void test_bug16144() @@ -15073,6 +15088,7 @@ static struct my_tests_st my_tests[]= { { "test_bug15510", test_bug15510 }, { "test_opt_reconnect", test_opt_reconnect }, { "test_bug12744", test_bug12744 }, + { "test_bug16143", test_bug16143 }, { "test_bug16144", test_bug16144 }, { "test_bug15613", test_bug15613 }, { 0, 0 } From e145908eb38237c2f5c9818388f1778fee6b03e8 Mon Sep 17 00:00:00 2001 From: "jimw@mysql.com" <> Date: Wed, 15 Feb 2006 11:20:57 -0800 Subject: [PATCH 12/20] Bug #16782: Partitions: crash, REPLACE .. on table with PK, DUPLICATE KEY event. Partitioning wrongly claimed to be able to handle HA_DUPP_POS when it was supported by the underlying storage engine, which resulted in a crash when handling REPLACE statements. --- mysql-test/r/partition.result | 6 ++++++ mysql-test/t/partition.test | 10 ++++++++++ sql/ha_partition.cc | 7 ++++--- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/mysql-test/r/partition.result b/mysql-test/r/partition.result index a638babc365..04f96dd4266 100644 --- a/mysql-test/r/partition.result +++ b/mysql-test/r/partition.result @@ -278,3 +278,9 @@ partition p1 values in (14) insert into t1 values (10,1); ERROR HY000: Table has no partition for value 11 drop table t1; +create table t1 (f_int1 integer, f_int2 integer, primary key (f_int1)) +partition by hash(f_int1) partitions 2; +insert into t1 values (1,1),(2,2); +replace into t1 values (1,1),(2,2); +drop table t1; +End of 5.1 tests diff --git a/mysql-test/t/partition.test b/mysql-test/t/partition.test index 301c1971c91..a262883373e 100644 --- a/mysql-test/t/partition.test +++ b/mysql-test/t/partition.test @@ -353,3 +353,13 @@ insert into t1 values (10,1); drop table t1; +# +# Bug #16782: Crash using REPLACE on table with primary key +# +create table t1 (f_int1 integer, f_int2 integer, primary key (f_int1)) + partition by hash(f_int1) partitions 2; +insert into t1 values (1,1),(2,2); +replace into t1 values (1,1),(2,2); +drop table t1; + +--echo End of 5.1 tests diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 50299dffd85..a71b13bed17 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -359,7 +359,7 @@ int ha_partition::ha_initialise() other parameters are calculated on demand. HA_FILE_BASED is always set for partition handler since we use a special file for handling names of partitions, engine types. - HA_CAN_GEOMETRY, HA_CAN_FULLTEXT, HA_CAN_SQL_HANDLER, + HA_CAN_GEOMETRY, HA_CAN_FULLTEXT, HA_CAN_SQL_HANDLER, HA_DUPP_POS, HA_CAN_INSERT_DELAYED is disabled until further investigated. */ m_table_flags= m_file[0]->table_flags(); @@ -382,8 +382,8 @@ int ha_partition::ha_initialise() m_pkey_is_clustered= FALSE; m_table_flags&= file->table_flags(); } while (*(++file_array)); - m_table_flags&= ~(HA_CAN_GEOMETRY & HA_CAN_FULLTEXT & - HA_CAN_SQL_HANDLER & HA_CAN_INSERT_DELAYED); + m_table_flags&= ~(HA_CAN_GEOMETRY | HA_CAN_FULLTEXT | HA_DUPP_POS | + HA_CAN_SQL_HANDLER | HA_CAN_INSERT_DELAYED); m_table_flags|= HA_FILE_BASED | HA_REC_NOT_IN_SEQ; DBUG_RETURN(0); } @@ -4688,6 +4688,7 @@ int ha_partition::extra(enum ha_extra_function operation) case HA_EXTRA_PREPARE_FOR_UPDATE: case HA_EXTRA_PREPARE_FOR_DELETE: case HA_EXTRA_FORCE_REOPEN: + case HA_EXTRA_FLUSH_CACHE: { if (m_myisam) DBUG_RETURN(loop_extra(operation)); From 8193669f0ac301f2be5e1056ba1c1bb790a5448b Mon Sep 17 00:00:00 2001 From: "andrey@lmy004." <> Date: Wed, 15 Feb 2006 21:40:38 +0100 Subject: [PATCH 13/20] cosmetic post-review changes --- mysql-test/r/events.result | 16 ++++++++-------- sql/event_executor.cc | 16 +++++++++------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/mysql-test/r/events.result b/mysql-test/r/events.result index 32c43d3c7a8..ec5bb448b04 100644 --- a/mysql-test/r/events.result +++ b/mysql-test/r/events.result @@ -129,10 +129,10 @@ SHOW CREATE EVENT root20; Event sql_mode Create Event root20 CREATE EVENT `events_test`.`root20` ON SCHEDULE EVERY '50 20:12:45' DAY_SECOND ON COMPLETION NOT PRESERVE ENABLE DO select 1 set names cp1251; -create event ðóóò21 on schedule every '50:23:59:95' day_second COMMENT 'òîâà Ã¥ 1251 êîìåíòàð' do select 1; -SHOW CREATE EVENT ðóóò21; +create event ðóóò21 on schedule every '50:23:59:95' day_second COMMENT 'òîâà å 1251 êîìåíòàð' do select 1; +SHOW CREATE EVENT ðóóò21; Event sql_mode Create Event -ðóóò21 CREATE EVENT `events_test`.`ðóóò21` ON SCHEDULE EVERY '51 0:0:35' DAY_SECOND ON COMPLETION NOT PRESERVE ENABLE COMMENT 'òîâà Ã¥ 1251 êîìåíòàð' DO select 1 +ðóóò21 CREATE EVENT `events_test`.`ðóóò21` ON SCHEDULE EVERY '51 0:0:35' DAY_SECOND ON COMPLETION NOT PRESERVE ENABLE COMMENT 'òîâà å 1251 êîìåíòàð' DO select 1 insert into mysql.event (db, name, body, definer, interval_value, interval_field) values (database(), "root22", "select 1", user(), 100, "SECOND_MICROSECOND"); show create event root22; ERROR 42000: This version of MySQL doesn't yet support 'MICROSECOND' @@ -157,7 +157,7 @@ drop event root17_1; drop event root18; drop event root19; drop event root20; -drop event ðóóò21; +drop event ðóóò21; set names latin1; CREATE EVENT intact_check ON SCHEDULE EVERY 10 HOUR DO SELECT "nothing"; SHOW EVENTS; @@ -354,8 +354,8 @@ create event закачка on schedule every 10 hour do select get_lock("test_l show processlist; Id User Host db Command Time State Info # root localhost events_test Query # NULL show processlist -# event_scheduler NULL Connect # Sleeping NULL -# root events_test Connect # User lock select get_lock("test_lock2", 20) +# event_scheduler connecting host NULL Connect # Sleeping NULL +# root localhost events_test Connect # User lock select get_lock("test_lock2", 20) "Release the mutex, the event worker should finish." select release_lock("test_lock2"); release_lock("test_lock2") @@ -373,8 +373,8 @@ set global event_scheduler=0; show processlist; Id User Host db Command Time State Info # root localhost events_test Query # NULL show processlist -# event_scheduler NULL Connect # Sleeping NULL -# root events_test Connect # User lock select get_lock("test_lock2_1", 20) +# event_scheduler connecting host NULL Connect # Sleeping NULL +# root localhost events_test Connect # User lock select get_lock("test_lock2_1", 20) "Release the lock so the child process should finish. Hence the scheduler also" select release_lock("test_lock2_1"); release_lock("test_lock2_1") diff --git a/sql/event_executor.cc b/sql/event_executor.cc index 32584b3ea33..43be372e96c 100644 --- a/sql/event_executor.cc +++ b/sql/event_executor.cc @@ -638,18 +638,20 @@ event_executor_worker(void *event_void) { int ret; - sql_print_information("SCHEDULER: Executing event %s.%s [EXPR:%d]", - event->dbname.str, event->name.str,(int) event->expression); + sql_print_information("SCHEDULER: Executing event %s.%s of %s [EXPR:%d]", + event->dbname.str, event->name.str, + event->definer.str, (int) event->expression); ret= event->execute(thd, &worker_mem_root); evex_print_warnings(thd, event); - sql_print_information("SCHEDULER: Executed event %s.%s [EXPR:%d]. RetCode=%d", - event->dbname.str, event->name.str, - (int) event->expression, ret); + sql_print_information("SCHEDULER: Executed event %s.%s of %s [EXPR:%d]. " + "RetCode=%d", event->dbname.str, event->name.str, + event->definer.str, (int) event->expression, ret); if (ret == EVEX_COMPILE_ERROR) - sql_print_information("SCHEDULER: COMPILE ERROR for event %s.%s", - event->dbname.str, event->name.str); + sql_print_information("SCHEDULER: COMPILE ERROR for event %s.%s of", + event->dbname.str, event->name.str, + event->definer.str); } if ((event->flags & EVENT_EXEC_NO_MORE) || event->status==MYSQL_EVENT_DISABLED) { From a613782f96b60b10a84bedb14e1a6f4e2ed572c3 Mon Sep 17 00:00:00 2001 From: "andrey@lmy004." <> Date: Wed, 15 Feb 2006 22:32:25 +0100 Subject: [PATCH 14/20] more cosmetic before push of fix for bug#17289 --- sql/event_timed.cc | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sql/event_timed.cc b/sql/event_timed.cc index b888ff89f05..c6831bfaf8c 100644 --- a/sql/event_timed.cc +++ b/sql/event_timed.cc @@ -1061,6 +1061,7 @@ event_timed::get_create_event(THD *thd, String *buf) RETURNS 0 success + -99 No rights on this.dbname.str -100 event in execution (parallel execution is impossible) others retcodes of sp_head::execute_procedure() */ @@ -1089,16 +1090,12 @@ event_timed::execute(THD *thd, MEM_ROOT *mem_root) if (!sphead && (ret= compile(thd, mem_root))) goto done; - thd->db= dbname.str; - thd->db_length= dbname.length; - DBUG_PRINT("info", ("master_access=%d db_access=%d", thd->security_ctx->master_access, thd->security_ctx->db_access)); change_security_context(thd, &save_ctx); DBUG_PRINT("info", ("master_access=%d db_access=%d", thd->security_ctx->master_access, thd->security_ctx->db_access)); -// if (mysql_change_db(thd, dbname.str, 0)) - if (!check_access(thd, EVENT_ACL,dbname.str, 0, 0, 0,is_schema_db(dbname.str))) + if (mysql_change_db(thd, dbname.str, 0)) { List empty_item_list; empty_item_list.empty(); @@ -1113,7 +1110,6 @@ event_timed::execute(THD *thd, MEM_ROOT *mem_root) restore_security_context(thd, save_ctx); DBUG_PRINT("info", ("master_access=%d db_access=%d", thd->security_ctx->master_access, thd->security_ctx->db_access)); - thd->db= 0; VOID(pthread_mutex_lock(&this->LOCK_running)); running= false; From 00b188cc5223c219493a38a311af2b56a506516a Mon Sep 17 00:00:00 2001 From: "andrey@lmy004." <> Date: Wed, 15 Feb 2006 23:43:11 +0100 Subject: [PATCH 15/20] build fixes --- sql/event.cc | 2 +- sql/event_timed.cc | 7 ++++++- sql/set_var.cc | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/sql/event.cc b/sql/event.cc index 2dfede2cb63..c0a043aaf09 100644 --- a/sql/event.cc +++ b/sql/event.cc @@ -1239,7 +1239,7 @@ int db_drop_event(THD *thd, event_timed *et, bool drop_if_exists, { TABLE *table; Open_tables_state backup; - uint ret; + int ret; DBUG_ENTER("db_drop_event"); ret= EVEX_OPEN_TABLE_FAILED; diff --git a/sql/event_timed.cc b/sql/event_timed.cc index c6831bfaf8c..34e9b50f7a7 100644 --- a/sql/event_timed.cc +++ b/sql/event_timed.cc @@ -1093,6 +1093,7 @@ event_timed::execute(THD *thd, MEM_ROOT *mem_root) DBUG_PRINT("info", ("master_access=%d db_access=%d", thd->security_ctx->master_access, thd->security_ctx->db_access)); change_security_context(thd, &save_ctx); + DBUG_PRINT("info", ("master_access=%d db_access=%d", thd->security_ctx->master_access, thd->security_ctx->db_access)); if (mysql_change_db(thd, dbname.str, 0)) @@ -1108,6 +1109,7 @@ event_timed::execute(THD *thd, MEM_ROOT *mem_root) ret= -99; } restore_security_context(thd, save_ctx); + DBUG_PRINT("info", ("master_access=%d db_access=%d", thd->security_ctx->master_access, thd->security_ctx->db_access)); @@ -1145,6 +1147,7 @@ event_timed::change_security_context(THD *thd, Security_context **backup) { DBUG_ENTER("event_timed::change_security_context"); DBUG_PRINT("info",("%s@%s@%s",definer_user.str,definer_host.str, dbname.str)); +#ifndef NO_EMBEDDED_ACCESS_CHECKS *backup= 0; if (acl_getroot_no_password(&sphead->m_security_ctx, definer_user.str, definer_host.str, definer_host.str, dbname.str)) @@ -1154,7 +1157,7 @@ event_timed::change_security_context(THD *thd, Security_context **backup) } *backup= thd->security_ctx; thd->security_ctx= &sphead->m_security_ctx; - +#endif DBUG_RETURN(FALSE); } @@ -1171,8 +1174,10 @@ void event_timed::restore_security_context(THD *thd, Security_context *backup) { DBUG_ENTER("event_timed::change_security_context"); +#ifndef NO_EMBEDDED_ACCESS_CHECKS if (backup) thd->security_ctx= backup; +#endif DBUG_VOID_RETURN; } diff --git a/sql/set_var.cc b/sql/set_var.cc index c9ba454f80c..c9feea49618 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -3488,7 +3488,7 @@ byte *sys_var_thd_dbug::value_ptr(THD *thd, enum_var_type type, LEX_STRING *b) DBUG_EXPLAIN_INITIAL(buf, sizeof(buf)); else DBUG_EXPLAIN(buf, sizeof(buf)); - (byte*) thd->strdup(buf); + return (byte*) thd->strdup(buf); } /**************************************************************************** From cd8f8449e21e3abf1f38930144df1c6d13ab1c00 Mon Sep 17 00:00:00 2001 From: "tomas@poseidon.ndb.mysql.com" <> Date: Thu, 16 Feb 2006 00:30:56 +0100 Subject: [PATCH 16/20] Bug #17414 ndb schema distribution functionality does not work on mysql servers without binlog --- mysql-test/r/ndb_alter_table_stm.result | 2 - mysql-test/r/ndb_multi.result | 15 ------ mysql-test/t/ndb_alter_table_stm.test | 2 - mysql-test/t/ndb_multi.test | 15 +++--- sql/ha_ndbcluster.cc | 41 ++++++++++------- sql/ha_ndbcluster_binlog.cc | 61 +++++++++++++++---------- 6 files changed, 69 insertions(+), 67 deletions(-) diff --git a/mysql-test/r/ndb_alter_table_stm.result b/mysql-test/r/ndb_alter_table_stm.result index d05c2ba1c5c..9c1d09a8970 100644 --- a/mysql-test/r/ndb_alter_table_stm.result +++ b/mysql-test/r/ndb_alter_table_stm.result @@ -8,8 +8,6 @@ a b c 2 two two alter table t1 drop index c; select * from t1 where c = 'two'; -ERROR HY000: Table definition has changed, please retry transaction -select * from t1 where c = 'two'; a b c 2 two two drop table t1; diff --git a/mysql-test/r/ndb_multi.result b/mysql-test/r/ndb_multi.result index 5147d052d6e..723712d99ad 100644 --- a/mysql-test/r/ndb_multi.result +++ b/mysql-test/r/ndb_multi.result @@ -30,14 +30,6 @@ drop table t1; create table t1 (a int) engine=ndbcluster; insert into t1 value (2); select * from t1; -ERROR HY000: Table definition has changed, please retry transaction -show warnings; -Level Code Message -Error 1296 Got error 241 'Invalid schema object version' from NDB -Error 1412 Table definition has changed, please retry transaction -Error 1105 Unknown error -flush table t1; -select * from t1; a 2 flush status; @@ -58,15 +50,9 @@ a select * from t3; a b c last_col 1 Hi! 89 Longtext column -show status like 'handler_discover%'; -Variable_name Value -Handler_discover 1 show tables like 't4'; Tables_in_test (t4) t4 -show status like 'handler_discover%'; -Variable_name Value -Handler_discover 2 show tables; Tables_in_test t1 @@ -74,4 +60,3 @@ t2 t3 t4 drop table t1, t2, t3, t4; -drop table t1, t3, t4; diff --git a/mysql-test/t/ndb_alter_table_stm.test b/mysql-test/t/ndb_alter_table_stm.test index dfe36487c1c..2c52b542b12 100644 --- a/mysql-test/t/ndb_alter_table_stm.test +++ b/mysql-test/t/ndb_alter_table_stm.test @@ -17,8 +17,6 @@ select * from t1 where c = 'two'; connection server1; alter table t1 drop index c; connection server2; --- error 1412 -select * from t1 where c = 'two'; select * from t1 where c = 'two'; connection server1; drop table t1; diff --git a/mysql-test/t/ndb_multi.test b/mysql-test/t/ndb_multi.test index 0cc8f57e4b5..d2dc0561955 100644 --- a/mysql-test/t/ndb_multi.test +++ b/mysql-test/t/ndb_multi.test @@ -41,11 +41,12 @@ drop table t1; create table t1 (a int) engine=ndbcluster; insert into t1 value (2); connection server1; -# Currently a retry is required remotely ---error 1412 -select * from t1; -show warnings; -flush table t1; +## Currently a retry is required remotely +#--error 1412 +#select * from t1; +#show warnings; +#flush table t1; +# Table definition change should be propagated automatically select * from t1; # Connect to server2 and use the tables from there @@ -65,13 +66,9 @@ create table t4 (pk int primary key, b int) engine=ndb; connection server1; select * from t1; select * from t3; -show status like 'handler_discover%'; show tables like 't4'; -show status like 'handler_discover%'; show tables; drop table t1, t2, t3, t4; -connection server2; -drop table t1, t3, t4; # End of 4.1 tests diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 1b1b16f4ae4..82cad126d58 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -34,6 +34,7 @@ #include #include "ha_ndbcluster_binlog.h" +#include "ha_ndbcluster_tables.h" #ifdef ndb_dynamite #undef assert @@ -4381,6 +4382,12 @@ int ha_ndbcluster::create(const char *name, const NDBTAB *t= dict->getTable(m_tabname); String event_name(INJECTOR_EVENT_LEN); ndb_rep_event_name(&event_name,m_dbname,m_tabname); + int do_event_op= ndb_binlog_running; + + if (!schema_share && + strcmp(share->db, NDB_REP_DB) == 0 && + strcmp(share->table_name, NDB_SCHEMA_TABLE) == 0) + do_event_op= 1; /* Always create an event for the table, as other mysql servers @@ -4389,7 +4396,7 @@ int ha_ndbcluster::create(const char *name, if (ndbcluster_create_event(ndb, t, event_name.c_ptr(), share) < 0) { /* this is only a serious error if the binlog is on */ - if (share && ndb_binlog_running) + if (share && do_event_op) { push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, ER_GET_ERRMSG, ER(ER_GET_ERRMSG), @@ -4402,14 +4409,14 @@ int ha_ndbcluster::create(const char *name, sql_print_information("NDB Binlog: CREATE TABLE Event: %s", event_name.c_ptr()); - if (share && ndb_binlog_running && + if (share && do_event_op && ndbcluster_create_event_ops(share, t, event_name.c_ptr()) < 0) { sql_print_error("NDB Binlog: FAILED CREATE TABLE event operations." " Event: %s", name2); /* a warning has been issued to the client */ } - if (share && !ndb_binlog_running) + if (share && !do_event_op) share->flags|= NSF_NO_BINLOG; ndbcluster_log_schema_op(current_thd, share, current_thd->query, current_thd->query_length, @@ -4692,9 +4699,8 @@ int ha_ndbcluster::rename_table(const char *from, const char *to) ERR_RETURN(dict->getNdbError()); } #ifdef HAVE_NDB_BINLOG - NDB_SHARE *share= 0; - if (ndb_binlog_running && - (share= get_share(from, 0, false))) + NDB_SHARE *share= get_share(from, 0, false); + if (share) { int r= rename_share(share, to); DBUG_ASSERT(r == 0); @@ -4755,7 +4761,7 @@ int ha_ndbcluster::rename_table(const char *from, const char *to) if (ndb_extra_logging) sql_print_information("NDB Binlog: RENAME Event: %s", event_name.c_ptr()); - if (share) + if (share && ndb_binlog_running) { if (ndbcluster_create_event_ops(share, ndbtab, event_name.c_ptr()) < 0) @@ -6615,16 +6621,19 @@ static int rename_share(NDB_SHARE *share, const char *new_key) ("db.tablename: %s.%s use_count: %d commit_count: %d", share->db, share->table_name, share->use_count, share->commit_count)); - DBUG_PRINT("rename_share", - ("table->s->db.table_name: %s.%s", - share->table->s->db.str, share->table->s->table_name.str)); - - if (share->op == 0) + if (share->table) { - share->table->s->db.str= share->db; - share->table->s->db.length= strlen(share->db); - share->table->s->table_name.str= share->table_name; - share->table->s->table_name.length= strlen(share->table_name); + DBUG_PRINT("rename_share", + ("table->s->db.table_name: %s.%s", + share->table->s->db.str, share->table->s->table_name.str)); + + if (share->op == 0) + { + share->table->s->db.str= share->db; + share->table->s->db.length= strlen(share->db); + share->table->s->table_name.str= share->table_name; + share->table->s->table_name.length= strlen(share->table_name); + } } /* else rename will be handled when the ALTER event comes */ share->old_names= old_key; diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index b349e3320de..10e6ce63274 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -240,10 +240,33 @@ void ndbcluster_binlog_init_share(NDB_SHARE *share, TABLE *_table) { THD *thd= current_thd; MEM_ROOT *mem_root= &share->mem_root; + int do_event_op= ndb_binlog_running; share->op= 0; share->table= 0; - if (!ndb_binlog_running) + + if (!schema_share && + strcmp(share->db, NDB_REP_DB) == 0 && + strcmp(share->table_name, NDB_SCHEMA_TABLE) == 0) + do_event_op= 1; + + { + int i, no_nodes= g_ndb_cluster_connection->no_db_nodes(); + share->subscriber_bitmap= (MY_BITMAP*) + alloc_root(mem_root, no_nodes * sizeof(MY_BITMAP)); + for (i= 0; i < no_nodes; i++) + { + bitmap_init(&share->subscriber_bitmap[i], + (Uint32*)alloc_root(mem_root, max_ndb_nodes/8), + max_ndb_nodes, false); + bitmap_clear_all(&share->subscriber_bitmap[i]); + } + bitmap_init(&share->slock_bitmap, share->slock, + sizeof(share->slock)*8, false); + bitmap_clear_all(&share->slock_bitmap); + } + + if (!do_event_op) { if (_table) { @@ -318,21 +341,6 @@ void ndbcluster_binlog_init_share(NDB_SHARE *share, TABLE *_table) share->ndb_value[1]= (NdbValue*) alloc_root(mem_root, sizeof(NdbValue) * table->s->fields +1 /*extra for hidden key*/); - { - int i, no_nodes= g_ndb_cluster_connection->no_db_nodes(); - share->subscriber_bitmap= (MY_BITMAP*) - alloc_root(mem_root, no_nodes * sizeof(MY_BITMAP)); - for (i= 0; i < no_nodes; i++) - { - bitmap_init(&share->subscriber_bitmap[i], - (Uint32*)alloc_root(mem_root, max_ndb_nodes/8), - max_ndb_nodes, false); - bitmap_clear_all(&share->subscriber_bitmap[i]); - } - bitmap_init(&share->slock_bitmap, share->slock, - sizeof(share->slock)*8, false); - bitmap_clear_all(&share->slock_bitmap); - } if (table->s->primary_key == MAX_KEY) share->flags|= NSF_HIDDEN_PK; if (table->s->blob_fields != 0) @@ -1361,6 +1369,7 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb, switch (ev_type) { case NDBEVENT::TE_UPDATE: + /* fall through */ case NDBEVENT::TE_INSERT: { Cluster_replication_schema *schema= (Cluster_replication_schema *) @@ -1378,21 +1387,20 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb, { case SOT_DROP_TABLE: /* binlog dropping table after any table operations */ - post_epoch_log_list->push_back(schema, mem_root); + if (ndb_binlog_running) + post_epoch_log_list->push_back(schema, mem_root); log_query= 0; break; case SOT_RENAME_TABLE: /* fall through */ case SOT_ALTER_TABLE: - /* fall through */ - if (!ndb_binlog_running) + if (ndb_binlog_running) { log_query= 1; break; /* discovery will be handled by binlog */ } /* fall through */ case SOT_CREATE_TABLE: - /* fall through */ pthread_mutex_lock(&LOCK_open); if (ndb_create_table_from_engine(thd, schema->db, schema->name)) { @@ -1410,7 +1418,8 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb, TRUE, /* print error */ TRUE); /* don't binlog the query */ /* binlog dropping database after any table operations */ - post_epoch_log_list->push_back(schema, mem_root); + if (ndb_binlog_running) + post_epoch_log_list->push_back(schema, mem_root); log_query= 0; break; case SOT_CREATE_DB: @@ -1466,7 +1475,7 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb, } } - if (log_query) + if (log_query && ndb_binlog_running) { char *thd_db_save= thd->db; thd->db= schema->db; @@ -1755,6 +1764,7 @@ int ndbcluster_create_binlog_setup(Ndb *ndb, const char *key, const char *table_name, my_bool share_may_exist) { + int do_event_op= ndb_binlog_running; DBUG_ENTER("ndbcluster_create_binlog_setup"); DBUG_PRINT("enter",("key: %s key_len: %d %s.%s share_may_exist: %d", key, key_len, db, table_name, share_may_exist)); @@ -1795,7 +1805,12 @@ int ndbcluster_create_binlog_setup(Ndb *ndb, const char *key, "allocating table share for %s failed", key); } - if (!ndb_binlog_running) + if (!schema_share && + strcmp(share->db, NDB_REP_DB) == 0 && + strcmp(share->table_name, NDB_SCHEMA_TABLE) == 0) + do_event_op= 1; + + if (!do_event_op) { share->flags|= NSF_NO_BINLOG; pthread_mutex_unlock(&ndbcluster_mutex); From d3e08757596495e8a191cadd7072eb1c41909093 Mon Sep 17 00:00:00 2001 From: "andrey@lmy004." <> Date: Thu, 16 Feb 2006 00:43:11 +0100 Subject: [PATCH 17/20] fix for bug#16406 (Events: DROP DATABASE doesn't automatically drop events) WL#1034 - This changeset also changes the executor so its quite more stable now. Stressing test case added that executes ~800 events per second and dropping hundreds of events at once using DROP DATABASE. (with fixes after review of JimW) (with fixes after review of Serg) --- mysql-test/r/events.result | 2 +- mysql-test/r/events_stress.result | 46 +++++ mysql-test/t/events.test | 1 - mysql-test/t/events_stress.test | 80 +++++++++ sql/event.cc | 231 +++++++++++++++++++++++-- sql/event.h | 54 +++++- sql/event_executor.cc | 277 ++++++++++++++++++------------ sql/event_priv.h | 12 +- sql/event_timed.cc | 140 ++++++++++++++- sql/sql_db.cc | 2 + 10 files changed, 710 insertions(+), 135 deletions(-) create mode 100644 mysql-test/r/events_stress.result create mode 100644 mysql-test/t/events_stress.test diff --git a/mysql-test/r/events.result b/mysql-test/r/events.result index 41f944ab089..4c7b9faec05 100644 --- a/mysql-test/r/events.result +++ b/mysql-test/r/events.result @@ -64,7 +64,7 @@ SHOW GRANTS; Grants for ev_test@localhost GRANT USAGE ON *.* TO 'ev_test'@'localhost' GRANT ALL PRIVILEGES ON `events_test`.* TO 'ev_test'@'localhost' -GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE ON `events_test2`.* TO 'ev_test'@'localhost' +GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, TRIGGER ON `events_test2`.* TO 'ev_test'@'localhost' "Here comes an error:"; SHOW EVENTS; ERROR 42000: Access denied for user 'ev_test'@'localhost' to database 'events_test2' diff --git a/mysql-test/r/events_stress.result b/mysql-test/r/events_stress.result new file mode 100644 index 00000000000..9f95cfad75d --- /dev/null +++ b/mysql-test/r/events_stress.result @@ -0,0 +1,46 @@ +CREATE DATABASE IF NOT EXISTS events_test; +CREATE DATABASE events_test2; +USE events_test2; +CREATE EVENT ev_drop1 ON SCHEDULE EVERY 10 MINUTE DISABLE DO SELECT 1; +CREATE EVENT ev_drop2 ON SCHEDULE EVERY 10 MINUTE DISABLE DO SELECT 1; +CREATE EVENT ev_drop3 ON SCHEDULE EVERY 10 MINUTE DISABLE DO SELECT 1; +USE events_test; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; +COUNT(*) +3 +DROP DATABASE events_test2; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; +COUNT(*) +0 +"Now testing stability - dropping db -> events while they are running" +CREATE DATABASE events_test2; +USE events_test2; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; +COUNT(*) +1000 +SET GLOBAL event_scheduler=1; +DROP DATABASE events_test2; +SET GLOBAL event_scheduler=0; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; +COUNT(*) +0 +CREATE DATABASE events_test3; +USE events_test3; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test3'; +COUNT(*) +950 +CREATE DATABASE events_test4; +USE events_test4; +CREATE DATABASE events_test2; +USE events_test2; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; +COUNT(*) +1050 +DROP DATABASE events_test2; +SET GLOBAL event_scheduler=0; +DROP DATABASE events_test3; +SET GLOBAL event_scheduler=1; +DROP DATABASE events_test4; +SET GLOBAL event_scheduler=1; +USE events_test; +DROP DATABASE events_test; diff --git a/mysql-test/t/events.test b/mysql-test/t/events.test index be24d490393..dff7f48f4d5 100644 --- a/mysql-test/t/events.test +++ b/mysql-test/t/events.test @@ -109,7 +109,6 @@ drop event one_event; - create event e_26 on schedule at '2017-01-01 00:00:00' disable do set @a = 5; select db, name, body, definer, convert_tz(execute_at, 'UTC', 'SYSTEM'), on_completion from mysql.event; drop event e_26; diff --git a/mysql-test/t/events_stress.test b/mysql-test/t/events_stress.test new file mode 100644 index 00000000000..f6eed79425c --- /dev/null +++ b/mysql-test/t/events_stress.test @@ -0,0 +1,80 @@ +CREATE DATABASE IF NOT EXISTS events_test; +# +# DROP DATABASE test start (bug #16406) +# +CREATE DATABASE events_test2; +USE events_test2; +CREATE EVENT ev_drop1 ON SCHEDULE EVERY 10 MINUTE DISABLE DO SELECT 1; +CREATE EVENT ev_drop2 ON SCHEDULE EVERY 10 MINUTE DISABLE DO SELECT 1; +CREATE EVENT ev_drop3 ON SCHEDULE EVERY 10 MINUTE DISABLE DO SELECT 1; +USE events_test; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; +DROP DATABASE events_test2; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; + +--echo "Now testing stability - dropping db -> events while they are running" +CREATE DATABASE events_test2; +USE events_test2; +--disable_query_log +let $1= 1000; +while ($1) +{ + eval CREATE EVENT ev_drop$1 ON SCHEDULE EVERY 1 SECOND DO SELECT $1; + dec $1; +} +--enable_query_log +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; +SET GLOBAL event_scheduler=1; +--sleep 4 +DROP DATABASE events_test2; + +SET GLOBAL event_scheduler=0; +--sleep 2 +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; +CREATE DATABASE events_test3; +USE events_test3; +--disable_query_log +let $1= 950; +while ($1) +{ + eval CREATE EVENT ev_drop$1 ON SCHEDULE EVERY 1 SECOND DO SELECT $1; + dec $1; +} +--enable_query_log +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test3'; +--sleep 3 +CREATE DATABASE events_test4; +USE events_test4; +--disable_query_log +let $1= 860; +while ($1) +{ + eval CREATE EVENT ev_drop$1 ON SCHEDULE EVERY 1 SECOND DO SELECT $1; + dec $1; +} +--enable_query_log + + +CREATE DATABASE events_test2; +USE events_test2; +--disable_query_log +let $1= 1050; +while ($1) +{ + eval CREATE EVENT ev_drop$1 ON SCHEDULE EVERY 1 SECOND DO SELECT $1; + dec $1; +} +--enable_query_log +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; +--sleep 6 +DROP DATABASE events_test2; +SET GLOBAL event_scheduler=0; +DROP DATABASE events_test3; +SET GLOBAL event_scheduler=1; +DROP DATABASE events_test4; +SET GLOBAL event_scheduler=1; +USE events_test; +# +# DROP DATABASE test end (bug #16406) +# +DROP DATABASE events_test; diff --git a/sql/event.cc b/sql/event.cc index abca622835a..063a4ef9333 100644 --- a/sql/event.cc +++ b/sql/event.cc @@ -165,11 +165,34 @@ evex_open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table) } + /* Find row in open mysql.event table representing event SYNOPSIS evex_db_find_event_aux() + thd Thread context + et evet_timed object containing dbname, name & definer + table TABLE object for open mysql.event table. + + RETURN VALUE + 0 - Routine found + EVEX_KEY_NOT_FOUND - No routine with given name +*/ + +inline int +evex_db_find_event_aux(THD *thd, event_timed *et, TABLE *table) +{ + return evex_db_find_event_by_name(thd, et->dbname, et->name, + et->definer, table); +} + + +/* + Find row in open mysql.event table representing event + + SYNOPSIS + evex_db_find_event_by_name() thd Thread context dbname Name of event's database rname Name of the event inside the db @@ -181,13 +204,13 @@ evex_open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table) */ int -evex_db_find_event_aux(THD *thd, const LEX_STRING dbname, - const LEX_STRING ev_name, - const LEX_STRING user_name, - TABLE *table) +evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname, + const LEX_STRING ev_name, + const LEX_STRING user_name, + TABLE *table) { byte key[MAX_KEY_LENGTH]; - DBUG_ENTER("evex_db_find_event_aux"); + DBUG_ENTER("evex_db_find_event_by_name"); DBUG_PRINT("enter", ("name: %.*s", ev_name.length, ev_name.str)); /* @@ -373,7 +396,7 @@ db_create_event(THD *thd, event_timed *et, my_bool create_if_not, } DBUG_PRINT("info", ("check existance of an event with the same name")); - if (!evex_db_find_event_aux(thd, et->dbname, et->name, et->definer, table)) + if (!evex_db_find_event_aux(thd, et, table)) { if (create_if_not) { @@ -511,7 +534,7 @@ db_update_event(THD *thd, event_timed *et, sp_name *new_name) goto err; } - if (!evex_db_find_event_aux(thd, new_name->m_db, new_name->m_name, + if (!evex_db_find_event_by_name(thd, new_name->m_db, new_name->m_name, et->definer, table)) { my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->m_name.str); @@ -524,8 +547,7 @@ db_update_event(THD *thd, event_timed *et, sp_name *new_name) overwrite the key and SE will tell us that it cannot find the already found row (copied into record[1] later */ - if (EVEX_KEY_NOT_FOUND == evex_db_find_event_aux(thd, et->dbname, et->name, - et->definer, table)) + if (EVEX_KEY_NOT_FOUND == evex_db_find_event_aux(thd, et, table)) { my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->name.str); goto err; @@ -603,8 +625,8 @@ db_find_event(THD *thd, sp_name *name, LEX_STRING definer, event_timed **ett, goto done; } - if ((ret= evex_db_find_event_aux(thd, name->m_db, name->m_name, definer, - table))) + if ((ret= evex_db_find_event_by_name(thd, name->m_db, name->m_name, definer, + table))) { my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->m_name.str); goto done; @@ -727,7 +749,7 @@ evex_remove_from_cache(LEX_STRING *db, LEX_STRING *name, bool use_lock, if (!sortcmp_lex_string(*name, et->name, system_charset_info) && !sortcmp_lex_string(*db, et->dbname, system_charset_info)) { - if (!et->is_running()) + if (et->can_spawn_now()) { DBUG_PRINT("evex_remove_from_cache", ("not running - free and delete")); et->free_sp(); @@ -887,7 +909,7 @@ evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists, goto done; } - if (!(ret= evex_db_find_event_aux(thd, et->dbname,et->name,et->definer,table))) + if (!(ret= evex_db_find_event_aux(thd, et, table))) { if ((ret= table->file->ha_delete_row(table->record[0]))) { @@ -923,3 +945,186 @@ done: DBUG_RETURN(ret); } + +/* + evex_drop_db_events - Drops all events in the selected database + + thd - Thread + db - ASCIIZ the name of the database + + Returns: + 0 - OK + 1 - Failed to delete a specific row + 2 - Got NULL while reading db name from a row + + Note: + The algo is the following + 1. Go through the in-memory cache, if the scheduler is working + and for every event whose dbname matches the database we drop + check whether is currently in execution: + - event_timed::can_spawn() returns true -> the event is not + being executed in a child thread. The reason not to use + event_timed::is_running() is that the latter shows only if + it is being executed, which is 99% of the time in the thread + but there are some initiliazations before and after the + anonymous SP is being called. So if we delete in this moment + -=> *boom*, so we have to check whether the thread has been + spawned and can_spawn() is the right method. + - event_timed::can_spawn() returns false -> being runned ATM + just set the flags so it should drop itself. + +*/ + +int +evex_drop_db_events(THD *thd, char *db) +{ + TABLE *table; + READ_RECORD read_record_info; + MYSQL_LOCK *lock; + int ret= 0; + int i; + LEX_STRING db_lex= {db, strlen(db)}; + + DBUG_ENTER("evex_drop_db_events"); + DBUG_PRINT("info",("dropping events from %s", db)); + + + VOID(pthread_mutex_lock(&LOCK_event_arrays)); + + if ((ret= evex_open_event_table(thd, TL_WRITE, &table))) + { + sql_print_error("Table mysql.event is damaged."); + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + DBUG_RETURN(SP_OPEN_TABLE_FAILED); + } + + DBUG_PRINT("info",("%d elements in the queue", + evex_queue_num_elements(EVEX_EQ_NAME))); + VOID(pthread_mutex_lock(&LOCK_evex_running)); + if (!evex_is_running) + goto skip_memory; + + for (i= 0; i < evex_queue_num_elements(EVEX_EQ_NAME); ++i) + { + event_timed *et= evex_queue_element(&EVEX_EQ_NAME, i, event_timed*); + if (sortcmp_lex_string(et->dbname, db_lex, system_charset_info)) + continue; + + if (et->can_spawn_now_n_lock(thd)) + { + DBUG_PRINT("info",("event %s not running - direct delete", et->name.str)); + if (!(ret= evex_db_find_event_aux(thd, et, table))) + { + DBUG_PRINT("info",("event %s found on disk", et->name.str)); + if ((ret= table->file->ha_delete_row(table->record[0]))) + { + sql_print_error("Error while deleting a row - dropping " + "a database. Skipping the rest."); + my_error(ER_EVENT_DROP_FAILED, MYF(0), et->name.str); + goto end; + } + DBUG_PRINT("info",("deleted event [%s] num [%d]. Time to free mem", + et->name.str, i)); + } + else if (ret == EVEX_KEY_NOT_FOUND) + { + sql_print_error("Expected to find event %s.%s of %s on disk-not there.", + et->dbname.str, et->name.str, et->definer.str); + } + et->free_sp(); + delete et; + et= 0; + /* no need to call et->spawn_unlock because we already cleaned et */ + } + else + { + DBUG_PRINT("info",("event %s is running. setting exec_no_more and dropped", + et->name.str)); + et->flags|= EVENT_EXEC_NO_MORE; + et->dropped= TRUE; + } + DBUG_PRINT("info",("%d elements in the queue", + evex_queue_num_elements(EVEX_EQ_NAME))); + evex_queue_delete_element(&EVEX_EQ_NAME, i);// 1 is top + DBUG_PRINT("info",("%d elements in the queue", + evex_queue_num_elements(EVEX_EQ_NAME))); + /* + decrease so we start at the same position, there will be + less elements in the queue, it will still be ordered so on + next iteration it will be again i the current element or if + no more we finish. + */ + --i; + } + +skip_memory: + /* + The reasoning behind having two loops is the following: + If there was only one loop, the table-scan, then for every element which + matches, the queue in memory has to be searched to remove the element. + While if we go first over the queue and remove what's in there we have only + one pass over it and after finishing it, moving to table-scan for the disabled + events. This needs quite less time and means quite less locking on + LOCK_event_arrays. + */ + DBUG_PRINT("info",("Mem-cache checked, now going to db for disabled events")); + /* only enabled events are in memory, so we go now and delete the rest */ + init_read_record(&read_record_info, thd, table ,NULL,1,0); + while (!(read_record_info.read_record(&read_record_info)) && !ret) + { + char *et_db; + + if ((et_db= get_field(thd->mem_root, table->field[EVEX_FIELD_DB])) == NULL) + { + ret= 2; + break; + } + + LEX_STRING et_db_lex= {et_db, strlen(et_db)}; + if (!sortcmp_lex_string(et_db_lex, db_lex, system_charset_info)) + { + event_timed ett; + char *ptr; + + if ((ptr= get_field(thd->mem_root, table->field[EVEX_FIELD_STATUS])) + == NullS) + { + sql_print_error("Error while loading from mysql.event. " + "Table probably corrupted"); + goto end; + } + /* + When not running nothing is in memory so we have to clean + everything. + We don't delete EVENT_ENABLED events when the scheduler is running + because maybe this is an event which we asked to drop itself when + it is finished and it hasn't finished yet, so we don't touch it. + It will drop itself. The not running ENABLED events has been already + deleted from ha_delete_row() above in the loop over the QUEUE + (in case the executor is running). + 'D' stands for DISABLED, 'E' for ENABLED - it's an enum + */ + if ((evex_is_running && ptr[0] == 'D') || !evex_is_running) + { + DBUG_PRINT("info", ("Dropping %s.%s", et_db, ett.name.str)); + if ((ret= table->file->ha_delete_row(table->record[0]))) + { + my_error(ER_EVENT_DROP_FAILED, MYF(0), ett.name.str); + goto end; + } + } + } + } + DBUG_PRINT("info",("Disk checked for disabled events. Finishing.")); + +end: + VOID(pthread_mutex_unlock(&LOCK_evex_running)); + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + end_read_record(&read_record_info); + + thd->version--; // Force close to free memory + + close_thread_tables(thd); + + DBUG_RETURN(ret); +} diff --git a/sql/event.h b/sql/event.h index 1fe5c8e5713..f9dea2d85e9 100644 --- a/sql/event.h +++ b/sql/event.h @@ -79,6 +79,8 @@ class event_timed { event_timed(const event_timed &); /* Prevent use of these */ void operator=(event_timed &); + my_bool in_spawned_thread; + ulong locked_by_thread_id; my_bool running; pthread_mutex_t LOCK_running; @@ -116,9 +118,10 @@ public: bool free_sphead_on_delete; uint flags;//all kind of purposes - event_timed():running(0), status_changed(false), last_executed_changed(false), - expression(0), created(0), modified(0), - on_completion(MYSQL_EVENT_ON_COMPLETION_DROP), + event_timed():in_spawned_thread(0),locked_by_thread_id(0), + running(0), status_changed(false), + last_executed_changed(false), expression(0), created(0), + modified(0), on_completion(MYSQL_EVENT_ON_COMPLETION_DROP), status(MYSQL_EVENT_ENABLED), sphead(0), dropped(false), free_sphead_on_delete(true), flags(0) @@ -197,8 +200,45 @@ public: return ret; } + + /* + Checks whether the object is being used in a spawned thread. + This method is for very basic checking. Use ::can_spawn_now_n_lock() + for most of the cases. + */ + + my_bool + can_spawn_now() + { + my_bool ret; + VOID(pthread_mutex_lock(&this->LOCK_running)); + ret= !in_spawned_thread; + VOID(pthread_mutex_unlock(&this->LOCK_running)); + return ret; + } + + /* + Checks whether this thread can lock the object for modification -> + preventing being spawned for execution, and locks if possible. + use ::can_spawn_now() only for basic checking because a race + condition may occur between the check and eventual modification (deletion) + of the object. + */ + + my_bool + can_spawn_now_n_lock(THD *thd); + + int + spawn_unlock(THD *thd); + + int + spawn_now(void * (*thread_func)(void*)); - void free_sp() + void + spawn_thread_finish(THD *thd); + + void + free_sp() { delete sphead; sphead= 0; @@ -221,7 +261,11 @@ evex_drop_event(THD *thd, event_timed *et, bool drop_if_exists, int evex_open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table); -int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs); +int +sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs); + +int +evex_drop_db_events(THD *thd, char *db); int init_events(); diff --git a/sql/event_executor.cc b/sql/event_executor.cc index 7960f1e1758..7bcc8882fca 100644 --- a/sql/event_executor.cc +++ b/sql/event_executor.cc @@ -18,6 +18,11 @@ #include "event.h" #include "sp.h" +#define WAIT_STATUS_READY 0 +#define WAIT_STATUS_EMPTY_QUEUE 1 +#define WAIT_STATUS_NEW_TOP_EVENT 2 +#define WAIT_STATUS_STOP_EXECUTOR 3 + /* Make this define DBUG_FAULTY_THR to be able to put breakpoints inside @@ -165,18 +170,97 @@ init_event_thread(THD* thd) DBUG_RETURN(0); } + +/* + This function waits till the time next event in the queue should be + executed. + + Returns + WAIT_STATUS_READY There is an event to be executed right now + WAIT_STATUS_EMPTY_QUEUE No events or the last event was dropped. + WAIT_STATUS_NEW_TOP_EVENT New event has entered the queue and scheduled + on top. Restart ticking. + WAIT_STATUS_STOP_EXECUTOR The thread was killed or SET global event_scheduler=0; +*/ + +static int +executor_wait_till_next_event_exec(THD *thd) +{ + event_timed *et; + TIME time_now; + int t2sleep; + + DBUG_ENTER("executor_wait_till_next_event_exec"); + /* + now let's see how much time to sleep, we know there is at least 1 + element in the queue. + */ + VOID(pthread_mutex_lock(&LOCK_event_arrays)); + if (!evex_queue_num_elements(EVEX_EQ_NAME)) + { + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + DBUG_RETURN(1); + } + et= evex_queue_first_element(&EVEX_EQ_NAME, event_timed*); + DBUG_ASSERT(et); + if (et->status == MYSQL_EVENT_DISABLED) + { + DBUG_PRINT("evex main thread",("Now it is disabled-exec no more")); + if (et->dropped) + et->drop(thd); + delete et; + evex_queue_delete_element(&EVEX_EQ_NAME, 1);// 1 is top + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + sql_print_information("Event found disabled, dropping."); + DBUG_RETURN(1); + } + + DBUG_PRINT("evex main thread",("computing time to sleep till next exec")); + // set the internal clock of thd + thd->end_time(); + my_tz_UTC->gmt_sec_to_TIME(&time_now, thd->query_start()); + t2sleep= evex_time_diff(&et->execute_at, &time_now); + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + + DBUG_PRINT("evex main thread",("unlocked LOCK_event_arrays")); + if (t2sleep > 0) + { + /* + We sleep t2sleep seconds but we check every second whether this thread + has been killed, or there is a new candidate + */ + while (t2sleep-- && !thd->killed && event_executor_running_global_var && + evex_queue_num_elements(EVEX_EQ_NAME) && + (evex_queue_first_element(&EVEX_EQ_NAME, event_timed*) == et)) + { + DBUG_PRINT("evex main thread",("will sleep a bit more")); + my_sleep(1000000); + } + } + + int ret= 0; + if (!evex_queue_num_elements(EVEX_EQ_NAME)) + ret= 1; + else if (evex_queue_first_element(&EVEX_EQ_NAME, event_timed*) != et) + ret= 2; + if (thd->killed && event_executor_running_global_var) + ret= 3; + + DBUG_RETURN(ret); +} + + pthread_handler_t event_executor_main(void *arg) { THD *thd; /* needs to be first for thread_stack */ - ulonglong iter_num= 0; uint i=0, j=0; my_ulonglong cnt= 0; + TIME time_now; DBUG_ENTER("event_executor_main"); DBUG_PRINT("event_executor_main", ("EVEX thread started")); - // init memory root init_alloc_root(&evex_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); @@ -186,23 +270,24 @@ event_executor_main(void *arg) if (sizeof(my_time_t) != sizeof(time_t)) { - sql_print_error("sizeof(my_time_t) != sizeof(time_t) ." + sql_print_error("SCHEDULER: sizeof(my_time_t) != sizeof(time_t) ." "The scheduler will not work correctly. Stopping."); + DBUG_ASSERT(0); goto err_no_thd; } //TODO Andrey: Check for NULL if (!(thd = new THD)) // note that contructor of THD uses DBUG_ ! { - sql_print_error("Cannot create THD for event_executor_main"); + sql_print_error("SCHEDULER: Cannot create THD for the main thread."); goto err_no_thd; } thd->thread_stack = (char*)&thd; // remember where our stack is pthread_detach_this_thread(); - + if (init_event_thread(thd)) - goto err; + goto finish; // make this thread invisible it has no vio -> show processlist won't see thd->system_thread= 1; @@ -213,7 +298,7 @@ event_executor_main(void *arg) thread_running++; VOID(pthread_mutex_unlock(&LOCK_thread_count)); - DBUG_PRINT("EVEX main thread", ("Initing events_queuey")); + DBUG_PRINT("EVEX main thread", ("Initing events_queue")); /* eventually manifest that we are running, not to crashe because of @@ -229,15 +314,14 @@ event_executor_main(void *arg) thd->security_ctx->user= my_strdup("event_scheduler", MYF(0)); if (evex_load_events_from_db(thd)) - goto err; + goto finish; evex_main_thread_id= thd->thread_id; - sql_print_information("Scheduler thread started"); + sql_print_information("SCHEDULER: Main thread started"); while (!thd->killed) { TIME time_now; - my_time_t now; event_timed *et; cnt++; @@ -246,7 +330,7 @@ event_executor_main(void *arg) thd->proc_info = "Sleeping"; if (!event_executor_running_global_var) { - sql_print_information("Scheduler asked to stop."); + sql_print_information("SCHEDULER: Asked to stop."); break; } @@ -255,62 +339,30 @@ event_executor_main(void *arg) my_sleep(1000000);// sleep 1s continue; } - - { - int t2sleep; - /* - now let's see how much time to sleep, we know there is at least 1 - element in the queue. - */ - VOID(pthread_mutex_lock(&LOCK_event_arrays)); - if (!evex_queue_num_elements(EVEX_EQ_NAME)) - { - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); - continue; - } - et= evex_queue_first_element(&EVEX_EQ_NAME, event_timed*); - if (et->status == MYSQL_EVENT_DISABLED) - { - DBUG_PRINT("evex main thread",("Now it is disabled-exec no more")); - if (et->dropped) - et->drop(thd); - delete et; - evex_queue_delete_element(&EVEX_EQ_NAME, 1);// 1 is top - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); - sql_print_information("Event found disabled, dropping."); - continue; - } - - DBUG_PRINT("evex main thread",("computing time to sleep till next exec")); - time((time_t *)&now); - my_tz_UTC->gmt_sec_to_TIME(&time_now, now); - t2sleep= evex_time_diff(&et->execute_at, &time_now); - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); - - DBUG_PRINT("evex main thread",("unlocked LOCK_event_arrays")); - if (t2sleep > 0) - { - /* - We sleep t2sleep seconds but we check every second whether this thread - has been killed, or there is a new candidate - */ - while (t2sleep-- && !thd->killed && event_executor_running_global_var && - evex_queue_num_elements(EVEX_EQ_NAME) && - (evex_queue_first_element(&EVEX_EQ_NAME, event_timed*) == et)) - { - DBUG_PRINT("evex main thread",("will sleep a bit more")); - my_sleep(1000000); - } - } - if (!event_executor_running_global_var) - { - sql_print_information("Scheduler asked to stop."); - break; - } + +restart_ticking: + switch (executor_wait_till_next_event_exec(thd)) { + case WAIT_STATUS_READY: // time to execute the event on top + DBUG_PRINT("evex main thread",("time to execute an event")); + break; + case WAIT_STATUS_EMPTY_QUEUE: // no more events + DBUG_PRINT("evex main thread",("no more events")); + continue; + break; + case WAIT_STATUS_NEW_TOP_EVENT: // new event on top in the queue + DBUG_PRINT("evex main thread",("restart ticking")); + goto restart_ticking; + case WAIT_STATUS_STOP_EXECUTOR: + sql_print_information("SCHEDULER: Asked to stop."); + goto finish; + break; + default: + DBUG_ASSERT(0); } - VOID(pthread_mutex_lock(&LOCK_event_arrays)); + thd->end_time(); + my_tz_UTC->gmt_sec_to_TIME(&time_now, thd->query_start()); if (!evex_queue_num_elements(EVEX_EQ_NAME)) { @@ -332,14 +384,13 @@ event_executor_main(void *arg) DBUG_PRINT("evex main thread",("it's right time")); if (et->status == MYSQL_EVENT_ENABLED) { - pthread_t th; - + int fork_ret_code; DBUG_PRINT("evex main thread", ("[%10s] this exec at [%llu]", et->name.str, TIME_to_ulonglong_datetime(&et->execute_at))); et->mark_last_executed(thd); if (et->compute_next_execution_time()) { - sql_print_error("Error while computing time of %s.%s . " + sql_print_error("SCHEDULER: Error while computing time of %s.%s . " "Disabling after execution.", et->dbname.str, et->name.str); et->status= MYSQL_EVENT_DISABLED; @@ -348,13 +399,23 @@ event_executor_main(void *arg) TIME_to_ulonglong_datetime(&et->execute_at))); et->update_fields(thd); - ++iter_num; - DBUG_PRINT("info", (" Spawning a thread %d", iter_num)); #ifndef DBUG_FAULTY_THR - if (pthread_create(&th, NULL, event_executor_worker, (void*)et)) - { - sql_print_error("Problem while trying to create a thread"); - UNLOCK_MUTEX_AND_BAIL_OUT(LOCK_event_arrays, err); + thread_safe_increment(workers_count, &LOCK_workers_count); + switch ((fork_ret_code= et->spawn_now(event_executor_worker))) { + case EVENT_EXEC_CANT_FORK: + thread_safe_decrement(workers_count, &LOCK_workers_count); + sql_print_error("SCHEDULER: Problem while trying to create a thread"); + UNLOCK_MUTEX_AND_BAIL_OUT(LOCK_event_arrays, finish); + case EVENT_EXEC_ALREADY_EXEC: + thread_safe_decrement(workers_count, &LOCK_workers_count); + sql_print_information("SCHEDULER: %s.%s in execution. Skip this time.", + et->dbname.str, et->name.str); + break; + default: + DBUG_ASSERT(!fork_ret_code); + if (fork_ret_code) + thread_safe_decrement(workers_count, &LOCK_workers_count); + break; } #else event_executor_worker((void *) et); @@ -364,22 +425,21 @@ event_executor_main(void *arg) et->flags |= EVENT_EXEC_NO_MORE; if ((et->flags & EVENT_EXEC_NO_MORE) || et->status == MYSQL_EVENT_DISABLED) - evex_queue_delete_element(&EVEX_EQ_NAME, 1);// 1 is top + evex_queue_delete_element(&EVEX_EQ_NAME, 0);// 0 is top, internally 1 else evex_queue_first_updated(&EVEX_EQ_NAME); } DBUG_PRINT("evex main thread",("unlocking")); VOID(pthread_mutex_unlock(&LOCK_event_arrays)); }// while +finish: -err: // First manifest that this thread does not work and then destroy VOID(pthread_mutex_lock(&LOCK_evex_running)); evex_is_running= false; evex_main_thread_id= 0; VOID(pthread_mutex_unlock(&LOCK_evex_running)); - sql_print_information("Event scheduler stopping. Waiting for worker threads to finish."); /* TODO: A better will be with a conditional variable @@ -388,21 +448,33 @@ err: Read workers_count without lock, no need for locking. In the worst case we have to wait 1sec more. */ - while (workers_count) - my_sleep(1000000);// 1s + sql_print_information("SCHEDULER: Stopping. Waiting for worker threads to finish."); + while (1) + { + VOID(pthread_mutex_lock(&LOCK_workers_count)); + if (!workers_count) + { + VOID(pthread_mutex_unlock(&LOCK_workers_count)); + break; + } + VOID(pthread_mutex_unlock(&LOCK_workers_count)); + my_sleep(1000000);// 1s + } /* - LEX_STRINGs reside in the memory root and will be destroyed with it. - Hence no need of delete but only freeing of SP + First we free all objects ... + Lock because a DROP DATABASE could be running in parallel and it locks on these */ - // First we free all objects ... + sql_print_information("SCHEDULER: Emptying the queue."); + VOID(pthread_mutex_lock(&LOCK_event_arrays)); for (i= 0; i < evex_queue_num_elements(EVEX_EQ_NAME); ++i) { event_timed *et= evex_queue_element(&EVEX_EQ_NAME, i, event_timed*); et->free_sp(); delete et; } - // ... then we can thras the whole queue at once + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + // ... then we can thrash the whole queue at once evex_queue_destroy(&EVEX_EQ_NAME); thd->proc_info = "Clearing"; @@ -426,7 +498,7 @@ err_no_thd: VOID(pthread_mutex_unlock(&LOCK_evex_running)); free_root(&evex_mem_root, MYF(0)); - sql_print_information("Event scheduler stopped."); + sql_print_information("SCHEDULER: Stopped."); #ifndef DBUG_FAULTY_THR my_thread_end(); @@ -444,9 +516,6 @@ event_executor_worker(void *event_void) MEM_ROOT worker_mem_root; DBUG_ENTER("event_executor_worker"); - VOID(pthread_mutex_lock(&LOCK_workers_count)); - ++workers_count; - VOID(pthread_mutex_unlock(&LOCK_workers_count)); init_alloc_root(&worker_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); @@ -455,7 +524,7 @@ event_executor_worker(void *event_void) if (!(thd = new THD)) // note that contructor of THD uses DBUG_ ! { - sql_print_error("Cannot create a THD structure in a scheduler worker thread"); + sql_print_error("SCHEDULER: Cannot create a THD structure in an worker."); goto err_no_thd; } thd->thread_stack = (char*)&thd; // remember where our stack is @@ -495,30 +564,23 @@ event_executor_worker(void *event_void) int ret; DBUG_PRINT("info", (" EVEX EXECUTING event %s.%s [EXPR:%d]", event->dbname.str, event->name.str,(int) event->expression)); - sql_print_information(" EVEX EXECUTING event %s.%s [EXPR:%d]", + sql_print_information("SCHEDULER: Executing event %s.%s [EXPR:%d]", event->dbname.str, event->name.str,(int) event->expression); ret= event->execute(thd, &worker_mem_root); - sql_print_information(" EVEX EXECUTED event %s.%s [EXPR:%d]. RetCode=%d", + sql_print_information("SCHEDULER: Executed event %s.%s [EXPR:%d]. RetCode=%d", event->dbname.str, event->name.str, (int) event->expression, ret); if (ret == EVEX_COMPILE_ERROR) - sql_print_information(" EVEX COMPILE ERROR for event %s.%s", + sql_print_information("SCHEDULER:COMPILE ERROR for event %s.%s", event->dbname.str, event->name.str); DBUG_PRINT("info", (" EVEX EXECUTED event %s.%s [EXPR:%d]. RetCode=%d", event->dbname.str, event->name.str, (int) event->expression, ret)); } - if ((event->flags & EVENT_EXEC_NO_MORE) || event->status==MYSQL_EVENT_DISABLED) - { - DBUG_PRINT("event_executor_worker", - ("%s exec no more. to drop=%d",event->name.str, event->dropped)); - if (event->dropped) - event->drop(thd); - delete event; - } + event->spawn_thread_finish(thd); thd->db= 0; @@ -548,10 +610,7 @@ err: err_no_thd: free_root(&worker_mem_root, MYF(0)); - - VOID(pthread_mutex_lock(&LOCK_workers_count)); - --workers_count; - VOID(pthread_mutex_unlock(&LOCK_workers_count)); + thread_safe_decrement(workers_count, &LOCK_workers_count); #ifndef DBUG_FAULTY_THR my_thread_end(); @@ -574,7 +633,7 @@ evex_load_events_from_db(THD *thd) if ((ret= evex_open_event_table(thd, TL_READ, &table))) { - sql_print_error("Table mysql.event is damaged."); + sql_print_error("SCHEDULER: Table mysql.event is damaged. Can not open."); DBUG_RETURN(SP_OPEN_TABLE_FAILED); } @@ -594,7 +653,7 @@ evex_load_events_from_db(THD *thd) if ((ret= et->load_from_row(&evex_mem_root, table))) { - sql_print_error("Error while loading from mysql.event. " + sql_print_error("SCHEDULER: Error while loading from mysql.event. " "Table probably corrupted"); goto end; } @@ -606,11 +665,11 @@ evex_load_events_from_db(THD *thd) } DBUG_PRINT("evex_load_events_from_db", - ("Event %s loaded from row. Time to compile", et->name.str)); + ("Event %s loaded from row. Time to compile", et->name.str)); if ((ret= et->compile(thd, &evex_mem_root))) { - sql_print_error("Error while compiling %s.%s. Aborting load.", + sql_print_error("SCHEDULER: Error while compiling %s.%s. Aborting load.", et->dbname.str, et->name.str); goto end; } @@ -618,8 +677,8 @@ evex_load_events_from_db(THD *thd) // let's find when to be executed if (et->compute_next_execution_time()) { - sql_print_error("Error while computing execution time of %s.%s. Skipping", - et->dbname.str, et->name.str); + sql_print_error("SCHEDULER: Error while computing execution time of %s.%s." + " Skipping", et->dbname.str, et->name.str); continue; } @@ -640,7 +699,7 @@ end: thd->version--; // Force close to free memory close_thread_tables(thd); - sql_print_information("Scheduler loaded %d event%s", count, (count == 1)?"":"s"); + sql_print_information("SCHEDULER: Loaded %d event%s", count, (count == 1)?"":"s"); DBUG_PRINT("info", ("Status code %d. Loaded %d event(s)", ret, count)); DBUG_RETURN(ret); diff --git a/sql/event_priv.h b/sql/event_priv.h index 7d1cdbcd264..7bcb26aaed0 100644 --- a/sql/event_priv.h +++ b/sql/event_priv.h @@ -19,6 +19,10 @@ #include "mysql_priv.h" +#define EVENT_EXEC_STARTED 0 +#define EVENT_EXEC_ALREADY_EXEC 1 +#define EVENT_EXEC_CANT_FORK 2 + #define EVEX_USE_QUEUE #define UNLOCK_MUTEX_AND_BAIL_OUT(__mutex, __label) \ @@ -32,10 +36,10 @@ int my_time_compare(TIME *a, TIME *b); int -evex_db_find_event_aux(THD *thd, const LEX_STRING dbname, - const LEX_STRING rname, - const LEX_STRING definer, - TABLE *table); +evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname, + const LEX_STRING ev_name, + const LEX_STRING user_name, + TABLE *table); int event_timed_compare_q(void *vptr, byte* a, byte *b); diff --git a/sql/event_timed.cc b/sql/event_timed.cc index 28d21089b74..19edaf345dd 100644 --- a/sql/event_timed.cc +++ b/sql/event_timed.cc @@ -879,11 +879,12 @@ event_timed::drop(THD *thd) TABLE *table; int ret= 0; DBUG_ENTER("event_timed::drop"); + DBUG_PRINT("info",("%s.%s", dbname.str, name.str)); if (evex_open_event_table(thd, TL_WRITE, &table)) DBUG_RETURN(-1); - if (evex_db_find_event_aux(thd, dbname, name, definer, table)) + if (evex_db_find_event_by_name(thd, dbname, name, definer, table)) DBUG_RETURN(-2); if ((ret= table->file->ha_delete_row(table->record[0]))) @@ -919,7 +920,7 @@ event_timed::update_fields(THD *thd) } - if ((ret= evex_db_find_event_aux(thd, dbname, name, definer, table))) + if ((ret= evex_db_find_event_by_name(thd, dbname, name, definer, table))) goto done; store_record(table,record[1]); @@ -1059,6 +1060,7 @@ event_timed::compile(THD *thd, MEM_ROOT *mem_root) MEM_ROOT *tmp_mem_root= 0; LEX *old_lex= thd->lex, lex; char *old_db; + int old_db_length; event_timed *ett; sp_name *spn; char *old_query; @@ -1088,7 +1090,9 @@ event_timed::compile(THD *thd, MEM_ROOT *mem_root) old_query_len= thd->query_length; old_query= thd->query; old_db= thd->db; + old_db_length= thd->db_length; thd->db= dbname.str; + thd->db_length= dbname.length; thd->query= get_show_create_event(thd, &thd->query_length); DBUG_PRINT("event_timed::compile", ("query:%s",thd->query)); @@ -1148,3 +1152,135 @@ done: DBUG_RETURN(ret); } + +/* + Checks whether this thread can lock the object for modification -> + preventing being spawned for execution, and locks if possible. + use ::can_spawn_now() only for basic checking because a race + condition may occur between the check and eventual modification (deletion) + of the object. + + Returns + true - locked + false - cannot lock +*/ + +my_bool +event_timed::can_spawn_now_n_lock(THD *thd) +{ + my_bool ret= FALSE; + VOID(pthread_mutex_lock(&this->LOCK_running)); + if (!in_spawned_thread) + { + in_spawned_thread= TRUE; + ret= TRUE; + locked_by_thread_id= thd->thread_id; + } + VOID(pthread_mutex_unlock(&this->LOCK_running)); + return ret; +} + + +extern pthread_attr_t connection_attrib; + +/* + Checks whether is possible and forks a thread. Passes self as argument. + + Returns + EVENT_EXEC_STARTED - OK + EVENT_EXEC_ALREADY_EXEC - Thread not forked, already working + EVENT_EXEC_CANT_FORK - Unable to spawn thread (error) +*/ + +int +event_timed::spawn_now(void * (*thread_func)(void*)) +{ + int ret= EVENT_EXEC_STARTED; + static uint exec_num= 0; + DBUG_ENTER("event_timed::spawn_now"); + DBUG_PRINT("info", ("[%s.%s]", dbname.str, name.str)); + + VOID(pthread_mutex_lock(&this->LOCK_running)); + if (!in_spawned_thread) + { + pthread_t th; + in_spawned_thread= true; + if (pthread_create(&th, &connection_attrib, thread_func, (void*)this)) + { + DBUG_PRINT("info", ("problem while spawning thread")); + ret= EVENT_EXEC_CANT_FORK; + in_spawned_thread= false; + } +#ifndef DBUG_OFF + else + { + sql_print_information("SCHEDULER: Started thread %d", ++exec_num); + DBUG_PRINT("info", ("thread spawned")); + } +#endif + } + else + { + DBUG_PRINT("info", ("already in spawned thread. skipping")); + ret= EVENT_EXEC_ALREADY_EXEC; + } + VOID(pthread_mutex_unlock(&this->LOCK_running)); + + DBUG_RETURN(ret); +} + + +void +event_timed::spawn_thread_finish(THD *thd) +{ + DBUG_ENTER("event_timed::spawn_thread_finish"); + VOID(pthread_mutex_lock(&this->LOCK_running)); + in_spawned_thread= false; + if ((flags & EVENT_EXEC_NO_MORE) || status == MYSQL_EVENT_DISABLED) + { + DBUG_PRINT("info", ("%s exec no more. to drop=%d", name.str, dropped)); + if (dropped) + drop(thd); + VOID(pthread_mutex_unlock(&this->LOCK_running)); + delete this; + DBUG_VOID_RETURN; + } + VOID(pthread_mutex_unlock(&this->LOCK_running)); + DBUG_VOID_RETURN; +} + + +/* + Unlocks the object after it has been locked with ::can_spawn_now_n_lock() + + Returns + 0 - ok + 1 - not locked by this thread + +*/ + + +int +event_timed::spawn_unlock(THD *thd) +{ + int ret= 0; + VOID(pthread_mutex_lock(&this->LOCK_running)); + if (!in_spawned_thread) + { + if (locked_by_thread_id == thd->thread_id) + { + in_spawned_thread= FALSE; + locked_by_thread_id= 0; + } + else + { + sql_print_error("A thread tries to unlock when he hasn't locked. " + "thread_id=%ld locked by %ld", + thd->thread_id, locked_by_thread_id); + DBUG_ASSERT(0); + ret= 1; + } + } + VOID(pthread_mutex_unlock(&this->LOCK_running)); + return ret; +} diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 5ffa4fd76ed..2b026783ca5 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -20,6 +20,7 @@ #include "mysql_priv.h" #include #include "sp.h" +#include "event.h" #include #include #ifdef __WIN__ @@ -748,6 +749,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) exit: (void)sp_drop_db_routines(thd, db); /* QQ Ignore errors for now */ + (void)evex_drop_db_events(thd, db); /* QQ Ignore errors for now */ start_waiting_global_read_lock(thd); /* If this database was the client's selected database, we silently change the From 6892c2a52a58c2519934245327ce2fe159140442 Mon Sep 17 00:00:00 2001 From: "jimw@mysql.com" <> Date: Wed, 15 Feb 2006 17:01:31 -0800 Subject: [PATCH 18/20] Small post-merge fix to partition test --- mysql-test/r/partition.result | 1 + mysql-test/t/partition.test | 1 + 2 files changed, 2 insertions(+) diff --git a/mysql-test/r/partition.result b/mysql-test/r/partition.result index 6b056dd3782..e2601937da6 100644 --- a/mysql-test/r/partition.result +++ b/mysql-test/r/partition.result @@ -305,6 +305,7 @@ create table t1 (f_int1 int(11) default null) engine = memory partition by range (f_int1) subpartition by hash (f_int1) (partition part1 values less than (1000) (subpartition subpart11 engine = memory)); +drop table t1; set session storage_engine='myisam'; create table t1 (f_int1 integer, f_int2 integer, primary key (f_int1)) partition by hash(f_int1) partitions 2; diff --git a/mysql-test/t/partition.test b/mysql-test/t/partition.test index 41d1d61677d..ded54ad28b4 100644 --- a/mysql-test/t/partition.test +++ b/mysql-test/t/partition.test @@ -389,6 +389,7 @@ create table t1 (f_int1 int(11) default null) engine = memory partition by range (f_int1) subpartition by hash (f_int1) (partition part1 values less than (1000) (subpartition subpart11 engine = memory)); +drop table t1; set session storage_engine='myisam'; # From 212d8c05e61c610c10598646dceb82bdd76c22f6 Mon Sep 17 00:00:00 2001 From: "tomas@poseidon.ndb.mysql.com" <> Date: Thu, 16 Feb 2006 03:23:43 +0100 Subject: [PATCH 19/20] Bug #17415 special character tables are not handled correctly in ndb binlog/schema dist --- sql/ha_ndbcluster.cc | 61 ++++++++++++++++++------------------- sql/ha_ndbcluster_binlog.cc | 21 +++++-------- 2 files changed, 37 insertions(+), 45 deletions(-) diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 82cad126d58..e423a38124b 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -477,8 +477,7 @@ ha_ndbcluster::invalidate_dictionary_cache(TABLE_SHARE *share, Ndb *ndb, #ifdef HAVE_NDB_BINLOG char key[FN_REFLEN]; - strxnmov(key, FN_LEN-1, mysql_data_home, "/", - dbname, "/", tabname, NullS); + build_table_filename(key, sizeof(key), dbname, tabname, ""); DBUG_PRINT("info", ("Getting ndbcluster mutex")); pthread_mutex_lock(&ndbcluster_mutex); NDB_SHARE *ndb_share= (NDB_SHARE*)hash_search(&ndbcluster_open_tables, @@ -4191,16 +4190,14 @@ int ha_ndbcluster::create(const char *name, NDBCOL col; uint pack_length, length, i, pk_length= 0; const void *data, *pack_data; - char name2[FN_HEADLEN]; bool create_from_engine= (info->table_options & HA_OPTION_CREATE_FROM_ENGINE); DBUG_ENTER("ha_ndbcluster::create"); DBUG_PRINT("enter", ("name: %s", name)); - strcpy(name2, name); - DBUG_ASSERT(*fn_rext((char*)name2) == 0); - set_dbname(name2); - set_tabname(name2); + DBUG_ASSERT(*fn_rext((char*)name) == 0); + set_dbname(name); + set_tabname(name); table= form; if (create_from_engine) @@ -4213,7 +4210,7 @@ int ha_ndbcluster::create(const char *name, if ((my_errno= write_ndb_file(name))) DBUG_RETURN(my_errno); #ifdef HAVE_NDB_BINLOG - ndbcluster_create_binlog_setup(get_ndb(), name2, strlen(name2), + ndbcluster_create_binlog_setup(get_ndb(), name, strlen(name), m_dbname, m_tabname, FALSE); #endif /* HAVE_NDB_BINLOG */ DBUG_RETURN(my_errno); @@ -4361,18 +4358,18 @@ int ha_ndbcluster::create(const char *name, First make sure we get a "fresh" share here, not an old trailing one... */ { - const char *key= name2; - uint length= (uint) strlen(key); + uint length= (uint) strlen(name); if ((share= (NDB_SHARE*) hash_search(&ndbcluster_open_tables, - (byte*) key, length))) + (byte*) name, length))) handle_trailing_share(share); } /* get a new share */ - if (!(share= get_share(name2, form, true, true))) + + if (!(share= get_share(name, form, true, true))) { - sql_print_error("NDB: allocating table share for %s failed", name2); + sql_print_error("NDB: allocating table share for %s failed", name); /* my_errno is set */ } pthread_mutex_unlock(&ndbcluster_mutex); @@ -4413,7 +4410,7 @@ int ha_ndbcluster::create(const char *name, ndbcluster_create_event_ops(share, t, event_name.c_ptr()) < 0) { sql_print_error("NDB Binlog: FAILED CREATE TABLE event operations." - " Event: %s", name2); + " Event: %s", name); /* a warning has been issued to the client */ } if (share && !do_event_op) @@ -5285,7 +5282,7 @@ int ndbcluster_discover(THD* thd, const char *db, const char *name, NDBDICT* dict= ndb->getDictionary(); dict->set_local_table_data_size(sizeof(Ndb_local_table_statistics)); dict->invalidateTable(name); - strxnmov(key, FN_LEN-1, mysql_data_home, "/", db, "/", name, NullS); + build_table_filename(key, sizeof(key), db, name, ""); NDB_SHARE *share= get_share(key, 0, false); if (share && get_ndb_share_state(share) == NSS_ALTERED) { @@ -5419,13 +5416,14 @@ int ndbcluster_drop_database_impl(const char *path) } // Drop any tables belonging to database char full_path[FN_REFLEN]; - char *tmp= strxnmov(full_path, FN_REFLEN-1, share_prefix, dbname, "/", - NullS); + char *tmp= full_path + + build_table_filename(full_path, sizeof(full_path), dbname, "", ""); + ndb->setDatabaseName(dbname); List_iterator_fast it(drop_list); while ((tabname=it++)) { - strxnmov(tmp, FN_REFLEN - (tmp - full_path)-1, tabname, NullS); + tablename_to_filename(tabname, tmp, FN_REFLEN - (tmp - full_path)-1); if (ha_ndbcluster::delete_table(0, ndb, full_path, dbname, tabname)) { const NdbError err= dict->getNdbError(); @@ -5518,14 +5516,16 @@ int ndbcluster_find_all_files(THD *thd) continue; /* check if database exists */ - char *end= strxnmov(key, FN_LEN-1, mysql_data_home, "/", - elmt.database, NullS); + char *end= key + + build_table_filename(key, sizeof(key), elmt.database, "", ""); if (my_access(key, F_OK)) { /* no such database defined, skip table */ continue; } - end= strxnmov(end, FN_LEN-1-(end-key), "/", elmt.name, NullS); + /* finalize construction of path */ + end+= tablename_to_filename(elmt.name, end, + sizeof(key)-(end-key)); const void *data= 0, *pack_data= 0; uint length, pack_length; int discover= 0; @@ -5660,10 +5660,9 @@ int ndbcluster_find_files(THD *thd,const char *db,const char *path, } // File is not in NDB, check for .ndb file with this name - (void)strxnmov(name, FN_REFLEN-1, - mysql_data_home,"/",db,"/",file_name,ha_ndb_ext,NullS); + build_table_filename(name, sizeof(name), db, file_name, ha_ndb_ext); DBUG_PRINT("info", ("Check access for %s", name)); - if (access(name, F_OK)) + if (my_access(name, F_OK)) { DBUG_PRINT("info", ("%s did not exist on disk", name)); // .ndb file did not exist on disk, another table type @@ -5685,12 +5684,13 @@ int ndbcluster_find_files(THD *thd,const char *db,const char *path, #ifdef HAVE_NDB_BINLOG /* setup logging to binlog for all discovered tables */ { - char *end, *end1= - strxnmov(name, sizeof(name), mysql_data_home, "/", db, "/", NullS); + char *end, *end1= name + + build_table_filename(name, sizeof(name), db, "", ""); for (i= 0; i < ok_tables.records; i++) { file_name= (char*)hash_element(&ok_tables, i); - end= strxnmov(end1, sizeof(name) - (end1 - name), file_name, NullS); + end= end1 + + tablename_to_filename(file_name, end1, sizeof(name) - (end1 - name)); pthread_mutex_lock(&LOCK_open); ndbcluster_create_binlog_setup(ndb, name, end-name, db, file_name, TRUE); @@ -5707,9 +5707,8 @@ int ndbcluster_find_files(THD *thd,const char *db,const char *path, file_name= hash_element(&ndb_tables, i); if (!hash_search(&ok_tables, file_name, strlen(file_name))) { - strxnmov(name, sizeof(name)-1, - mysql_data_home, "/", db, "/", file_name, reg_ext, NullS); - if (access(name, F_OK)) + build_table_filename(name, sizeof(name), db, file_name, reg_ext); + if (my_access(name, F_OK)) { DBUG_PRINT("info", ("%s must be discovered", file_name)); // File is in list of ndb tables and not in ok_tables @@ -6243,7 +6242,7 @@ uint ndb_get_commitcount(THD *thd, char *dbname, char *tabname, NDB_SHARE *share; DBUG_ENTER("ndb_get_commitcount"); - (void)strxnmov(name, FN_REFLEN-1, share_prefix, dbname, "/", tabname, NullS); + build_table_filename(name, sizeof(name), dbname, tabname, ""); DBUG_PRINT("enter", ("name: %s", name)); pthread_mutex_lock(&ndbcluster_mutex); if (!(share=(NDB_SHARE*) hash_search(&ndbcluster_open_tables, diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index 10e6ce63274..ffb746b712b 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -659,11 +659,8 @@ static int ndbcluster_create_apply_status_table(THD *thd) if so, remove it since there is none in Ndb */ { - strxnmov(buf, sizeof(buf), - mysql_data_home, - "/" NDB_REP_DB "/" NDB_APPLY_TABLE, - reg_ext, NullS); - unpack_filename(buf,buf); + build_table_filename(buf, sizeof(buf), + NDB_REP_DB, NDB_APPLY_TABLE, reg_ext); my_delete(buf, MYF(0)); } @@ -711,11 +708,8 @@ static int ndbcluster_create_schema_table(THD *thd) if so, remove it since there is none in Ndb */ { - strxnmov(buf, sizeof(buf), - mysql_data_home, - "/" NDB_REP_DB "/" NDB_SCHEMA_TABLE, - reg_ext, NullS); - unpack_filename(buf,buf); + build_table_filename(buf, sizeof(buf), + NDB_REP_DB, NDB_SCHEMA_TABLE, reg_ext); my_delete(buf, MYF(0)); } @@ -940,8 +934,7 @@ int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share, if (get_a_share) { char key[FN_REFLEN]; - (void)strxnmov(key, FN_REFLEN, share_prefix, db, - "/", table_name, NullS); + build_table_filename(key, sizeof(key), db, table_name, ""); share= get_share(key, 0, false, false); } @@ -1434,8 +1427,8 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb, case SOT_CLEAR_SLOCK: { char key[FN_REFLEN]; - (void)strxnmov(key, FN_REFLEN, share_prefix, schema->db, - "/", schema->name, NullS); + build_table_filename(key, sizeof(key), + schema->db, schema->name, ""); NDB_SHARE *share= get_share(key, 0, false, false); if (share) { From 7b2650a25663beb61f3a9fab7c2ded5dbbd99131 Mon Sep 17 00:00:00 2001 From: "andrey@lmy004." <> Date: Thu, 16 Feb 2006 05:21:02 +0100 Subject: [PATCH 20/20] small post-merge fixes for bug#16406 (pre-push) --- mysql-test/r/events.result | 10 +++++++- mysql-test/t/events.test | 12 +++++++-- sql/event.cc | 4 +-- sql/event.h | 3 ++- sql/event_timed.cc | 50 ++++++++++++++++++++++++++------------ 5 files changed, 57 insertions(+), 22 deletions(-) diff --git a/mysql-test/r/events.result b/mysql-test/r/events.result index f7b28056c1a..2ab2dd0076c 100644 --- a/mysql-test/r/events.result +++ b/mysql-test/r/events.result @@ -23,6 +23,7 @@ DROP EVENT e_x2; DROP DATABASE db_x; DROP USER pauline@localhost; USE events_test; +SET GLOBAL event_scheduler=0; drop event if exists event1; Warnings: Note 1305 Event event1 does not exist @@ -43,6 +44,14 @@ select db, name, body, status, interval_field, interval_value from mysql.event; db name body status interval_field interval_value events_test e_43 set @a = 4 ENABLED SECOND 1 drop event e_43; +"Let's check whether we can use non-qualified names" +create table non_qualif(a int); +create event non_qualif_ev on schedule every 10 minute do insert into non_qualif values (800219); +select * from non_qualif; +a +800219 +drop event non_qualif_ev; +drop table non_qualif; set global event_scheduler = 0; create table t_event3 (a int, b float); drop event if exists event3; @@ -374,7 +383,6 @@ show processlist; Id User Host db Command Time State Info # root localhost events_test Query # NULL show processlist # event_scheduler connecting host NULL Connect # Sleeping NULL -# root localhost events_test Connect # User lock select get_lock("test_lock2_1", 20) "Release the lock so the child process should finish. Hence the scheduler also" select release_lock("test_lock2_1"); release_lock("test_lock2_1") diff --git a/mysql-test/t/events.test b/mysql-test/t/events.test index 2755d4ec02e..6cad9991c44 100644 --- a/mysql-test/t/events.test +++ b/mysql-test/t/events.test @@ -28,11 +28,11 @@ connection default; DROP DATABASE db_x; DROP USER pauline@localhost; USE events_test; ---sleep 1 # # END: BUG #17289 Events: missing privilege check for drop database # - +SET GLOBAL event_scheduler=0; +--sleep 1 drop event if exists event1; create event event1 on schedule every 15 minute starts now() ends date_add(now(), interval 5 hour) DO begin end; alter event event1 rename to event2 enable; @@ -56,6 +56,14 @@ alter event e_43 do alter event e_43 do set @a = 4; select db, name, body, status, interval_field, interval_value from mysql.event; drop event e_43; --sleep 1 + +--echo "Let's check whether we can use non-qualified names" +create table non_qualif(a int); +create event non_qualif_ev on schedule every 10 minute do insert into non_qualif values (800219); +--sleep 2 +select * from non_qualif; +drop event non_qualif_ev; +drop table non_qualif; set global event_scheduler = 0; create table t_event3 (a int, b float); diff --git a/sql/event.cc b/sql/event.cc index 76d802b9ac0..d9e71a263b8 100644 --- a/sql/event.cc +++ b/sql/event.cc @@ -1272,7 +1272,7 @@ int db_drop_event(THD *thd, event_timed *et, bool drop_if_exists, goto done; } - if (!(ret= evex_db_find_event_aux(thd, et->dbname,et->name,et->definer,table))) + if (!(ret= evex_db_find_event_aux(thd, et, table))) { if ((ret= table->file->ha_delete_row(table->record[0]))) { @@ -1452,7 +1452,7 @@ evex_drop_db_events(THD *thd, char *db) READ_RECORD read_record_info; MYSQL_LOCK *lock; int ret= 0; - int i; + uint i; LEX_STRING db_lex= {db, strlen(db)}; DBUG_ENTER("evex_drop_db_events"); diff --git a/sql/event.h b/sql/event.h index f6e1ecd3188..6df6267abc4 100644 --- a/sql/event.h +++ b/sql/event.h @@ -247,7 +247,8 @@ public: } protected: bool - change_security_context(THD *thd, Security_context **backup); + change_security_context(THD *thd, Security_context *s_ctx, + Security_context **backup); void restore_security_context(THD *thd, Security_context *backup); diff --git a/sql/event_timed.cc b/sql/event_timed.cc index d76a9777d4a..cb0c0167a54 100644 --- a/sql/event_timed.cc +++ b/sql/event_timed.cc @@ -412,21 +412,30 @@ event_timed::init_definer(THD *thd) { DBUG_ENTER("event_timed::init_definer"); + DBUG_PRINT("info",("init definer_user thd->mem_root=0x%lx " + "thd->sec_ctx->priv_user=0x%lx", thd->mem_root, + thd->security_ctx->priv_user)); definer_user.str= strdup_root(thd->mem_root, thd->security_ctx->priv_user); definer_user.length= strlen(thd->security_ctx->priv_user); + DBUG_PRINT("info",("init definer_host thd->s_c->priv_host=0x%lx", + thd->security_ctx->priv_host)); definer_host.str= strdup_root(thd->mem_root, thd->security_ctx->priv_host); definer_host.length= strlen(thd->security_ctx->priv_host); + DBUG_PRINT("info",("init definer as whole")); definer.length= definer_user.length + definer_host.length + 1; definer.str= alloc_root(thd->mem_root, definer.length + 1); + DBUG_PRINT("info",("copy the user")); memcpy(definer.str, definer_user.str, definer_user.length); definer.str[definer_user.length]= '@'; + DBUG_PRINT("info",("copy the host")); memcpy(definer.str + definer_user.length + 1, definer_host.str, definer_host.length); definer.str[definer.length]= '\0'; + DBUG_PRINT("info",("definer initted")); DBUG_RETURN(0); } @@ -1070,6 +1079,8 @@ int event_timed::execute(THD *thd, MEM_ROOT *mem_root) { Security_context *save_ctx; + /* this one is local and not needed after exec */ + Security_context security_ctx; int ret= 0; DBUG_ENTER("event_timed::execute"); @@ -1085,18 +1096,19 @@ event_timed::execute(THD *thd, MEM_ROOT *mem_root) running= true; VOID(pthread_mutex_unlock(&this->LOCK_running)); - // TODO Andrey : make this as member variable and delete in destructor - + DBUG_PRINT("info", ("master_access=%d db_access=%d", + thd->security_ctx->master_access, thd->security_ctx->db_access)); + change_security_context(thd, &security_ctx, &save_ctx); + DBUG_PRINT("info", ("master_access=%d db_access=%d", + thd->security_ctx->master_access, thd->security_ctx->db_access)); + if (!sphead && (ret= compile(thd, mem_root))) goto done; - - DBUG_PRINT("info", ("master_access=%d db_access=%d", - thd->security_ctx->master_access, thd->security_ctx->db_access)); - change_security_context(thd, &save_ctx); - - DBUG_PRINT("info", ("master_access=%d db_access=%d", - thd->security_ctx->master_access, thd->security_ctx->db_access)); - if (mysql_change_db(thd, dbname.str, 0)) + /* Now we are sure we have valid this->sphead so we can copy the context */ + sphead->m_security_ctx= security_ctx; + thd->db= dbname.str; + thd->db_length= dbname.length; + if (!check_access(thd, EVENT_ACL,dbname.str, 0, 0, 0,is_schema_db(dbname.str))) { List empty_item_list; empty_item_list.empty(); @@ -1109,16 +1121,20 @@ event_timed::execute(THD *thd, MEM_ROOT *mem_root) ret= -99; } restore_security_context(thd, save_ctx); - DBUG_PRINT("info", ("master_access=%d db_access=%d", thd->security_ctx->master_access, thd->security_ctx->db_access)); + thd->db= 0; VOID(pthread_mutex_lock(&this->LOCK_running)); running= false; VOID(pthread_mutex_unlock(&this->LOCK_running)); done: - // Don't cache sphead if allocated on another mem_root + /* + 1. Don't cache sphead if allocated on another mem_root + 2. Don't call security_ctx.destroy() because this will free our dbname.str + name.str and definer.str + */ if (mem_root && sphead) { delete sphead; @@ -1143,20 +1159,22 @@ done: 1 - Error (generates error too) */ bool -event_timed::change_security_context(THD *thd, Security_context **backup) +event_timed::change_security_context(THD *thd, Security_context *s_ctx, + Security_context **backup) { DBUG_ENTER("event_timed::change_security_context"); DBUG_PRINT("info",("%s@%s@%s",definer_user.str,definer_host.str, dbname.str)); #ifndef NO_EMBEDDED_ACCESS_CHECKS + s_ctx->init(); *backup= 0; - if (acl_getroot_no_password(&sphead->m_security_ctx, definer_user.str, - definer_host.str, definer_host.str, dbname.str)) + if (acl_getroot_no_password(s_ctx, definer_user.str, definer_host.str, + definer_host.str, dbname.str)) { my_error(ER_NO_SUCH_USER, MYF(0), definer_user.str, definer_host.str); DBUG_RETURN(TRUE); } *backup= thd->security_ctx; - thd->security_ctx= &sphead->m_security_ctx; + thd->security_ctx= s_ctx; #endif DBUG_RETURN(FALSE); }