1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-27 18:02:13 +03:00

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)
This commit is contained in:
Monty
2017-08-05 19:26:10 +03:00
parent 008786aedb
commit 74543698a7
24 changed files with 177 additions and 101 deletions

View File

@ -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);