diff --git a/mysql-test/main/alter_table_online_debug.result b/mysql-test/main/alter_table_online_debug.result index c987ff4d2f6..16e9fd190be 100644 --- a/mysql-test/main/alter_table_online_debug.result +++ b/mysql-test/main/alter_table_online_debug.result @@ -1807,6 +1807,28 @@ a b 456 NULL 789 NULL drop table t1; +# MDEV-32614 LeakSanitizer errors in copy_data_between_tables +create table t (a int, b int) engine=aria; +insert into t select seq, seq from seq_1_to_5; +backup stage start; +connect con_lock,localhost,root,,; +set lock_wait_timeout= 1; +set debug_sync='copy_data_between_tables_before_reset_backup_lock wait_for continue'; +alter table t add index (b), algorithm=copy, lock=none; +connection default; +backup stage block_commit; +set debug_sync='now signal continue'; +connection con_lock; +ERROR HY000: Lock wait timeout exceeded; try restarting transaction +set debug_sync='copy_data_between_tables_before_reset_backup_lock wait_for continue'; +alter table t add index (a), algorithm=copy, lock=none; +connection default; +backup stage end; +set debug_sync='now signal continue'; +connection con_lock; +disconnect con_lock; +connection default; +drop table t; set global default_storage_engine= MyISAM; disconnect con1; disconnect con2; diff --git a/mysql-test/main/alter_table_online_debug.test b/mysql-test/main/alter_table_online_debug.test index 2a37f6a0c46..a45226b277a 100644 --- a/mysql-test/main/alter_table_online_debug.test +++ b/mysql-test/main/alter_table_online_debug.test @@ -2066,6 +2066,41 @@ select * from t1; drop table t1; +--echo # MDEV-32614 LeakSanitizer errors in copy_data_between_tables +create table t (a int, b int) engine=aria; +insert into t select seq, seq from seq_1_to_5; +backup stage start; + +--connect (con_lock,localhost,root,,) +set lock_wait_timeout= 1; + +set debug_sync='copy_data_between_tables_before_reset_backup_lock wait_for continue'; +send alter table t add index (b), algorithm=copy, lock=none; + +--connection default +backup stage block_commit; + +set debug_sync='now signal continue'; + +--connection con_lock +--error ER_LOCK_WAIT_TIMEOUT +--reap +# --echo # error $mysql_errno + +set debug_sync='copy_data_between_tables_before_reset_backup_lock wait_for continue'; +send alter table t add index (a), algorithm=copy, lock=none; + +--connection default +backup stage end; +set debug_sync='now signal continue'; + +--connection con_lock +--reap +--disconnect con_lock +--connection default +drop table t; + + eval set global default_storage_engine= $default_storage_engine; --disconnect con1 diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 20eeeb545bf..2b1228ac82f 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -12130,6 +12130,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, #ifdef HAVE_REPLICATION if (online) { + DBUG_ASSERT(from->s->online_alter_binlog == NULL); from->s->online_alter_binlog= new Cache_flip_event_log(); if (!from->s->online_alter_binlog) goto err; @@ -12363,29 +12364,10 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, thd_progress_next_stage(thd); error= online_alter_read_from_binlog(thd, &rgi, binlog, &found_count); } - if (error) - from->s->tdc->flush_unused(1); // to free the binlog to->pos_in_table_list= NULL; // Safety DBUG_ASSERT(thd->lex->sql_command == saved_sql_command); thd->lex->sql_command= saved_sql_command; // Just in case } - else if (online) // error was on copy stage - { - /* - We can't free the resources properly now, as we can still be in - non-exclusive state. So this s->online_alter_binlog will be used - until all transactions will release it. - Once the transaction commits, it can release online_alter_binlog - by decreasing ref_count. - - online_alter_binlog->ref_count can be reached 0 only once. - Proof: - If share exists, we'll always have ref_count >= 1. - Once it reaches destroy(), nobody can acquire it again, - therefore, only release() is possible at this moment. - */ - from->s->tdc->flush_unused(1); // to free the binlog - } #endif if (error > 0 && !from->s->tmp_table) @@ -12400,6 +12382,26 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, if (unlikely(mysql_trans_commit_alter_copy_data(thd))) error= 1; + + if (unlikely(error) && online) + { + /* + We can't free the resources properly now, as we can still be in + non-exclusive state. So this s->online_alter_binlog will be used + until all transactions will release it. + Once the transaction commits, it can release online_alter_binlog + by decreasing ref_count. + + online_alter_binlog->ref_count can be reached 0 only once. + Proof: + If share exists, we'll always have ref_count >= 1. + Once it reaches destroy(), nobody can acquire it again, + therefore, only release() is possible at this moment. + + Also, this will release the binlog. + */ + from->s->tdc->flush_unused(1); + } err: if (bulk_insert_started)