1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-08 11:22:35 +03:00

MDEV-34517: Memory leak on re-compilation of a failing statement inside a stored routine

SP instructions, consisting a body of a stored routine, had the same memory
root as an instance of the class sp_head, representing abstraction for stored
routine itself. It resulted in memory leaks on re-parsing a failed statement
of a stored routine in case the statement re-compilation has to be performed
by the reason of changes in metadata of tables, triggers, etc. the stored
routine depends on.

To fix this kind of memory leaks, every SP instruction requiring access to
a LEX object must do re-parsing of a failed statement on its own memory root.
These memory roots are allocated on sp_head's memory root and every instance of
the sp_lex_instr class has a pointer to allocated memory root in case re-parsing
of the correspondiong SP instruction was requested. On every subsequent
re-parsing of the failed statement, a memory allocated on SP instruction's
memory root is released and the memory root re-initialized. Following memory
allocations taken place on re-parsing the SP instruction's statement
is performed on the dedicated memory root. So, no memory leaks will happen on
SP statement re-parsing.
This commit is contained in:
Dmitry Shulga
2024-07-24 16:01:38 +07:00
parent 03807c8449
commit ba5a0ff4f8
4 changed files with 183 additions and 1 deletions

View File

@@ -653,6 +653,84 @@ bool sp_lex_instr::setup_table_fields_for_trigger(
return result;
}
/**
Initialize a new memory root for re-parsing a failed SP instruction's
statement or free a memory allocated on re-parsing of the failed statement
and re-initialize it again so to avoid memory leaks on repeating a statement
re-parsing.
@param sphead The stored program.
@return false on success, true on error (OOM)
*/
bool sp_lex_instr::setup_memroot_for_reparsing(sp_head *sphead)
{
if (!m_mem_root_for_reparsing)
{
DBUG_EXECUTE_IF("sp_instr_reparsing_2nd_time", DBUG_ASSERT(0););
/*
Allocate a memory for SP-instruction's mem_root on a mem_root of sp_head.
Since the method sp_lex_instr::setup_memroot_for_reparsing() is called
on failing execution of SP-instruction by the reason of changes in data
dictionary objects metadata, the sp_head mem_root protection flag could
has been already set on first execution of the stored routine. Therefore,
clear the flag
ROOT_FLAG_READ_ONLY
in case it is set before allocating a memory for SP instruction's
mem_root on sp_head's mem_root and restore its original value once
the memory for the SP-instruction's new_root allocated. Read only
property for the stored routine's mem_root can be not set after first
invocation of a stored routine in case it was completed with error.
So, check the flag is set before resetting its value and restoring its
original value on return.
*/
MEM_ROOT *sphead_mem_root= sphead->get_main_mem_root();
#ifdef PROTECT_STATEMENT_MEMROOT
const bool read_only_mem_root=
(sphead_mem_root->flags & ROOT_FLAG_READ_ONLY);
if (read_only_mem_root)
sphead_mem_root->flags&= ~ROOT_FLAG_READ_ONLY;
#endif
m_mem_root_for_reparsing=
(MEM_ROOT*)alloc_root(sphead_mem_root, sizeof(MEM_ROOT));
#ifdef PROTECT_STATEMENT_MEMROOT
if (read_only_mem_root)
/*
Restore original read only property of sp_head' s mem_root
in case it was set
*/
sphead_mem_root->flags|= ROOT_FLAG_READ_ONLY;
#endif
if (!m_mem_root_for_reparsing)
return true;
}
else
{
DBUG_EXECUTE_IF("sp_instr_reparsing_1st_time", DBUG_ASSERT(0););
/*
Free a memory allocated on SP-instruction's mem_root to avoid
memory leaks could take place on recompilation of SP-instruction's
statement.
*/
free_root(m_mem_root_for_reparsing, MYF(0));
}
init_sql_alloc(key_memory_sp_head_main_root, m_mem_root_for_reparsing,
MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC, MYF(0));
mem_root= m_mem_root_for_reparsing;
return false;
}
LEX* sp_lex_instr::parse_expr(THD *thd, sp_head *sp, LEX *sp_instr_lex)
{
String sql_query;
@@ -704,6 +782,18 @@ LEX* sp_lex_instr::parse_expr(THD *thd, sp_head *sp, LEX *sp_instr_lex)
SP arena's state to STMT_INITIALIZED_FOR_SP as its initial state.
*/
state= STMT_INITIALIZED_FOR_SP;
/*
First, set up a men_root for the statement is going to re-compile.
*/
if (setup_memroot_for_reparsing(sp))
return nullptr;
/*
and then set it as the current mem_root. Any memory allocations can take
place on re-parsing the SP-instruction's statement will be performed on
this mem_root.
*/
thd->set_n_backup_active_arena(this, &backup);
thd->free_list= nullptr;