mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 12:02:42 +01:00
Bug#14640599 MEMORY LEAK WHEN EXECUTING STORED ROUTINE EXCEPTION HANDLER
When a SP handler is activated, memory is allocated to hold the MESSAGE_TEXT for the condition that caused the activation. The problem was that this memory was allocated on the MEM_ROOT belonging to the stored program. Since this MEM_ROOT is not freed until the stored program ends, a stored program that causes lots of handler activations can start using lots of memory. In 5.1 and earlier the problem did not exist as no MESSAGE_TEXT was allocated if a condition was raised with a handler present. However, this behavior lead to a number of other issues such as Bug#23032. This patch fixes the problem by allocating enough memory for the necessary MESSAGE_TEXTs in the SP MEM_ROOT when the SP starts and then re-using this memory each time a handler is activated. This is the 5.5 version of the patch.
This commit is contained in:
parent
d68d303118
commit
6c0b206570
3 changed files with 54 additions and 14 deletions
|
@ -67,19 +67,15 @@ sp_rcontext::~sp_rcontext()
|
||||||
bool sp_rcontext::init(THD *thd)
|
bool sp_rcontext::init(THD *thd)
|
||||||
{
|
{
|
||||||
uint handler_count= m_root_parsing_ctx->max_handler_index();
|
uint handler_count= m_root_parsing_ctx->max_handler_index();
|
||||||
uint i;
|
|
||||||
|
|
||||||
in_sub_stmt= thd->in_sub_stmt;
|
in_sub_stmt= thd->in_sub_stmt;
|
||||||
|
|
||||||
if (init_var_table(thd) || init_var_items())
|
if (init_var_table(thd) || init_var_items())
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
if (!(m_raised_conditions= new (thd->mem_root) MYSQL_ERROR[handler_count]))
|
if (!(m_raised_conditions= new (thd->mem_root) Sql_condition_info[handler_count]))
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
for (i= 0; i<handler_count; i++)
|
|
||||||
m_raised_conditions[i].init(thd->mem_root);
|
|
||||||
|
|
||||||
return
|
return
|
||||||
!(m_handler=
|
!(m_handler=
|
||||||
(sp_handler_t*)thd->alloc(handler_count * sizeof(sp_handler_t))) ||
|
(sp_handler_t*)thd->alloc(handler_count * sizeof(sp_handler_t))) ||
|
||||||
|
@ -446,13 +442,12 @@ sp_rcontext::exit_handler()
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
MYSQL_ERROR*
|
Sql_condition_info* sp_rcontext::raised_condition() const
|
||||||
sp_rcontext::raised_condition() const
|
|
||||||
{
|
{
|
||||||
if (m_ihsp > 0)
|
if (m_ihsp > 0)
|
||||||
{
|
{
|
||||||
uint hindex= m_in_handler[m_ihsp - 1].index;
|
uint hindex= m_in_handler[m_ihsp - 1].index;
|
||||||
MYSQL_ERROR *raised= & m_raised_conditions[hindex];
|
Sql_condition_info *raised= & m_raised_conditions[hindex];
|
||||||
return raised;
|
return raised;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,46 @@ typedef struct
|
||||||
uint index;
|
uint index;
|
||||||
} sp_active_handler_t;
|
} sp_active_handler_t;
|
||||||
|
|
||||||
|
|
||||||
|
class Sql_condition_info : public Sql_alloc
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/** SQL error code. */
|
||||||
|
uint m_sql_errno;
|
||||||
|
|
||||||
|
/** Error level. */
|
||||||
|
MYSQL_ERROR::enum_warning_level m_level;
|
||||||
|
|
||||||
|
/** SQLSTATE. */
|
||||||
|
char m_sql_state[SQLSTATE_LENGTH + 1];
|
||||||
|
|
||||||
|
/** Text message. */
|
||||||
|
char m_message[MYSQL_ERRMSG_SIZE];
|
||||||
|
|
||||||
|
void set(uint sql_errno, const char* sqlstate,
|
||||||
|
MYSQL_ERROR::enum_warning_level level,
|
||||||
|
const char* msg)
|
||||||
|
{
|
||||||
|
m_sql_errno= sql_errno;
|
||||||
|
m_level= level;
|
||||||
|
|
||||||
|
memcpy(m_sql_state, sqlstate, SQLSTATE_LENGTH);
|
||||||
|
m_sql_state[SQLSTATE_LENGTH]= '\0';
|
||||||
|
|
||||||
|
strncpy(m_message, msg, MYSQL_ERRMSG_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
m_sql_errno= 0;
|
||||||
|
m_level= MYSQL_ERROR::WARN_LEVEL_ERROR;
|
||||||
|
|
||||||
|
m_sql_state[0]= '\0';
|
||||||
|
m_message[0]= '\0';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This class is a runtime context of a Stored Routine. It is used in an
|
This class is a runtime context of a Stored Routine. It is used in an
|
||||||
execution and is intended to contain all dynamic objects (i.e. objects, which
|
execution and is intended to contain all dynamic objects (i.e. objects, which
|
||||||
|
@ -146,8 +186,7 @@ class sp_rcontext : public Sql_alloc
|
||||||
MYSQL_ERROR::enum_warning_level level,
|
MYSQL_ERROR::enum_warning_level level,
|
||||||
const char *msg);
|
const char *msg);
|
||||||
|
|
||||||
MYSQL_ERROR *
|
Sql_condition_info *raised_condition() const;
|
||||||
raised_condition() const;
|
|
||||||
|
|
||||||
void
|
void
|
||||||
push_hstack(uint h);
|
push_hstack(uint h);
|
||||||
|
@ -232,7 +271,7 @@ private:
|
||||||
SQL conditions caught by each handler.
|
SQL conditions caught by each handler.
|
||||||
This is an array indexed by handler index.
|
This is an array indexed by handler index.
|
||||||
*/
|
*/
|
||||||
MYSQL_ERROR *m_raised_conditions;
|
Sql_condition_info *m_raised_conditions;
|
||||||
|
|
||||||
uint m_hcount; // Stack pointer for m_handler
|
uint m_hcount; // Stack pointer for m_handler
|
||||||
uint *m_hstack; // Return stack for continue handlers
|
uint *m_hstack; // Return stack for continue handlers
|
||||||
|
|
|
@ -478,7 +478,7 @@ bool Signal_statement::execute(THD *thd)
|
||||||
|
|
||||||
bool Resignal_statement::execute(THD *thd)
|
bool Resignal_statement::execute(THD *thd)
|
||||||
{
|
{
|
||||||
MYSQL_ERROR *signaled;
|
Sql_condition_info *signaled;
|
||||||
int result= TRUE;
|
int result= TRUE;
|
||||||
|
|
||||||
DBUG_ENTER("Resignal_statement::execute");
|
DBUG_ENTER("Resignal_statement::execute");
|
||||||
|
@ -491,15 +491,21 @@ bool Resignal_statement::execute(THD *thd)
|
||||||
DBUG_RETURN(result);
|
DBUG_RETURN(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MYSQL_ERROR signaled_err(thd->mem_root);
|
||||||
|
signaled_err.set(signaled->m_sql_errno,
|
||||||
|
signaled->m_sql_state,
|
||||||
|
signaled->m_level,
|
||||||
|
signaled->m_message);
|
||||||
|
|
||||||
if (m_cond == NULL)
|
if (m_cond == NULL)
|
||||||
{
|
{
|
||||||
/* RESIGNAL without signal_value */
|
/* RESIGNAL without signal_value */
|
||||||
result= raise_condition(thd, signaled);
|
result= raise_condition(thd, &signaled_err);
|
||||||
DBUG_RETURN(result);
|
DBUG_RETURN(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* RESIGNAL with signal_value */
|
/* RESIGNAL with signal_value */
|
||||||
result= raise_condition(thd, signaled);
|
result= raise_condition(thd, &signaled_err);
|
||||||
|
|
||||||
DBUG_RETURN(result);
|
DBUG_RETURN(result);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue