summaryrefslogtreecommitdiff
path: root/sql/sql_admin.cc
diff options
context:
space:
mode:
authorJon Olav Hauglid <jon.hauglid@oracle.com>2010-09-22 10:15:41 +0200
committerJon Olav Hauglid <jon.hauglid@oracle.com>2010-09-22 10:15:41 +0200
commit5d06dddff34475c1a2b76be2b16d804e63a2f32b (patch)
treeb82a9808056ecd7a1dfbd25d90cded1321a1b280 /sql/sql_admin.cc
parent1818165cdb4ee0c3b8063348cc0462ac07aadf8a (diff)
downloadmariadb-git-5d06dddff34475c1a2b76be2b16d804e63a2f32b.tar.gz
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.
Diffstat (limited to 'sql/sql_admin.cc')
-rw-r--r--sql/sql_admin.cc46
1 files changed, 39 insertions, 7 deletions
diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc
index 1f96f1cf0e4..21a05a5baca 100644
--- a/sql/sql_admin.cc
+++ b/sql/sql_admin.cc
@@ -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 @@ end:
}
+/**
+ 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,7 +331,13 @@ 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;
- thd->no_warnings_for_error= no_warnings_for_error;
+ /*
+ 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 */