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

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