diff options
author | Monty <monty@mariadb.org> | 2017-08-05 19:26:10 +0300 |
---|---|---|
committer | Monty <monty@mariadb.org> | 2017-08-07 03:48:58 +0300 |
commit | 74543698a76c02d1a81e17e5918ecbf6b795607b (patch) | |
tree | 5f814415c2d08f1c9ab0303d673cfe64a9a048d4 /sql | |
parent | 008786aedb7cacf41a371937c6215c41638c1f96 (diff) | |
download | mariadb-git-74543698a76c02d1a81e17e5918ecbf6b795607b.tar.gz |
MDEV-13179 main.errors fails with wrong errno
The problem was that the introduction of max-thread-mem-used can cause
an allocation error very early, even before mysql_parse() is called.
As mysql_parse() calls thd->reset_for_next_command(), which called
clear_error(), the error number was lost.
Fixed by adding an option to have unique messages for each KILL
signal and change max-thread-mem-used to use this new feature.
This removes a lot of problems with the original approach, where
one could get errors signaled silenty almost any time.
ixed by moving clear_error() from reset_for_next_command() to
do_command(), before any memory allocation for the thread.
Related changes:
- reset_for_next_command() now have an optional parameter if we should
call clear_error() or not. By default it's called, but not anymore from
dispatch_command() which was the original problem.
- Added optional paramater to clear_error() to force calling of
reset_diagnostics_area(). Before clear_error() only called
reset_diagnostics_area() if there was no error, so we normally
called reset_diagnostics_area() twice.
- This change removed several duplicated calls to clear_error()
when starting a query.
- Reset max_mem_used on COM_QUIT, to protect against kill during
quit.
- Use fatal_error() instead of setting is_fatal_error (cleanup)
- Set fatal_error if max_thead_mem_used is signaled.
(Same logic we use for other places where we are out of resources)
Diffstat (limited to 'sql')
-rw-r--r-- | sql/debug_sync.cc | 2 | ||||
-rw-r--r-- | sql/log_event.cc | 21 | ||||
-rw-r--r-- | sql/mysqld.cc | 20 | ||||
-rw-r--r-- | sql/mysqld.h | 2 | ||||
-rw-r--r-- | sql/rpl_parallel.cc | 8 | ||||
-rw-r--r-- | sql/sp_rcontext.cc | 3 | ||||
-rw-r--r-- | sql/sql_base.cc | 4 | ||||
-rw-r--r-- | sql/sql_cache.cc | 5 | ||||
-rw-r--r-- | sql/sql_class.cc | 31 | ||||
-rw-r--r-- | sql/sql_class.h | 85 | ||||
-rw-r--r-- | sql/sql_connect.cc | 2 | ||||
-rw-r--r-- | sql/sql_insert.cc | 14 | ||||
-rw-r--r-- | sql/sql_load.cc | 2 | ||||
-rw-r--r-- | sql/sql_parse.cc | 44 | ||||
-rw-r--r-- | sql/sql_select.cc | 2 | ||||
-rw-r--r-- | sql/sql_table.cc | 2 | ||||
-rw-r--r-- | sql/sql_update.cc | 2 | ||||
-rw-r--r-- | sql/threadpool_unix.cc | 2 | ||||
-rw-r--r-- | sql/threadpool_win.cc | 2 | ||||
-rw-r--r-- | sql/wsrep_mysqld.cc | 4 | ||||
-rw-r--r-- | sql/wsrep_thd.cc | 2 |
21 files changed, 165 insertions, 94 deletions
diff --git a/sql/debug_sync.cc b/sql/debug_sync.cc index 8b3412eb732..f6291ca7acc 100644 --- a/sql/debug_sync.cc +++ b/sql/debug_sync.cc @@ -1502,7 +1502,7 @@ static void debug_sync_execute(THD *thd, st_debug_sync_action *action) { if (!--action->hit_limit) { - thd->killed= KILL_QUERY; + thd->set_killed(KILL_QUERY); my_error(ER_DEBUG_SYNC_HIT_LIMIT, MYF(0)); } DBUG_PRINT("debug_sync_exec", ("hit_limit: %lu at: '%s'", diff --git a/sql/log_event.cc b/sql/log_event.cc index 4265a23df2b..92885344cd6 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -371,12 +371,6 @@ static void pretty_print_str(IO_CACHE* cache, const char* str, int len) #if defined(HAVE_REPLICATION) && !defined(MYSQL_CLIENT) -static void clear_all_errors(THD *thd, Relay_log_info *rli) -{ - thd->is_slave_error = 0; - thd->clear_error(); -} - inline int idempotent_error_code(int err_code) { int ret= 0; @@ -4255,7 +4249,7 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi, DBUG_PRINT("info", ("log_pos: %lu", (ulong) log_pos)); - clear_all_errors(thd, const_cast<Relay_log_info*>(rli)); + thd->clear_error(1); current_stmt_is_commit= is_commit(); DBUG_ASSERT(!current_stmt_is_commit || !rgi->tables_to_lock); @@ -4475,7 +4469,7 @@ int Query_log_event::do_apply_event(rpl_group_info *rgi, to check/fix it. */ if (mysql_test_parse_for_slave(thd, thd->query(), thd->query_length())) - clear_all_errors(thd, const_cast<Relay_log_info*>(rli)); /* Can ignore query */ + thd->clear_error(1); else { rli->report(ERROR_LEVEL, expected_error, rgi->gtid_info(), @@ -4556,7 +4550,7 @@ compare_errors: ignored_error_code(actual_error)) { DBUG_PRINT("info",("error ignored")); - clear_all_errors(thd, const_cast<Relay_log_info*>(rli)); + thd->clear_error(1); if (actual_error == ER_QUERY_INTERRUPTED || actual_error == ER_CONNECTION_KILLED) thd->reset_killed(); @@ -6025,8 +6019,7 @@ int Load_log_event::do_apply_event(NET* net, rpl_group_info *rgi, new_db.str= (char *) rpl_filter->get_rewrite_db(db, &new_db.length); thd->set_db(new_db.str, new_db.length); DBUG_ASSERT(thd->query() == 0); - thd->is_slave_error= 0; - clear_all_errors(thd, const_cast<Relay_log_info*>(rli)); + thd->clear_error(1); /* see Query_log_event::do_apply_event() and BUG#13360 */ DBUG_ASSERT(!rgi->m_table_map.count()); @@ -6036,7 +6029,7 @@ int Load_log_event::do_apply_event(NET* net, rpl_group_info *rgi, */ lex_start(thd); thd->lex->local_file= local_fname; - thd->reset_for_next_command(); + thd->reset_for_next_command(0); // Errors are cleared above /* We test replicate_*_db rules. Note that we have already prepared @@ -10091,7 +10084,7 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi) slave_rows_error_report(WARNING_LEVEL, error, rgi, thd, table, get_type_str(), RPL_LOG_NAME, (ulong) log_pos); - clear_all_errors(thd, const_cast<Relay_log_info*>(rli)); + thd->clear_error(1); error= 0; if (idempotent_error == 0) break; @@ -10143,7 +10136,7 @@ int Rows_log_event::do_apply_event(rpl_group_info *rgi) slave_rows_error_report(WARNING_LEVEL, error, rgi, thd, table, get_type_str(), RPL_LOG_NAME, (ulong) log_pos); - clear_all_errors(thd, const_cast<Relay_log_info*>(rli)); + thd->clear_error(1); error= 0; } } // if (table) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 17172207613..e363c6de3f1 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -878,7 +878,7 @@ PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list, key_LOCK_prepared_stmt_count, key_LOCK_rpl_status, key_LOCK_server_started, key_LOCK_status, key_LOCK_show_status, - key_LOCK_system_variables_hash, key_LOCK_thd_data, + key_LOCK_system_variables_hash, key_LOCK_thd_data, key_LOCK_thd_kill, key_LOCK_user_conn, key_LOCK_uuid_short_generator, key_LOG_LOCK_log, key_master_info_data_lock, key_master_info_run_lock, key_master_info_sleep_lock, key_master_info_start_stop_lock, @@ -949,6 +949,7 @@ static PSI_mutex_info all_server_mutexes[]= { &key_LOCK_wait_commit, "wait_for_commit::LOCK_wait_commit", 0}, { &key_LOCK_gtid_waiting, "gtid_waiting::LOCK_gtid_waiting", 0}, { &key_LOCK_thd_data, "THD::LOCK_thd_data", 0}, + { &key_LOCK_thd_kill, "THD::LOCK_thd_kill", 0}, { &key_LOCK_user_conn, "LOCK_user_conn", PSI_FLAG_GLOBAL}, { &key_LOCK_uuid_short_generator, "LOCK_uuid_short_generator", PSI_FLAG_GLOBAL}, { &key_LOG_LOCK_log, "LOG::LOCK_log", 0}, @@ -1650,7 +1651,7 @@ static void close_connections(void) if (WSREP(tmp) && (tmp->wsrep_exec_mode==REPL_RECV || tmp->wsrep_applier)) continue; #endif - tmp->killed= KILL_SERVER_HARD; + tmp->set_killed(KILL_SERVER_HARD); MYSQL_CALLBACK(thread_scheduler, post_kill_notification, (tmp)); mysql_mutex_lock(&tmp->LOCK_thd_data); if (tmp->mysys_var) @@ -1738,7 +1739,7 @@ static void close_connections(void) if (WSREP(tmp) && tmp->wsrep_exec_mode==REPL_RECV) { sql_print_information("closing wsrep system thread"); - tmp->killed= KILL_CONNECTION; + tmp->set_killed(KILL_CONNECTION); MYSQL_CALLBACK(thread_scheduler, post_kill_notification, (tmp)); if (tmp->mysys_var) { @@ -3943,11 +3944,16 @@ static void my_malloc_size_cb_func(long long size, my_bool is_thread_specific) thd->status_var.local_memory_used > (int64)thd->variables.max_mem_used && !thd->killed && !thd->get_stmt_da()->is_set()) { - char buf[1024]; - thd->killed= KILL_QUERY; + /* Ensure we don't get called here again */ + char buf[50], *buf2; + thd->set_killed(KILL_QUERY); my_snprintf(buf, sizeof(buf), "--max-thread-mem-used=%llu", thd->variables.max_mem_used); - my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), buf); + if ((buf2= (char*) thd->alloc(256))) + { + my_snprintf(buf2, 256, ER_THD(thd, ER_OPTION_PREVENTS_STATEMENT), buf); + thd->set_killed(KILL_QUERY, ER_OPTION_PREVENTS_STATEMENT, buf2); + } } DBUG_ASSERT((longlong) thd->status_var.local_memory_used >= 0); } @@ -6318,7 +6324,7 @@ void create_thread_to_handle_connection(THD *thd) DBUG_PRINT("error", ("Can't create thread to handle request (error %d)", error)); - thd->killed= KILL_CONNECTION; // Safety + thd->set_killed(KILL_CONNECTION); // Safety mysql_mutex_unlock(&LOCK_thread_count); mysql_mutex_lock(&LOCK_connection_count); diff --git a/sql/mysqld.h b/sql/mysqld.h index 419dbac1613..0725d862553 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -290,7 +290,7 @@ extern PSI_mutex_key key_BINLOG_LOCK_index, key_BINLOG_LOCK_xid_list, key_LOCK_prepared_stmt_count, key_LOCK_rpl_status, key_LOCK_server_started, key_LOCK_status, key_LOCK_show_status, - key_LOCK_thd_data, + key_LOCK_thd_data, key_LOCK_thd_kill, key_LOCK_user_conn, key_LOG_LOCK_log, key_master_info_data_lock, key_master_info_run_lock, key_master_info_sleep_lock, key_master_info_start_stop_lock, diff --git a/sql/rpl_parallel.cc b/sql/rpl_parallel.cc index f05239ce1ba..35cddee6d4d 100644 --- a/sql/rpl_parallel.cc +++ b/sql/rpl_parallel.cc @@ -714,9 +714,7 @@ do_retry: DBUG_EXECUTE_IF("inject_mdev8031", { /* Simulate that we get deadlock killed at this exact point. */ rgi->killed_for_retry= rpl_group_info::RETRY_KILL_KILLED; - mysql_mutex_lock(&thd->LOCK_thd_data); - thd->killed= KILL_CONNECTION; - mysql_mutex_unlock(&thd->LOCK_thd_data); + thd->set_killed(KILL_CONNECTION); }); rgi->cleanup_context(thd, 1); wait_for_pending_deadlock_kill(thd, rgi); @@ -862,9 +860,7 @@ do_retry: /* Simulate that we get deadlock killed during open_binlog(). */ thd->reset_for_next_command(); rgi->killed_for_retry= rpl_group_info::RETRY_KILL_KILLED; - mysql_mutex_lock(&thd->LOCK_thd_data); - thd->killed= KILL_CONNECTION; - mysql_mutex_unlock(&thd->LOCK_thd_data); + thd->set_killed(KILL_CONNECTION); thd->send_kill_message(); fd= (File)-1; err= 1; diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index b38f4a3ddad..4d74d2721f1 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -331,8 +331,7 @@ bool sp_rcontext::handle_sql_condition(THD *thd, /* Reset error state. */ thd->clear_error(); - thd->killed= NOT_KILLED; // Some errors set thd->killed - // (e.g. "bad data"). + thd->reset_killed(); // Some errors set thd->killed, (e.g. "bad data"). /* Add a frame to handler-call-stack. */ Sql_condition_info *cond_info= diff --git a/sql/sql_base.cc b/sql/sql_base.cc index ae10c0b771d..1aaeb2a5584 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -400,7 +400,7 @@ void kill_delayed_threads_for_table(TDC_element *element) if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) && ! in_use->killed) { - in_use->killed= KILL_SYSTEM_THREAD; + in_use->set_killed(KILL_SYSTEM_THREAD); mysql_mutex_lock(&in_use->mysys_var->mutex); if (in_use->mysys_var->current_cond) { @@ -9136,7 +9136,7 @@ bool mysql_notify_thread_having_shared_lock(THD *thd, THD *in_use, if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) && !in_use->killed) { - in_use->killed= KILL_SYSTEM_THREAD; + in_use->set_killed(KILL_SYSTEM_THREAD); mysql_mutex_lock(&in_use->mysys_var->mutex); if (in_use->mysys_var->current_cond) { diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index c69303c5273..66ac2449519 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -2160,8 +2160,7 @@ lookup: response, we can't handle it anyway. */ (void) trans_commit_stmt(thd); - if (!thd->get_stmt_da()->is_set()) - thd->get_stmt_da()->disable_status(); + thd->get_stmt_da()->disable_status(); BLOCK_UNLOCK_RD(query_block); MYSQL_QUERY_CACHE_HIT(thd->query(), (ulong) thd->limit_found_rows); @@ -4615,7 +4614,7 @@ void Query_cache::wreck(uint line, const char *message) DBUG_PRINT("warning", ("%5d QUERY CACHE WRECK => DISABLED",line)); DBUG_PRINT("warning", ("==================================")); if (thd) - thd->killed= KILL_CONNECTION; + thd->set_killed(KILL_CONNECTION); cache_dump(); /* check_integrity(0); */ /* Can't call it here because of locks */ bins_dump(); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 78b8b3f05eb..63663cdd037 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -333,7 +333,7 @@ void thd_set_psi(THD *thd, PSI_thread *psi) */ void thd_set_killed(THD *thd) { - thd->killed= KILL_CONNECTION; + thd->set_killed(KILL_CONNECTION); } /** @@ -935,6 +935,7 @@ THD::THD(bool is_wsrep_applier) query_start_used= query_start_sec_part_used= 0; count_cuted_fields= CHECK_FIELD_IGNORE; killed= NOT_KILLED; + killed_err= 0; col_access=0; is_slave_error= thread_specific_used= FALSE; my_hash_clear(&handler_tables_hash); @@ -992,6 +993,7 @@ THD::THD(bool is_wsrep_applier) #endif mysql_mutex_init(key_LOCK_thd_data, &LOCK_thd_data, MY_MUTEX_INIT_FAST); mysql_mutex_init(key_LOCK_wakeup_ready, &LOCK_wakeup_ready, MY_MUTEX_INIT_FAST); + mysql_mutex_init(key_LOCK_thd_kill, &LOCK_thd_kill, MY_MUTEX_INIT_FAST); mysql_cond_init(key_COND_wakeup_ready, &COND_wakeup_ready, 0); /* LOCK_thread_count goes before LOCK_thd_data - the former is called around @@ -1256,7 +1258,7 @@ Sql_condition* THD::raise_condition(uint sql_errno, push_warning and strict SQL_MODE case. */ level= Sql_condition::WARN_LEVEL_ERROR; - killed= KILL_BAD_DATA; + set_killed(KILL_BAD_DATA); } switch (level) @@ -1564,7 +1566,7 @@ void THD::cleanup(void) DBUG_ENTER("THD::cleanup"); DBUG_ASSERT(cleanup_done == 0); - killed= KILL_CONNECTION; + set_killed(KILL_CONNECTION); #ifdef ENABLE_WHEN_BINLOG_WILL_BE_ABLE_TO_PREPARE if (transaction.xid_state.xa_state == XA_PREPARED) { @@ -1667,6 +1669,7 @@ THD::~THD() mysql_cond_destroy(&COND_wakeup_ready); mysql_mutex_destroy(&LOCK_wakeup_ready); mysql_mutex_destroy(&LOCK_thd_data); + mysql_mutex_destroy(&LOCK_thd_kill); #ifndef DBUG_OFF dbug_sentry= THD_SENTRY_GONE; #endif @@ -1839,7 +1842,8 @@ void THD::awake(killed_state state_to_set) state_to_set= killed; /* Set the 'killed' flag of 'this', which is the target THD object. */ - killed= state_to_set; + mysql_mutex_lock(&LOCK_thd_kill); + set_killed_no_mutex(state_to_set); if (state_to_set >= KILL_CONNECTION || state_to_set == NOT_KILLED) { @@ -1925,6 +1929,7 @@ void THD::awake(killed_state state_to_set) } mysql_mutex_unlock(&mysys_var->mutex); } + mysql_mutex_unlock(&LOCK_thd_kill); DBUG_VOID_RETURN; } @@ -1942,7 +1947,7 @@ void THD::disconnect() mysql_mutex_lock(&LOCK_thd_data); - killed= KILL_CONNECTION; + set_killed(KILL_CONNECTION); #ifdef SIGNAL_WITH_VIO_CLOSE /* @@ -1978,7 +1983,7 @@ bool THD::notify_shared_lock(MDL_context_owner *ctx_in_use, DBUG_PRINT("info", ("kill delayed thread")); mysql_mutex_lock(&in_use->LOCK_thd_data); if (in_use->killed < KILL_CONNECTION) - in_use->killed= KILL_CONNECTION; + in_use->set_killed(KILL_CONNECTION); if (in_use->mysys_var) { mysql_mutex_lock(&in_use->mysys_var->mutex); @@ -2031,13 +2036,21 @@ bool THD::notify_shared_lock(MDL_context_owner *ctx_in_use, /* Get error number for killed state Note that the error message can't have any parameters. + If one needs parameters, one should use THD::killed_err_msg See thd::kill_message() */ -int killed_errno(killed_state killed) +int THD::killed_errno() { DBUG_ENTER("killed_errno"); - DBUG_PRINT("enter", ("killed: %d", killed)); + DBUG_PRINT("enter", ("killed: %d killed_errno: %d", + killed, killed_err ? killed_err->no: 0)); + + /* Ensure that killed_err is not set if we are not killed */ + DBUG_ASSERT(!killed_err || killed != NOT_KILLED); + + if (killed_err) + DBUG_RETURN(killed_err->no); switch (killed) { case NOT_KILLED: @@ -2478,7 +2491,7 @@ CHANGED_TABLE_LIST* THD::changed_table_dup(const char *key, long key_length) { my_error(EE_OUTOFMEMORY, MYF(ME_BELL+ME_FATALERROR), ALIGN_SIZE(sizeof(TABLE_LIST)) + key_length + 1); - killed= KILL_CONNECTION; + set_killed(KILL_CONNECTION); return 0; } diff --git a/sql/sql_class.h b/sql/sql_class.h index 342902363c6..44163333425 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -482,7 +482,6 @@ enum killed_state KILL_SERVER_HARD= 15 }; -extern int killed_errno(killed_state killed); #define killed_mask_hard(killed) ((killed_state) ((killed) & ~KILL_HARD_BIT)) enum killed_type @@ -1924,7 +1923,7 @@ public: rpl_sql_thread_info *rpl_sql_info; } system_thread_info; - void reset_for_next_command(); + void reset_for_next_command(bool do_clear_errors= 1); /* Constant for THD::where initialization in the beginning of every query. @@ -1980,6 +1979,8 @@ public: Is locked when THD is deleted. */ mysql_mutex_t LOCK_thd_data; + /* Protect kill information */ + mysql_mutex_t LOCK_thd_kill; /* all prepared statements and cursors of this connection */ Statement_map stmt_map; @@ -2615,7 +2616,7 @@ public: void check_limit_rows_examined() { if (++accessed_rows_and_keys > lex->limit_rows_examined_cnt) - killed= ABORT_QUERY; + set_killed(ABORT_QUERY); } USER_CONN *user_connect; @@ -2716,6 +2717,16 @@ public: */ killed_state volatile killed; + /* + The following is used if one wants to have a specific error number and + text for the kill + */ + struct err_info + { + int no; + const char msg[256]; + } *killed_err; + /* See also thd_killed() */ inline bool check_killed() { @@ -3280,18 +3291,18 @@ public: @todo: To silence an error, one should use Internal_error_handler mechanism. Issuing an error that can be possibly later "cleared" is not compatible with other installed error handlers and audit plugins. - In future this function will be removed. */ - inline void clear_error() + inline void clear_error(bool clear_diagnostics= 0) { DBUG_ENTER("clear_error"); - if (get_stmt_da()->is_error()) + if (get_stmt_da()->is_error() || clear_diagnostics) get_stmt_da()->reset_diagnostics_area(); is_slave_error= 0; if (killed == KILL_BAD_DATA) - killed= NOT_KILLED; // KILL_BAD_DATA can be reset w/o a mutex + reset_killed(); DBUG_VOID_RETURN; } + #ifndef EMBEDDED_LIBRARY inline bool vio_ok() const { return net.vio != 0; } /** Return FALSE if connection to client is broken. */ @@ -3401,10 +3412,54 @@ public: state after execution of a non-prepared SQL statement. */ void end_statement(); - inline int killed_errno() const + + /* + Mark thread to be killed, with optional error number and string. + string is not released, so it has to be allocted on thd mem_root + or be a global string + + Ensure that we don't replace a kill with a lesser one. For example + if user has done 'kill_connection' we shouldn't replace it with + KILL_QUERY. + */ + inline void set_killed(killed_state killed_arg, + int killed_errno_arg= 0, + const char *killed_err_msg_arg= 0) { - return ::killed_errno(killed); + mysql_mutex_lock(&LOCK_thd_kill); + set_killed_no_mutex(killed_arg, killed_errno_arg, killed_err_msg_arg); + mysql_mutex_unlock(&LOCK_thd_kill); } + /* + This is only used by THD::awake where we need to keep the lock mutex + locked over some time. + It's ok to have this inline, as in most cases killed_errno_arg will + be a constant 0 and most of the function will disappear. + */ + inline void set_killed_no_mutex(killed_state killed_arg, + int killed_errno_arg= 0, + const char *killed_err_msg_arg= 0) + { + if (killed <= killed_arg) + { + killed= killed_arg; + if (killed_errno_arg) + { + /* + If alloc fails, we only remember the killed flag. + The worst things that can happen is that we get + a suboptimal error message. + */ + if ((killed_err= (err_info*) alloc(sizeof(*killed_err)))) + { + killed_err->no= killed_errno_arg; + ::strmake((char*) killed_err->msg, killed_err_msg_arg, + sizeof(killed_err->msg)-1); + } + } + } + } + int killed_errno(); inline void reset_killed() { /* @@ -3413,9 +3468,10 @@ public: */ if (killed != NOT_KILLED) { - mysql_mutex_lock(&LOCK_thd_data); + mysql_mutex_lock(&LOCK_thd_kill); killed= NOT_KILLED; - mysql_mutex_unlock(&LOCK_thd_data); + killed_err= 0; + mysql_mutex_unlock(&LOCK_thd_kill); } } inline void reset_kill_query() @@ -3426,11 +3482,14 @@ public: mysys_var->abort= 0; } } - inline void send_kill_message() const + inline void send_kill_message() { + mysql_mutex_lock(&LOCK_thd_kill); int err= killed_errno(); if (err) - my_message(err, ER_THD(this, err), MYF(0)); + my_message(err, killed_err ? killed_err->msg : ER_THD(this, err), + MYF(0)); + mysql_mutex_unlock(&LOCK_thd_kill); } /* return TRUE if we will abort query if we make a warning now */ inline bool really_abort_on_warning() diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 7befde1c1f3..ac5b6ab29a2 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -1196,7 +1196,7 @@ void prepare_new_connection_state(THD* thd) if (thd->is_error()) { Host_errors errors; - thd->killed= KILL_CONNECTION; + thd->set_killed(KILL_CONNECTION); thd->print_aborted_warning(0, "init_connect command failed"); sql_print_warning("%s", thd->get_stmt_da()->message()); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 092b2154c61..721fff389e0 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -2681,7 +2681,7 @@ void kill_delayed_threads(void) { mysql_mutex_lock(&di->thd.LOCK_thd_data); if (di->thd.killed < KILL_CONNECTION) - di->thd.killed= KILL_CONNECTION; + di->thd.set_killed(KILL_CONNECTION); if (di->thd.mysys_var) { mysql_mutex_lock(&di->thd.mysys_var->mutex); @@ -2827,7 +2827,7 @@ pthread_handler_t handle_delayed_insert(void *arg) thd->set_current_time(); threads.append(thd); if (abort_loop) - thd->killed= KILL_CONNECTION; + thd->set_killed(KILL_CONNECTION); else thd->reset_killed(); mysql_mutex_unlock(&LOCK_thread_count); @@ -2972,7 +2972,7 @@ pthread_handler_t handle_delayed_insert(void *arg) } #endif if (error == ETIMEDOUT || error == ETIME) - thd->killed= KILL_CONNECTION; + thd->set_killed(KILL_CONNECTION); } /* We can't lock di->mutex and mysys_var->mutex at the same time */ mysql_mutex_unlock(&di->mutex); @@ -3001,7 +3001,7 @@ pthread_handler_t handle_delayed_insert(void *arg) if (! (thd->lock= mysql_lock_tables(thd, &di->table, 1, 0))) { /* Fatal error */ - thd->killed= KILL_CONNECTION; + thd->set_killed(KILL_CONNECTION); } mysql_cond_broadcast(&di->cond_client); } @@ -3010,7 +3010,7 @@ pthread_handler_t handle_delayed_insert(void *arg) if (di->handle_inserts()) { /* Some fatal error */ - thd->killed= KILL_CONNECTION; + thd->set_killed(KILL_CONNECTION); } } di->status=0; @@ -3054,7 +3054,7 @@ pthread_handler_t handle_delayed_insert(void *arg) this. */ mysql_mutex_lock(&thd->LOCK_thd_data); - thd->killed= KILL_CONNECTION_HARD; // If error + thd->set_killed(KILL_CONNECTION_HARD); // If error thd->mdl_context.set_needs_thr_lock_abort(0); mysql_mutex_unlock(&thd->LOCK_thd_data); @@ -3143,7 +3143,7 @@ bool Delayed_insert::handle_inserts(void) max_rows= delayed_insert_limit; if (thd.killed || table->s->tdc->flushed) { - thd.killed= KILL_SYSTEM_THREAD; + thd.set_killed(KILL_SYSTEM_THREAD); max_rows= ULONG_MAX; // Do as much as possible } diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 0168f46f8ee..25bc29de401 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -613,7 +613,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, DBUG_EXECUTE_IF("simulate_kill_bug27571", { error=1; - thd->killed= KILL_QUERY; + thd->set_killed(KILL_QUERY); };); #ifndef EMBEDDED_LIBRARY diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index c4b32933b8a..a1da14d5c53 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -669,6 +669,7 @@ void execute_init_command(THD *thd, LEX_STRING *init_command, */ save_vio= thd->net.vio; thd->net.vio= 0; + thd->clear_error(1); dispatch_command(COM_QUERY, thd, buf, len); thd->client_capabilities= save_client_capabilities; thd->net.vio= save_vio; @@ -800,6 +801,7 @@ static void handle_bootstrap_impl(THD *thd) if (bootstrap_error) break; + thd->reset_kill_query(); /* Ensure that killed_errmsg is released */ free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC)); free_root(&thd->transaction.mem_root,MYF(MY_KEEP_PREALLOC)); thd->lex->restore_set_statement_var(); @@ -954,13 +956,8 @@ bool do_command(THD *thd) if(!thd->skip_wait_timeout) my_net_set_read_timeout(net, thd->variables.net_wait_timeout); - - /* - XXX: this code is here only to clear possible errors of init_connect. - Consider moving to init_connect() instead. - */ - thd->clear_error(); // Clear error message - thd->get_stmt_da()->reset_diagnostics_area(); + /* Errors and diagnostics are cleared once here before query */ + thd->clear_error(1); net_new_transaction(net); @@ -1123,6 +1120,7 @@ bool do_command(THD *thd) WSREP_WARN("For retry temporally setting character set to : %s", my_charset_latin1.csname); } + thd->clear_error(); return_value= dispatch_command(command, thd, thd->wsrep_retry_query, thd->wsrep_retry_query_len); thd->variables.character_set_client = current_charset; @@ -1272,7 +1270,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, my_error(ER_LOCK_DEADLOCK, MYF(0), "wsrep aborted transaction"); WSREP_DEBUG("Deadlock error for: %s", thd->query()); mysql_mutex_unlock(&thd->LOCK_wsrep_thd); - thd->killed = NOT_KILLED; + thd->reset_killed(); thd->mysys_var->abort = 0; thd->wsrep_conflict_state = NO_CONFLICT; thd->wsrep_retry_counter = 0; @@ -1625,7 +1623,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; } packet= arg_end + 1; - thd->reset_for_next_command(); + thd->reset_for_next_command(0); // Don't clear errors lex_start(thd); /* Must be before we init the table list. */ if (lower_case_table_names) @@ -1694,7 +1692,10 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } #endif case COM_QUIT: - /* We don't calculate statistics for this command */ + /* Note: We don't calculate statistics for this command */ + + /* Ensure that quit works even if max_mem_used is set */ + thd->variables.max_mem_used= LONGLONG_MAX; general_log_print(thd, command, NullS); net->error=0; // Don't give 'abort' message thd->get_stmt_da()->disable_status(); // Don't send anything back @@ -1974,6 +1975,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, dec_thread_running(); thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory + thd->reset_kill_query(); /* Ensure that killed_errmsg is released */ free_root(thd->mem_root,MYF(MY_KEEP_PREALLOC)); #if defined(ENABLED_PROFILING) @@ -5047,7 +5049,7 @@ end_with_restore_list: /* Disconnect the current client connection. */ if (tx_release) { - thd->killed= KILL_CONNECTION; + thd->set_killed(KILL_CONNECTION); thd->print_aborted_warning(3, "RELEASE"); } #ifdef WITH_WSREP @@ -5093,7 +5095,7 @@ end_with_restore_list: } /* Disconnect the current client connection. */ if (tx_release) - thd->killed= KILL_CONNECTION; + thd->set_killed(KILL_CONNECTION); #ifdef WITH_WSREP if (WSREP(thd) && thd->wsrep_conflict_state != NO_CONFLICT) { @@ -6879,6 +6881,8 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize) Reset the part of THD responsible for the state of command processing. + @param do_clear_error Set if we should clear errors + This needs to be called before execution of every statement (prepared or conventional). It is not called by substatements of routines. @@ -6886,12 +6890,16 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize) @todo Call it after we use THD for queries, not before. */ -void THD::reset_for_next_command() +void THD::reset_for_next_command(bool do_clear_error) { THD *thd= this; DBUG_ENTER("THD::reset_for_next_command"); DBUG_ASSERT(!thd->spcont); /* not for substatements of routines */ DBUG_ASSERT(! thd->in_sub_stmt); + + if (do_clear_error) + clear_error(1); + thd->free_list= 0; thd->select_number= 1; /* @@ -6947,8 +6955,6 @@ void THD::reset_for_next_command() reset_dynamic(&thd->user_var_events); thd->user_var_events_alloc= thd->mem_root; } - thd->clear_error(); - thd->get_stmt_da()->reset_diagnostics_area(); thd->get_stmt_da()->reset_for_next_command(); thd->rand_used= 0; thd->m_sent_row_count= thd->m_examined_row_count= 0; @@ -7180,7 +7186,7 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length, thd->wsrep_conflict_state == CERT_FAILURE) { thd->reset_for_next_command(); - thd->killed= NOT_KILLED; + thd->reset_killed(); if (is_autocommit && thd->lex->sql_command != SQLCOM_SELECT && (thd->wsrep_retry_counter < thd->variables.wsrep_retry_autocommit)) @@ -7208,7 +7214,7 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length, thd->thread_id, is_autocommit, thd->wsrep_retry_counter, thd->variables.wsrep_retry_autocommit, thd->query()); my_error(ER_LOCK_DEADLOCK, MYF(0), "wsrep aborted transaction"); - thd->killed= NOT_KILLED; + thd->reset_killed(); thd->wsrep_conflict_state= NO_CONFLICT; if (thd->wsrep_conflict_state != REPLAYING) thd->wsrep_retry_counter= 0; // reset @@ -8282,10 +8288,10 @@ void sql_kill(THD *thd, longlong id, killed_state state, killed_type type) uint error; if (!(error= kill_one_thread(thd, id, state, type))) { - if ((!thd->killed)) + if (!thd->killed) my_ok(thd); else - my_error(killed_errno(thd->killed), MYF(0), id); + thd->send_kill_message(); } else my_error(error, MYF(0), id); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 37d7c801b43..f9da12aac29 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -3592,7 +3592,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, #endif DBUG_EXECUTE_IF("bug11747970_raise_error", - { join->thd->killed= KILL_QUERY_HARD; }); + { join->thd->set_killed(KILL_QUERY_HARD); }); if (error) { table->file->print_error(error, MYF(0)); diff --git a/sql/sql_table.cc b/sql/sql_table.cc index ace8ff1a7a9..29c382f57d6 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4783,7 +4783,7 @@ int create_table_impl(THD *thd, thd->variables.option_bits|= OPTION_KEEP_LOG; thd->log_current_statement= 1; create_info->table_was_deleted= 1; - DBUG_EXECUTE_IF("send_kill_after_delete", thd->killed= KILL_QUERY; ); + DBUG_EXECUTE_IF("send_kill_after_delete", thd->set_killed(KILL_QUERY); ); /* Restart statement transactions for the case of CREATE ... SELECT. diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 5ccc2384b84..a87261aa34b 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -940,7 +940,7 @@ int mysql_update(THD *thd, // simulated killing after the loop must be ineffective for binlogging DBUG_EXECUTE_IF("simulate_kill_bug27571", { - thd->killed= KILL_QUERY; + thd->set_killed(KILL_QUERY); };); error= (killed_status == NOT_KILLED)? error : 1; diff --git a/sql/threadpool_unix.cc b/sql/threadpool_unix.cc index 5d46b8790c5..f1133b22cf5 100644 --- a/sql/threadpool_unix.cc +++ b/sql/threadpool_unix.cc @@ -463,7 +463,7 @@ static void timeout_check(pool_timer_t *timer) { /* Wait timeout exceeded, kill connection. */ mysql_mutex_lock(&thd->LOCK_thd_data); - thd->killed = KILL_CONNECTION; + thd->set_killed(KILL_CONNECTION); post_kill_notification(thd); mysql_mutex_unlock(&thd->LOCK_thd_data); } diff --git a/sql/threadpool_win.cc b/sql/threadpool_win.cc index eda759a24e9..cb44687f154 100644 --- a/sql/threadpool_win.cc +++ b/sql/threadpool_win.cc @@ -613,7 +613,7 @@ static VOID CALLBACK timer_callback(PTP_CALLBACK_INSTANCE instance, if (timeout <= now()) { - con->thd->killed = KILL_CONNECTION; + con->thd->set_killed(KILL_CONNECTION); if(con->thd->net.vio) vio_shutdown(con->thd->net.vio, SD_BOTH); } diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index 9eaaa620200..617aebae82e 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -1965,7 +1965,7 @@ static bool have_client_connections() static void wsrep_close_thread(THD *thd) { - thd->killed= KILL_CONNECTION; + thd->set_killed(KILL_CONNECTION); MYSQL_CALLBACK(thread_scheduler, post_kill_notification, (thd)); if (thd->mysys_var) { @@ -2045,7 +2045,7 @@ void wsrep_close_client_connections(my_bool wait_to_end) if (is_replaying_connection(tmp)) { - tmp->killed= KILL_CONNECTION; + tmp->set_killed(KILL_CONNECTION); continue; } diff --git a/sql/wsrep_thd.cc b/sql/wsrep_thd.cc index 53632f56167..7b73273b8a9 100644 --- a/sql/wsrep_thd.cc +++ b/sql/wsrep_thd.cc @@ -232,7 +232,7 @@ void wsrep_replay_transaction(THD *thd) mysql_mutex_unlock(&thd->LOCK_wsrep_thd); thd->reset_for_next_command(); - thd->killed= NOT_KILLED; + thd->reset_killed(); close_thread_tables(thd); if (thd->locked_tables_mode && thd->lock) { |