Skip to content

Commit 256c8cf

Browse files
author
Nisha Gopalakrishnan
committed
BUG#16857395 - EXCESSIVE MEMORY USAGE AFTER REPEATED TRIGGER
EXCEPTION HANDLERS Merge from 5.6 to trunk
2 parents 4bb607d + 007b330 commit 256c8cf

File tree

4 files changed

+81
-29
lines changed

4 files changed

+81
-29
lines changed

sql/sp_instr.cc

+20-11
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,26 @@ LEX *sp_lex_instr::parse_expr(THD *thd, sp_head *sp)
461461
return NULL;
462462
}
463463

464+
// Cleanup current THD from previously held objects before new parsing.
465+
cleanup_before_parsing(thd);
466+
467+
// Cleanup and re-init the lex mem_root for re-parse.
468+
free_root(&m_lex_mem_root, MYF(0));
469+
init_sql_alloc(PSI_NOT_INSTRUMENTED, &m_lex_mem_root,
470+
MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
471+
472+
/*
473+
Switch mem-roots. We store the new LEX and its Items in the
474+
m_lex_mem_root since it is freed before reparse triggered due to
475+
invalidation. This avoids the memory leak in case re-parse is
476+
initiated. Also set the statement query arena to the lex mem_root.
477+
*/
478+
MEM_ROOT *execution_mem_root= thd->mem_root;
479+
Query_arena parse_arena(&m_lex_mem_root, thd->stmt_arena->state);
480+
481+
thd->mem_root= &m_lex_mem_root;
482+
thd->stmt_arena->set_query_arena(&parse_arena);
483+
464484
// Prepare parser state. It can be done just before parse_sql(), do it here
465485
// only to simplify exit in case of failure (out-of-memory error).
466486

@@ -469,17 +489,6 @@ LEX *sp_lex_instr::parse_expr(THD *thd, sp_head *sp)
469489
if (parser_state.init(thd, sql_query.c_ptr(), sql_query.length()))
470490
return NULL;
471491

472-
// Cleanup current THD from previously held objects before new parsing.
473-
474-
cleanup_before_parsing(thd);
475-
476-
// Switch mem-roots. We need to store new LEX and its Items in the persistent
477-
// SP-memory (memory which is not freed between executions).
478-
479-
MEM_ROOT *execution_mem_root= thd->mem_root;
480-
481-
thd->mem_root= thd->sp_runtime_ctx->sp->get_persistent_mem_root();
482-
483492
// Switch THD::free_list. It's used to remember the newly created set of Items
484493
// during parsing. We should clean those items after each execution.
485494

sql/sp_instr.h

+19-1
Original file line numberDiff line numberDiff line change
@@ -189,10 +189,21 @@ class sp_lex_instr : public sp_instr
189189
m_lex_query_tables_own_last(NULL)
190190
{
191191
set_lex(lex, is_lex_owner);
192+
memset(&m_lex_mem_root, 0, sizeof (MEM_ROOT));
192193
}
193194

194195
virtual ~sp_lex_instr()
195-
{ free_lex(); }
196+
{
197+
free_lex();
198+
/*
199+
If the instruction is reparsed, m_lex_mem_root was used to allocate the
200+
items, then freeing the memroot, frees the items. Hence set the free_list
201+
pointer to NULL.
202+
*/
203+
if (alloc_root_inited(&m_lex_mem_root))
204+
free_list= NULL;
205+
free_root(&m_lex_mem_root, MYF(0));
206+
}
196207

197208
/**
198209
Make a few attempts to execute the instruction.
@@ -354,6 +365,13 @@ class sp_lex_instr : public sp_instr
354365
virtual void cleanup_before_parsing(THD *thd);
355366

356367
private:
368+
/**
369+
Mem-root for storing the LEX-tree during reparse. This
370+
mem-root is freed when a reparse is triggered or the stored
371+
routine is dropped.
372+
*/
373+
MEM_ROOT m_lex_mem_root;
374+
357375
/// LEX-object.
358376
LEX *m_lex;
359377

sql/sp_rcontext.cc

+39-14
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "sql_tmp_table.h" // create_virtual_tmp_table
2424
#include "sp_instr.h"
2525

26+
extern "C" void sql_alloc_error_handler(void);
2627

2728
///////////////////////////////////////////////////////////////////////////
2829
// sp_rcontext implementation.
@@ -51,8 +52,12 @@ sp_rcontext::~sp_rcontext()
5152
while (m_activated_handlers.elements())
5253
delete m_activated_handlers.pop();
5354

54-
// Leave m_visible_handlers, m_var_items, m_cstack
55-
// and m_case_expr_holders untouched.
55+
while (m_visible_handlers.elements())
56+
delete m_visible_handlers.pop();
57+
58+
pop_all_cursors();
59+
60+
// Leave m_var_items and m_case_expr_holders untouched.
5661
// They are allocated in mem roots and will be freed accordingly.
5762
}
5863

@@ -158,13 +163,19 @@ bool sp_rcontext::set_return_value(THD *thd, Item **return_value_item)
158163
bool sp_rcontext::push_cursor(sp_instr_cpush *i)
159164
{
160165
/*
161-
We should create cursors in the callers arena, as
162-
it could be (and usually is) used in several instructions.
166+
We should create cursors on the system heap because:
167+
- they could be (and usually are) used in several instructions,
168+
thus they can not be stored on an execution mem-root;
169+
- a cursor can be pushed/popped many times in a loop, having these objects
170+
on callers' mem-root would lead to a memory leak in every iteration.
163171
*/
164-
sp_cursor *c= new (callers_arena->mem_root) sp_cursor(i);
172+
sp_cursor *c= new (std::nothrow) sp_cursor(i);
165173

166-
if (c == NULL)
174+
if (!c)
175+
{
176+
sql_alloc_error_handler();
167177
return true;
178+
}
168179

169180
m_cstack[m_ccount++]= c;
170181
return false;
@@ -183,14 +194,21 @@ void sp_rcontext::pop_cursors(uint count)
183194
bool sp_rcontext::push_handler(sp_handler *handler, uint first_ip)
184195
{
185196
/*
186-
We should create handler entries in the callers arena, as
187-
they could be (and usually are) used in several instructions.
197+
We should create handler entries on the system heap because:
198+
- they could be (and usually are) used in several instructions,
199+
thus they can not be stored on an execution mem-root;
200+
- a handler can be pushed/popped many times in a loop, having these
201+
objects on callers' mem-root would lead to a memory leak in every
202+
iteration.
188203
*/
189204
sp_handler_entry *he=
190-
new (callers_arena->mem_root) sp_handler_entry(handler, first_ip);
205+
new (std::nothrow) sp_handler_entry(handler, first_ip);
191206

192-
if (he == NULL)
207+
if (!he)
208+
{
209+
sql_alloc_error_handler();
193210
return true;
211+
}
194212

195213
return m_visible_handlers.append(he);
196214
}
@@ -203,7 +221,7 @@ void sp_rcontext::pop_handlers(sp_pcontext *current_scope)
203221
int handler_level= m_visible_handlers.at(i)->handler->scope->get_level();
204222

205223
if (handler_level >= current_scope->get_level())
206-
m_visible_handlers.pop();
224+
delete m_visible_handlers.pop();
207225
}
208226
}
209227

@@ -390,9 +408,16 @@ bool sp_rcontext::handle_sql_condition(THD *thd,
390408
cur_spi->get_cont_dest() : 0;
391409

392410
/* Add a frame to handler-call-stack. */
393-
Handler_call_frame *frame= new Handler_call_frame(found_handler,
394-
found_condition,
395-
continue_ip);
411+
Handler_call_frame *frame=
412+
new (std::nothrow) Handler_call_frame(found_handler,
413+
found_condition,
414+
continue_ip);
415+
if (!frame)
416+
{
417+
sql_alloc_error_handler();
418+
DBUG_RETURN(false);
419+
}
420+
396421
m_activated_handlers.append(frame);
397422

398423
/* End aborted result set. */

sql/sp_rcontext.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ class sp_rcontext : public Sql_alloc
7979
private:
8080
/// This is an auxillary class to store entering instruction pointer for an
8181
/// SQL-handler.
82-
class sp_handler_entry : public Sql_alloc
82+
class sp_handler_entry
8383
{
8484
public:
8585
/// Handler definition (from parsing context).
@@ -386,7 +386,7 @@ typedef class st_select_lex_unit SELECT_LEX_UNIT;
386386

387387
/* A mediator between stored procedures and server side cursors */
388388

389-
class sp_cursor : public Sql_alloc
389+
class sp_cursor
390390
{
391391
private:
392392
/// An interceptor of cursor result set used to implement
@@ -434,6 +434,6 @@ class sp_cursor : public Sql_alloc
434434

435435
private:
436436
void destroy();
437-
}; // class sp_cursor : public Sql_alloc
437+
}; // class sp_cursor
438438

439439
#endif /* _SP_RCONTEXT_H_ */

0 commit comments

Comments
 (0)