Commit bad2f156 authored by Sergey Vojtovich's avatar Sergey Vojtovich

MDEV-17167 - InnoDB: Failing assertion: table->get_ref_count() == 0 upon

             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.
parent b9a5ff36
...@@ -548,3 +548,27 @@ DROP TABLE nonexisting_table, t1; ...@@ -548,3 +548,27 @@ DROP TABLE nonexisting_table, t1;
ERROR 42S02: Unknown table 'temp_db.nonexisting_table' ERROR 42S02: Unknown table 'temp_db.nonexisting_table'
# Cleanup # Cleanup
DROP DATABASE temp_db; 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;
...@@ -594,4 +594,30 @@ DROP TABLE nonexisting_table, t1; ...@@ -594,4 +594,30 @@ DROP TABLE nonexisting_table, t1;
--echo # Cleanup --echo # Cleanup
DROP DATABASE temp_db; 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;
...@@ -4628,6 +4628,7 @@ class THD :public Statement, ...@@ -4628,6 +4628,7 @@ class THD :public Statement,
TMP_TABLE_SHARE* save_tmp_table_share(TABLE *table); TMP_TABLE_SHARE* save_tmp_table_share(TABLE *table);
void restore_tmp_table_share(TMP_TABLE_SHARE *share); void restore_tmp_table_share(TMP_TABLE_SHARE *share);
void close_unused_temporary_table_instances(const TABLE_LIST *tl);
private: private:
/* Whether a lock has been acquired? */ /* Whether a lock has been acquired? */
......
...@@ -184,7 +184,12 @@ class I_P_List_iterator ...@@ -184,7 +184,12 @@ class I_P_List_iterator
list= &a; list= &a;
current= a.m_first; 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) inline T* operator++(int)
{ {
T *result= current; T *result= current;
......
...@@ -401,6 +401,8 @@ bool Sql_cmd_truncate_table::truncate_table(THD *thd, TABLE_LIST *table_ref) ...@@ -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. */ /* In RBR, the statement is not binlogged if the table is temporary. */
binlog_stmt= !thd->is_current_stmt_binlog_format_row(); binlog_stmt= !thd->is_current_stmt_binlog_format_row();
thd->close_unused_temporary_table_instances(table_ref);
error= handler_truncate(thd, table_ref, TRUE); error= handler_truncate(thd, table_ref, TRUE);
/* /*
......
...@@ -1540,3 +1540,33 @@ void THD::unlock_temporary_tables() ...@@ -1540,3 +1540,33 @@ void THD::unlock_temporary_tables()
DBUG_VOID_RETURN; 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);
}
}
}
}
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