Commit 5d06dddf authored by Jon Olav Hauglid's avatar Jon Olav Hauglid

Bug #56494 Segfault in upgrade_shared_lock_to_exclusive() for

           REPAIR of merge table
Bug #56422 CHECK TABLE run when the table is locked reports
           corruption along with timeout

The crash happened if a table maintenance statement (ANALYZE TABLE,
REPAIR TABLE, etc.) was executed on a MERGE table and opening and 
locking a child table failed. This could for example happen if a child
table did not exist or if a lock timeout happened while waiting for
a conflicting metadata lock to disappear.

Since opening and locking the MERGE table and its children failed,
the tables would be closed and the metadata locks released.
However, TABLE_LIST::table for the MERGE table would still be set,
with its value invalid since the tables had been closed.
This caused the table maintenance statement to try to continue
and upgrade the metadata lock on the MERGE table. But since the lock
already had been released, this caused a segfault.

This patch fixes the problem by setting TABLE_LIST::table to NULL 
if open_and_lock_tables() fails. This prevents maintenance
statements from continuing and trying to upgrade the metadata lock.

The patch includes a 5.5 version of the fix for
Bug #46339 crash on REPAIR TABLE merge table USE_FRM.
This bug caused REPAIR TABLE ... USE_FRM to give an assert 
when used on merge tables.

The patch also enables the CHECK TABLE statement for log tables.
Before, CHECK TABLE for log tables gave ER_CANT_LOCK_LOG_TABLE,
yet still counted the statement as successfully executed.
With the changes to table maintenance statement error handling
in this patch, CHECK TABLE would no longer be considered as
successful in this case. This would have caused upgrade scripts
to mistakenly think that the general and slow logs are corrupted
and have to be repaired. Enabling CHECK TABLES for log tables
prevents this from happening.

Finally, the patch changes the error message from "Corrupt" to
"Operation failed" for a number of issues not related to table
corruption. For example "Lock wait timeout exceeded" and 
"Deadlock found trying to get lock".

Test cases added to merge.test and check.test.
parent 1818165c
......@@ -23,3 +23,19 @@ REPAIR TABLE t1;
Table Op Msg_type Msg_text
test.t1 repair status OK
DROP TABLE t1;
#
# Bug#56422 CHECK TABLE run when the table is locked reports corruption
# along with timeout
#
DROP TABLE IF EXISTS t1;
CREATE TABLE t1(a INT);
LOCK TABLE t1 WRITE;
# Connection con1
SET lock_wait_timeout= 1;
CHECK TABLE t1;
Table Op Msg_type Msg_text
test.t1 check Error Lock wait timeout exceeded; try restarting transaction
test.t1 check status Operation failed
# Connection default
UNLOCK TABLES;
DROP TABLE t1;
......@@ -17,9 +17,7 @@ mysql.columns_priv OK
mysql.db OK
mysql.event OK
mysql.func OK
mysql.general_log
Error : You can't use locks with log tables.
status : OK
mysql.general_log OK
mysql.help_category OK
mysql.help_keyword OK
mysql.help_relation OK
......@@ -31,9 +29,7 @@ mysql.proc OK
mysql.procs_priv OK
mysql.renamed_general_log OK
mysql.servers OK
mysql.slow_log
Error : You can't use locks with log tables.
status : OK
mysql.slow_log OK
mysql.tables_priv OK
mysql.time_zone OK
mysql.time_zone_leap_second OK
......
......@@ -2358,6 +2358,48 @@ t2 WHERE b SOUNDS LIKE e AND d = 1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables
DROP TABLE t2, t1;
#
# Bug#46339 - crash on REPAIR TABLE merge table USE_FRM
#
DROP TABLE IF EXISTS m1, t1;
CREATE TABLE t1 (c1 INT) ENGINE=MYISAM;
CREATE TABLE m1 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1) INSERT_METHOD=LAST;
LOCK TABLE m1 READ;
REPAIR TABLE m1 USE_FRM;
Table Op Msg_type Msg_text
test.m1 repair Error Table 'm1' was locked with a READ lock and can't be updated
test.m1 repair status Operation failed
UNLOCK TABLES;
REPAIR TABLE m1 USE_FRM;
Table Op Msg_type Msg_text
test.m1 repair note The storage engine for the table doesn't support repair
DROP TABLE m1,t1;
CREATE TABLE m1 (f1 BIGINT) ENGINE=MRG_MyISAM UNION(t1);
REPAIR TABLE m1 USE_FRM;
Table Op Msg_type Msg_text
test.m1 repair Warning Can't open table
test.m1 repair error Corrupt
CREATE TABLE t1 (f1 BIGINT) ENGINE = MyISAM;
REPAIR TABLE m1 USE_FRM;
Table Op Msg_type Msg_text
test.m1 repair note The storage engine for the table doesn't support repair
REPAIR TABLE m1;
Table Op Msg_type Msg_text
test.m1 repair note The storage engine for the table doesn't support repair
DROP TABLE m1, t1;
CREATE TEMPORARY TABLE m1 (f1 BIGINT) ENGINE=MRG_MyISAM UNION(t1);
REPAIR TABLE m1 USE_FRM;
Table Op Msg_type Msg_text
test.m1 repair Error Table 'test.m1' doesn't exist
test.m1 repair error Corrupt
CREATE TEMPORARY TABLE t1 (f1 BIGINT) ENGINE=MyISAM;
REPAIR TABLE m1 USE_FRM;
Table Op Msg_type Msg_text
m1 repair error Cannot repair temporary table from .frm file
REPAIR TABLE m1;
Table Op Msg_type Msg_text
test.m1 repair note The storage engine for the table doesn't support repair
DROP TABLE m1, t1;
End of 5.1 tests
#
# An additional test case for Bug#27430 Crash in subquery code
......@@ -2677,7 +2719,7 @@ OPTIMIZE TABLE t1;
Table Op Msg_type Msg_text
test.t1 optimize Error Table 'test.t_not_exists' doesn't exist
test.t1 optimize Error Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist
test.t1 optimize note The storage engine for the table doesn't support optimize
test.t1 optimize error Corrupt
DROP TABLE t1;
#
# Bug#36171 - CREATE TEMPORARY TABLE and MERGE engine
......@@ -3575,4 +3617,48 @@ ERROR HY000: The definition of table 'v1' prevents operation DELETE on table 'm1
drop view v1;
drop temporary table tmp;
drop table t1, t2, t3, m1, m2;
#
# Bug#56494 Segfault in upgrade_shared_lock_to_exclusive() for
# REPAIR of merge table
#
DROP TABLE IF EXISTS t1, t2, t_not_exists;
CREATE TABLE t1(a INT);
ALTER TABLE t1 engine= MERGE UNION (t_not_exists);
ANALYZE TABLE t1;
Table Op Msg_type Msg_text
test.t1 analyze Error Table 'test.t_not_exists' doesn't exist
test.t1 analyze Error Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist
test.t1 analyze error Corrupt
CHECK TABLE t1;
Table Op Msg_type Msg_text
test.t1 check Error Table 'test.t_not_exists' doesn't exist
test.t1 check Error Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist
test.t1 check error Corrupt
CHECKSUM TABLE t1;
Table Checksum
test.t1 NULL
Warnings:
Error 1146 Table 'test.t_not_exists' doesn't exist
Error 1168 Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist
OPTIMIZE TABLE t1;
Table Op Msg_type Msg_text
test.t1 optimize Error Table 'test.t_not_exists' doesn't exist
test.t1 optimize Error Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist
test.t1 optimize error Corrupt
REPAIR TABLE t1;
Table Op Msg_type Msg_text
test.t1 repair Error Table 'test.t_not_exists' doesn't exist
test.t1 repair Error Unable to open underlying table which is differently defined or of non-MyISAM type or doesn't exist
test.t1 repair error Corrupt
REPAIR TABLE t1 USE_FRM;
Table Op Msg_type Msg_text
test.t1 repair Warning Can't open table
test.t1 repair error Corrupt
DROP TABLE t1;
CREATE TABLE t1(a INT);
CREATE TABLE t2(a INT) engine= MERGE UNION (t1);
REPAIR TABLE t2 USE_FRM;
Table Op Msg_type Msg_text
test.t2 repair note The storage engine for the table doesn't support repair
DROP TABLE t1, t2;
End of 6.0 tests
......@@ -46,14 +46,12 @@ insert into t1 select * from t1;
flush tables;
optimize table t1;
Table Op Msg_type Msg_text
test.t1 optimize error Table 'test.t1' is read only
Warnings:
Error 1036 Table 't1' is read only
test.t1 optimize Error Table 't1' is read only
test.t1 optimize status Operation failed
repair table t1;
Table Op Msg_type Msg_text
test.t1 repair error Table 'test.t1' is read only
Warnings:
Error 1036 Table 't1' is read only
test.t1 repair Error Table 't1' is read only
test.t1 repair status Operation failed
drop table t1;
#
# BUG#41541 - Valgrind warnings on packed MyISAM table
......
......@@ -5,9 +5,7 @@ mysql.columns_priv OK
mysql.db OK
mysql.event OK
mysql.func OK
mysql.general_log
Error : You can't use locks with log tables.
status : OK
mysql.general_log OK
mysql.help_category OK
mysql.help_keyword OK
mysql.help_relation OK
......@@ -18,9 +16,7 @@ mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
mysql.servers OK
mysql.slow_log
Error : You can't use locks with log tables.
status : OK
mysql.slow_log OK
mysql.tables_priv OK
mysql.time_zone OK
mysql.time_zone_leap_second OK
......@@ -37,9 +33,7 @@ mysql.columns_priv OK
mysql.db OK
mysql.event OK
mysql.func OK
mysql.general_log
Error : You can't use locks with log tables.
status : OK
mysql.general_log OK
mysql.help_category OK
mysql.help_keyword OK
mysql.help_relation OK
......@@ -50,9 +44,7 @@ mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
mysql.servers OK
mysql.slow_log
Error : You can't use locks with log tables.
status : OK
mysql.slow_log OK
mysql.tables_priv OK
mysql.time_zone OK
mysql.time_zone_leap_second OK
......@@ -69,9 +61,7 @@ mysql.columns_priv OK
mysql.db OK
mysql.event OK
mysql.func OK
mysql.general_log
Error : You can't use locks with log tables.
status : OK
mysql.general_log OK
mysql.help_category OK
mysql.help_keyword OK
mysql.help_relation OK
......@@ -82,9 +72,7 @@ mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
mysql.servers OK
mysql.slow_log
Error : You can't use locks with log tables.
status : OK
mysql.slow_log OK
mysql.tables_priv OK
mysql.time_zone OK
mysql.time_zone_leap_second OK
......@@ -103,9 +91,7 @@ mysql.columns_priv OK
mysql.db OK
mysql.event OK
mysql.func OK
mysql.general_log
Error : You can't use locks with log tables.
status : OK
mysql.general_log OK
mysql.help_category OK
mysql.help_keyword OK
mysql.help_relation OK
......@@ -116,9 +102,7 @@ mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
mysql.servers OK
mysql.slow_log
Error : You can't use locks with log tables.
status : OK
mysql.slow_log OK
mysql.tables_priv OK
mysql.time_zone OK
mysql.time_zone_leap_second OK
......@@ -141,9 +125,7 @@ mysql.columns_priv OK
mysql.db OK
mysql.event OK
mysql.func OK
mysql.general_log
Error : You can't use locks with log tables.
status : OK
mysql.general_log OK
mysql.help_category OK
mysql.help_keyword OK
mysql.help_relation OK
......@@ -154,9 +136,7 @@ mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
mysql.servers OK
mysql.slow_log
Error : You can't use locks with log tables.
status : OK
mysql.slow_log OK
mysql.tables_priv OK
mysql.time_zone OK
mysql.time_zone_leap_second OK
......@@ -182,9 +162,7 @@ mysql.columns_priv OK
mysql.db OK
mysql.event OK
mysql.func OK
mysql.general_log
Error : You can't use locks with log tables.
status : OK
mysql.general_log OK
mysql.help_category OK
mysql.help_keyword OK
mysql.help_relation OK
......@@ -195,9 +173,7 @@ mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
mysql.servers OK
mysql.slow_log
Error : You can't use locks with log tables.
status : OK
mysql.slow_log OK
mysql.tables_priv OK
mysql.time_zone OK
mysql.time_zone_leap_second OK
......
......@@ -7,9 +7,7 @@ mysql.columns_priv OK
mysql.db OK
mysql.event OK
mysql.func OK
mysql.general_log
Error : You can't use locks with log tables.
status : OK
mysql.general_log OK
mysql.help_category OK
mysql.help_keyword OK
mysql.help_relation OK
......@@ -20,9 +18,7 @@ mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
mysql.servers OK
mysql.slow_log
Error : You can't use locks with log tables.
status : OK
mysql.slow_log OK
mysql.tables_priv OK
mysql.time_zone OK
mysql.time_zone_leap_second OK
......
......@@ -53,5 +53,29 @@ REPAIR TABLE t1;
DROP TABLE t1;
--echo #
--echo # Bug#56422 CHECK TABLE run when the table is locked reports corruption
--echo # along with timeout
--echo #
--disable_warnings
DROP TABLE IF EXISTS t1;
--enable_warnings
CREATE TABLE t1(a INT);
LOCK TABLE t1 WRITE;
--echo # Connection con1
connect(con1, localhost, root);
SET lock_wait_timeout= 1;
CHECK TABLE t1;
--echo # Connection default
connection default;
UNLOCK TABLES;
DROP TABLE t1;
disconnect con1;
# Wait till we reached the initial number of concurrent sessions
--source include/wait_until_count_sessions.inc
......@@ -1734,6 +1734,84 @@ t2 WHERE b SOUNDS LIKE e AND d = 1;
DROP TABLE t2, t1;
--echo #
--echo # Bug#46339 - crash on REPAIR TABLE merge table USE_FRM
--echo #
--disable_warnings
DROP TABLE IF EXISTS m1, t1;
--enable_warnings
#
# Test derived from a proposal of Shane Bester.
#
CREATE TABLE t1 (c1 INT) ENGINE=MYISAM;
CREATE TABLE m1 (c1 INT) ENGINE=MRG_MyISAM UNION=(t1) INSERT_METHOD=LAST;
#
# REPAIR ... USE_FRM with LOCK TABLES.
#
LOCK TABLE m1 READ;
REPAIR TABLE m1 USE_FRM;
UNLOCK TABLES;
#
# REPAIR ... USE_FRM without LOCK TABLES.
#
# This statement crashed the server (Bug#46339).
#
REPAIR TABLE m1 USE_FRM;
#
DROP TABLE m1,t1;
#
# Test derived from a proposal of Matthias Leich.
#
# Base table is missing.
#
CREATE TABLE m1 (f1 BIGINT) ENGINE=MRG_MyISAM UNION(t1);
#
# This statement crashed the server (Bug#46339).
#
REPAIR TABLE m1 USE_FRM;
#
# Create base table.
#
CREATE TABLE t1 (f1 BIGINT) ENGINE = MyISAM;
#
# This statement crashed the server (Bug#46339).
#
REPAIR TABLE m1 USE_FRM;
#
# Normal repair as reference.
#
REPAIR TABLE m1;
#
# Cleanup.
#
DROP TABLE m1, t1;
#
# Same with temporary tables.
#
# Base table is missing.
#
CREATE TEMPORARY TABLE m1 (f1 BIGINT) ENGINE=MRG_MyISAM UNION(t1);
#
# This statement crashed the server (Bug#46339).
#
REPAIR TABLE m1 USE_FRM;
#
# Create base table.
#
CREATE TEMPORARY TABLE t1 (f1 BIGINT) ENGINE=MyISAM;
#
# This statement crashed the server (Bug#46339).
#
REPAIR TABLE m1 USE_FRM;
#
# Normal repair as reference.
#
REPAIR TABLE m1;
#
# Cleanup.
#
DROP TABLE m1, t1;
--echo End of 5.1 tests
--echo #
......@@ -2668,6 +2746,36 @@ drop temporary table tmp;
drop table t1, t2, t3, m1, m2;
--echo #
--echo # Bug#56494 Segfault in upgrade_shared_lock_to_exclusive() for
--echo # REPAIR of merge table
--echo #
--disable_warnings
DROP TABLE IF EXISTS t1, t2, t_not_exists;
--enable_warnings
CREATE TABLE t1(a INT);
ALTER TABLE t1 engine= MERGE UNION (t_not_exists);
# This caused the segfault
ANALYZE TABLE t1;
CHECK TABLE t1;
CHECKSUM TABLE t1;
OPTIMIZE TABLE t1;
REPAIR TABLE t1;
# This caused an assert
REPAIR TABLE t1 USE_FRM;
DROP TABLE t1;
CREATE TABLE t1(a INT);
CREATE TABLE t2(a INT) engine= MERGE UNION (t1);
# This caused an assert
REPAIR TABLE t2 USE_FRM;
DROP TABLE t1, t2;
--echo End of 6.0 tests
--disable_result_log
......
......@@ -111,9 +111,6 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
table= &tmp_table;
}
/* A MERGE table must not come here. */
DBUG_ASSERT(table->file->ht->db_type != DB_TYPE_MRG_MYISAM);
/*
REPAIR TABLE ... USE_FRM for temporary tables makes little sense.
*/
......@@ -151,6 +148,9 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
if (!ext[0] || !ext[1])
goto end; // No data file
/* A MERGE table must not come here. */
DBUG_ASSERT(table->file->ht->db_type != DB_TYPE_MRG_MYISAM);
// Name of data file
strxmov(from, table->s->normalized_path.str, ext[1], NullS);
if (!mysql_file_stat(key_file_misc, from, &stat_info, MYF(0)))
......@@ -231,6 +231,26 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
}
/**
Check if a given error is something that could occur during
open_and_lock_tables() that does not indicate table corruption.
@param sql_errno Error number to check.
@retval TRUE Error does not indicate table corruption.
@retval FALSE Error could indicate table corruption.
*/
static inline bool table_not_corrupt_error(uint sql_errno)
{
return (sql_errno == ER_NO_SUCH_TABLE ||
sql_errno == ER_FILE_NOT_FOUND ||
sql_errno == ER_LOCK_WAIT_TIMEOUT ||
sql_errno == ER_LOCK_DEADLOCK ||
sql_errno == ER_CANT_LOCK_LOG_TABLE ||
sql_errno == ER_OPEN_AS_READONLY);
}
/*
RETURN VALUES
......@@ -311,6 +331,12 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
lex->query_tables= table;
lex->query_tables_last= &table->next_global;
lex->query_tables_own_last= 0;
/*
Under locked tables, we know that the table can be opened,
so any errors opening the table are logical errors.
In these cases it makes sense to report them.
*/
if (!thd->locked_tables_mode)
thd->no_warnings_for_error= no_warnings_for_error;
if (view_operator_func == NULL)
table->required_type=FRMTYPE_TABLE;
......@@ -320,6 +346,14 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
table->next_global= save_next_global;
table->next_local= save_next_local;
thd->open_options&= ~extra_open_options;
/*
If open_and_lock_tables() failed, close_thread_tables() will close
the table and table->table can therefore be invalid.
*/
if (open_error)
table->table= NULL;
/*
Under locked tables, we know that the table can be opened,
so any errors opening the table are logical errors.
......@@ -418,9 +452,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_VIEW_CHECKSUM, ER(ER_VIEW_CHECKSUM));
if (thd->stmt_da->is_error() &&
(thd->stmt_da->sql_errno() == ER_NO_SUCH_TABLE ||
thd->stmt_da->sql_errno() == ER_FILE_NOT_FOUND))
/* A missing table is just issued as a failed command */
table_not_corrupt_error(thd->stmt_da->sql_errno()))
result_code= HA_ADMIN_FAILED;
else
/* Default failure code is corrupt table */
......
......@@ -401,6 +401,7 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_REPAIR]= CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_OPTIMIZE]|= CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_ANALYZE]= CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_CHECK]= CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_CREATE_USER]|= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_DROP_USER]|= CF_AUTO_COMMIT_TRANS;
......@@ -414,7 +415,6 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_FLUSH]= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_RESET]= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_CHECK]= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_CREATE_SERVER]= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_ALTER_SERVER]= CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_DROP_SERVER]= CF_AUTO_COMMIT_TRANS;
......
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