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,
have failed to open since closing tables can trigger removal of
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
......@@ -4724,10 +4725,10 @@ bool open_tables(THD *thd, const DDL_options_st &options,
/* F.ex. deadlock happened */
if (ot_ctx.can_recover_from_failed_open())
{
DBUG_ASSERT(ot_ctx.get_action() !=
Open_table_context::OT_ADD_HISTORY_PARTITION);
DBUG_ASSERT(ot_ctx.remove_implicitly_used_deps());
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())
goto error;
......@@ -6017,14 +6018,19 @@ bool restart_trans_for_tables(THD *thd, TABLE_LIST *table)
trying to reopen tables. NULL if no metadata locks
were held and thus all metadata locks should be
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,
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 *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.
......@@ -6032,12 +6038,14 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables,
if (first_not_own_table == *tables)
*tables= 0;
thd->lex->chop_off_not_own_tables();
/* Reset MDL tickets for procedures/functions */
for (Sroutine_hash_entry *rt=
(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)
{
tmp->table= 0;
......
......@@ -156,7 +156,8 @@ thr_lock_type read_lock_type_for_table(THD *thd,
my_bool mysql_rm_tmp_tables(void);
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,
LEX_CSTRING *table, thr_lock_type lock_type);
TABLE_LIST *find_table_in_list(TABLE_LIST *table,
......@@ -566,9 +567,21 @@ class Open_table_context
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; }
......
......@@ -2945,7 +2945,7 @@ lock_tables_open_and_lock_tables(THD *thd, TABLE_LIST *tables)
Deadlock occurred during upgrade of metadata lock.
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))
goto err;
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