Commit 77c465d5 authored by Dmitry Shulga's avatar Dmitry Shulga

MDEV-34171: Memory leakage is detected on running the test versioning.partition

One of possible use cases that reproduces the memory leakage listed below:

  set timestamp= unix_timestamp('2000-01-01 00:00:00');
  create or replace table t1 (x int) with system versioning
    partition by system_time interval 1 hour auto
    partitions 3;

  create table t2 (x int);

  create trigger tr after insert on t2 for each row update t1 set x= 11;
  create or replace procedure sp2() insert into t2 values (5);

  set timestamp= unix_timestamp('2000-01-01 04:00:00');
  call sp2;

  set timestamp= unix_timestamp('2000-01-01 13:00:00');
  call sp2; # <<=== Memory leak happens there. In case MariaDB server is built
                    with the option -DWITH_PROTECT_STATEMENT_MEMROOT,
                    the second execution would hit assert failure.

The reason of leaking a memory is that once a new partition be created
the table should be closed and re-opened. It results in calling the function
extend_table_list() that indirectly invokes the function sp_add_used_routine()
to add routines implicitly used by the statement that makes a new memory
allocation.

To fix it, don't remove routines and tables the statement implicitly depends
on when a table being closed for subsequent re-opening.
parent 34813c1a
...@@ -4656,7 +4656,8 @@ bool open_tables(THD *thd, const DDL_options_st &options, ...@@ -4656,7 +4656,8 @@ bool open_tables(THD *thd, const DDL_options_st &options,
have failed to open since closing tables can trigger removal of have failed to open since closing tables can trigger removal of
elements from the table list (if MERGE tables are involved), elements from the table list (if MERGE tables are involved),
*/ */
close_tables_for_reopen(thd, start, ot_ctx.start_of_statement_svp()); close_tables_for_reopen(thd, start, ot_ctx.start_of_statement_svp(),
ot_ctx.remove_implicitly_used_deps());
/* /*
Here we rely on the fact that 'tables' still points to the valid Here we rely on the fact that 'tables' still points to the valid
...@@ -4724,10 +4725,10 @@ bool open_tables(THD *thd, const DDL_options_st &options, ...@@ -4724,10 +4725,10 @@ bool open_tables(THD *thd, const DDL_options_st &options,
/* F.ex. deadlock happened */ /* F.ex. deadlock happened */
if (ot_ctx.can_recover_from_failed_open()) if (ot_ctx.can_recover_from_failed_open())
{ {
DBUG_ASSERT(ot_ctx.get_action() != DBUG_ASSERT(ot_ctx.remove_implicitly_used_deps());
Open_table_context::OT_ADD_HISTORY_PARTITION);
close_tables_for_reopen(thd, start, close_tables_for_reopen(thd, start,
ot_ctx.start_of_statement_svp()); ot_ctx.start_of_statement_svp(),
ot_ctx.remove_implicitly_used_deps());
if (ot_ctx.recover_from_failed_open()) if (ot_ctx.recover_from_failed_open())
goto error; goto error;
...@@ -6017,27 +6018,34 @@ bool restart_trans_for_tables(THD *thd, TABLE_LIST *table) ...@@ -6017,27 +6018,34 @@ bool restart_trans_for_tables(THD *thd, TABLE_LIST *table)
trying to reopen tables. NULL if no metadata locks trying to reopen tables. NULL if no metadata locks
were held and thus all metadata locks should be were held and thus all metadata locks should be
released. released.
@param[in] remove_implicit_deps True in case routines and tables implicitly
used by a statement should be removed.
*/ */
void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, void close_tables_for_reopen(THD *thd, TABLE_LIST **tables,
const MDL_savepoint &start_of_statement_svp) const MDL_savepoint &start_of_statement_svp,
bool remove_implicit_deps)
{ {
TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table(); TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table();
TABLE_LIST *tmp; TABLE_LIST *tmp;
/* if (remove_implicit_deps)
If table list consists only from tables from prelocking set, table list {
for new attempt should be empty, so we have to update list's root pointer. /*
*/ If table list consists only from tables from prelocking set, table list
if (first_not_own_table == *tables) for new attempt should be empty, so we have to update list's root pointer.
*tables= 0; */
thd->lex->chop_off_not_own_tables(); if (first_not_own_table == *tables)
/* Reset MDL tickets for procedures/functions */ *tables= 0;
for (Sroutine_hash_entry *rt= thd->lex->chop_off_not_own_tables();
(Sroutine_hash_entry*)thd->lex->sroutines_list.first;
rt; rt= rt->next) /* Reset MDL tickets for procedures/functions */
rt->mdl_request.ticket= NULL; for (Sroutine_hash_entry *rt=
sp_remove_not_own_routines(thd->lex); (Sroutine_hash_entry*)thd->lex->sroutines_list.first;
rt; rt= rt->next)
rt->mdl_request.ticket= NULL;
sp_remove_not_own_routines(thd->lex);
}
for (tmp= *tables; tmp; tmp= tmp->next_global) for (tmp= *tables; tmp; tmp= tmp->next_global)
{ {
tmp->table= 0; tmp->table= 0;
......
...@@ -156,7 +156,8 @@ thr_lock_type read_lock_type_for_table(THD *thd, ...@@ -156,7 +156,8 @@ thr_lock_type read_lock_type_for_table(THD *thd,
my_bool mysql_rm_tmp_tables(void); my_bool mysql_rm_tmp_tables(void);
void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, void close_tables_for_reopen(THD *thd, TABLE_LIST **tables,
const MDL_savepoint &start_of_statement_svp); const MDL_savepoint &start_of_statement_svp,
bool remove_implicit_dependencies);
bool table_already_fk_prelocked(TABLE_LIST *tl, LEX_CSTRING *db, bool table_already_fk_prelocked(TABLE_LIST *tl, LEX_CSTRING *db,
LEX_CSTRING *table, thr_lock_type lock_type); LEX_CSTRING *table, thr_lock_type lock_type);
TABLE_LIST *find_table_in_list(TABLE_LIST *table, TABLE_LIST *find_table_in_list(TABLE_LIST *table,
...@@ -566,9 +567,21 @@ class Open_table_context ...@@ -566,9 +567,21 @@ class Open_table_context
return m_timeout; return m_timeout;
} }
enum_open_table_action get_action() const /**
Return true in case tables and routines the statement implicilty
dependent on should be removed, else return false.
@note The use case when routines and tables the statement implicitly
dependent on shouldn't be removed is the one when a new partition be
created on handling the INSERT statement against a versioning partitioned
table. For this case re-opening a versioning table would result in adding
implicitly dependent routines (e.g. table's triggers) that lead to
allocation of memory on PS mem_root and so leaking a memory until the PS
statement be deallocated.
*/
bool remove_implicitly_used_deps() const
{ {
return m_action; return m_action != OT_ADD_HISTORY_PARTITION;
} }
uint get_flags() const { return m_flags; } uint get_flags() const { return m_flags; }
......
...@@ -2945,7 +2945,7 @@ lock_tables_open_and_lock_tables(THD *thd, TABLE_LIST *tables) ...@@ -2945,7 +2945,7 @@ lock_tables_open_and_lock_tables(THD *thd, TABLE_LIST *tables)
Deadlock occurred during upgrade of metadata lock. Deadlock occurred during upgrade of metadata lock.
Let us restart acquring and opening tables for LOCK TABLES. Let us restart acquring and opening tables for LOCK TABLES.
*/ */
close_tables_for_reopen(thd, &tables, mdl_savepoint); close_tables_for_reopen(thd, &tables, mdl_savepoint, true);
if (thd->open_temporary_tables(tables)) if (thd->open_temporary_tables(tables))
goto err; goto err;
goto retry; goto retry;
......
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