Commit 6c0b2065 authored by Jon Olav Hauglid's avatar Jon Olav Hauglid

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.
parent d68d3031
...@@ -67,19 +67,15 @@ sp_rcontext::~sp_rcontext() ...@@ -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() ...@@ -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 ...@@ -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 ...@@ -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: ...@@ -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) ...@@ -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) ...@@ -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);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment