mirror of
https://github.com/MariaDB/server.git
synced 2025-08-08 11:22:35 +03:00
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.
This commit is contained in:
@@ -4656,7 +4656,8 @@ restart:
|
|||||||
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 @@ restart:
|
|||||||
/* 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)
|
|||||||
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,
|
|||||||
|
|
||||||
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 @@ public:
|
|||||||
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 @@ retry:
|
|||||||
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;
|
||||||
|
Reference in New Issue
Block a user