mirror of
https://github.com/MariaDB/server.git
synced 2025-07-04 01:23:45 +03:00
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.
This commit is contained in:
@ -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;
|
|||||||
|
|
||||||
--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 @@ public:
|
|||||||
|
|
||||||
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 @@ public:
|
|||||||
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)
|
|||||||
/* 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()
|
|||||||
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user