Skip to content

Commit

Permalink
MDEV-17167 - InnoDB: Failing assertion: table->get_ref_count() == 0 upon
Browse files Browse the repository at this point in the history
             truncating a temporary table

TRUNCATE expects only one TABLE instance (which is used by TRUNCATE
itself) to be open. However this requirement wasn't enforced after
"MDEV-5535: Cannot reopen temporary table".

Fixed by closing unused table instances before performing TRUNCATE.
  • Loading branch information
Sergey Vojtovich committed Oct 2, 2018
1 parent b9a5ff3 commit bad2f15
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 1 deletion.
24 changes: 24 additions & 0 deletions mysql-test/main/temp_table.result
Original file line number Diff line number Diff line change
Expand Up @@ -548,3 +548,27 @@ DROP TABLE nonexisting_table, t1;
ERROR 42S02: Unknown table 'temp_db.nonexisting_table'
# Cleanup
DROP DATABASE temp_db;
USE test;
#
# MDEV-17167 - InnoDB: Failing assertion: table->get_ref_count() == 0
# upon truncating a temporary table
#
CREATE TEMPORARY TABLE t1(a INT) ENGINE=InnoDB;
SELECT * FROM t1 AS t1a1, t1 AS t2a2;
a a
TRUNCATE TABLE t1;
LOCK TABLES t1 WRITE;
TRUNCATE TABLE t1;
SELECT * FROM t1;
a
UNLOCK TABLES;
LOCK TABLES t1 AS t1a1 WRITE, t1 AS t1a2 WRITE;
TRUNCATE TABLE t1;
SELECT * FROM t1 AS t1a1, t1 AS t1a2;
a a
UNLOCK TABLES;
CREATE TABLE t2(a INT) ENGINE=InnoDB;
LOCK TABLES t2 WRITE;
TRUNCATE TABLE t1;
UNLOCK TABLES;
DROP TABLE t1, t2;
26 changes: 26 additions & 0 deletions mysql-test/main/temp_table.test
Original file line number Diff line number Diff line change
Expand Up @@ -594,4 +594,30 @@ DROP TABLE nonexisting_table, t1;

--echo # Cleanup
DROP DATABASE temp_db;
USE test;


--echo #
--echo # MDEV-17167 - InnoDB: Failing assertion: table->get_ref_count() == 0
--echo # upon truncating a temporary table
--echo #
CREATE TEMPORARY TABLE t1(a INT) ENGINE=InnoDB;
SELECT * FROM t1 AS t1a1, t1 AS t2a2;
TRUNCATE TABLE t1;

LOCK TABLES t1 WRITE;
TRUNCATE TABLE t1;
SELECT * FROM t1;
UNLOCK TABLES;

LOCK TABLES t1 AS t1a1 WRITE, t1 AS t1a2 WRITE;
TRUNCATE TABLE t1;
SELECT * FROM t1 AS t1a1, t1 AS t1a2;
UNLOCK TABLES;

CREATE TABLE t2(a INT) ENGINE=InnoDB;
LOCK TABLES t2 WRITE;
TRUNCATE TABLE t1;
UNLOCK TABLES;

DROP TABLE t1, t2;
1 change: 1 addition & 0 deletions sql/sql_class.h
Original file line number Diff line number Diff line change
Expand Up @@ -4628,6 +4628,7 @@ class THD :public Statement,

TMP_TABLE_SHARE* save_tmp_table_share(TABLE *table);
void restore_tmp_table_share(TMP_TABLE_SHARE *share);
void close_unused_temporary_table_instances(const TABLE_LIST *tl);

private:
/* Whether a lock has been acquired? */
Expand Down
7 changes: 6 additions & 1 deletion sql/sql_plist.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,12 @@ class I_P_List_iterator
list= &a;
current= a.m_first;
}
/* Operator for it++ */
/**
Operator for it++
@note since we save next element pointer, caller may remove current element.
Such modification doesn't invalidate iterator.
*/
inline T* operator++(int)
{
T *result= current;
Expand Down
2 changes: 2 additions & 0 deletions sql/sql_truncate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,8 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref)
/* In RBR, the statement is not binlogged if the table is temporary. */
binlog_stmt= !thd->is_current_stmt_binlog_format_row();

thd->close_unused_temporary_table_instances(table_ref);

error= handler_truncate(thd, table_ref, TRUE);

/*
Expand Down
30 changes: 30 additions & 0 deletions sql/temporary_tables.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1540,3 +1540,33 @@ void THD::unlock_temporary_tables()
DBUG_VOID_RETURN;
}


/**
Close unused TABLE instances for given temporary table.
@param tl [IN] TABLE_LIST
Initial use case was TRUNCATE, which expects only one instance (which is used
by TRUNCATE itself) to be open. Most probably some ALTER TABLE variants and
REPAIR may have similar expectations.
*/

void THD::close_unused_temporary_table_instances(const TABLE_LIST *tl)
{
TMP_TABLE_SHARE *share= find_tmp_table_share(tl);

if (share)
{
All_share_tables_list::Iterator tables_it(share->all_tmp_tables);

while (TABLE *table= tables_it++)
{
if (table->query_id == 0)
{
/* Note: removing current list element doesn't invalidate iterator. */
share->all_tmp_tables.remove(table);
free_temporary_table(table);
}
}
}
}

0 comments on commit bad2f15

Please sign in to comment.