summaryrefslogtreecommitdiff
path: root/sql/sql_handler.cc
diff options
context:
space:
mode:
authorunknown <ingo@mysql.com>2005-11-15 21:57:02 +0100
committerunknown <ingo@mysql.com>2005-11-15 21:57:02 +0100
commit013b3d8ab33b052d504a2e3fa63a85c919c649cc (patch)
tree119cf106bff33787b0aad32bfaf568b654dd7115 /sql/sql_handler.cc
parentd9c7aaf23ff30675682775864c269bde84a3bc03 (diff)
downloadmariadb-git-013b3d8ab33b052d504a2e3fa63a85c919c649cc.tar.gz
Bug#14397 - OPTIMIZE TABLE with an open HANDLER causes a crash
Version for 5.0. It fixes three problems: 1. The cause of the bug was that we did not check the table version for the HANDLER ... READ commands. We did not notice when a table was replaced by a new one. This can happen during ALTER TABLE, REPAIR TABLE, and OPTIMIZE TABLE (there might be more cases). I call the fix for this problem "the primary bug fix". 2. mysql_ha_flush() was not always called with a locked LOCK_open. Though the function comment clearly said it must. I changed the code so that the locking is done when required. I call the fix for this problem "the secondary fix". 3. In 5.0 (not in 4.1 or 4.0) DROP TABLE had a possible deadlock flaw in concur with FLUSH TABLES WITH READ LOCK. I call the fix for this problem "the 5.0 addendum fix". include/my_pthread.h: Bug#14397 - OPTIMIZE TABLE with an open HANDLER causes a crash Added a new macro for the 5.0 addendum fix. mysql-test/r/handler.result: Bug#14397 - OPTIMIZE TABLE with an open HANDLER causes a crash The test result. mysql-test/t/handler.test: Bug#14397 - OPTIMIZE TABLE with an open HANDLER causes a crash The test case. sql/lock.cc: Bug#14397 - OPTIMIZE TABLE with an open HANDLER causes a crash Changed a comment which did confuse me and which is not fully correct anymore after the 5.0 addendum fix. Added an assertion which would fire without the 5.0 addendum fix. sql/mysql_priv.h: Bug#14397 - OPTIMIZE TABLE with an open HANDLER causes a crash Changed a definition for the secondary fix. sql/sql_base.cc: Bug#14397 - OPTIMIZE TABLE with an open HANDLER causes a crash Changed function calls for the secondary fix. sql/sql_class.cc: Bug#14397 - OPTIMIZE TABLE with an open HANDLER causes a crash Changed a function call for the secondary fix. sql/sql_handler.cc: Bug#14397 - OPTIMIZE TABLE with an open HANDLER causes a crash The first two diffs make the primary bug fix. The rest is for the secondary fix. sql/sql_table.cc: Bug#14397 - OPTIMIZE TABLE with an open HANDLER causes a crash The first diff (four changed places) make the 5.0 addendum fix. The other three are changed function calls for the secondary fix.
Diffstat (limited to 'sql/sql_handler.cc')
-rw-r--r--sql/sql_handler.cc50
1 files changed, 46 insertions, 4 deletions
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index cc45a7001cd..07f4de26707 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -336,6 +336,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
ha_rows select_limit_cnt, ha_rows offset_limit_cnt)
{
TABLE_LIST *hash_tables;
+ TABLE **table_ptr;
TABLE *table;
MYSQL_LOCK *lock;
List<Item> list;
@@ -368,6 +369,28 @@ bool mysql_ha_read(THD *thd, TABLE_LIST *tables,
DBUG_PRINT("info-in-hash",("'%s'.'%s' as '%s' tab %p",
hash_tables->db, hash_tables->table_name,
hash_tables->alias, table));
+ /* Table might have been flushed. */
+ if (table && (table->s->version != refresh_version))
+ {
+ /*
+ We must follow the thd->handler_tables chain, as we need the
+ address of the 'next' pointer referencing this table
+ for close_thread_table().
+ */
+ for (table_ptr= &(thd->handler_tables);
+ *table_ptr && (*table_ptr != table);
+ table_ptr= &(*table_ptr)->next)
+ {}
+ (*table_ptr)->file->ha_index_or_rnd_end();
+ VOID(pthread_mutex_lock(&LOCK_open));
+ if (close_thread_table(thd, table_ptr))
+ {
+ /* Tell threads waiting for refresh that something has happened */
+ VOID(pthread_cond_broadcast(&COND_refresh));
+ }
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ table= hash_tables->table= NULL;
+ }
if (!table)
{
/*
@@ -594,6 +617,7 @@ err0:
MYSQL_HA_REOPEN_ON_USAGE mark for reopen.
MYSQL_HA_FLUSH_ALL flush all tables, not only
those marked for flush.
+ is_locked If LOCK_open is locked.
DESCRIPTION
The list of HANDLER tables may be NULL, in which case all HANDLER
@@ -601,7 +625,6 @@ err0:
If 'tables' is NULL and MYSQL_HA_FLUSH_ALL is not set,
all HANDLER tables marked for flush are closed.
Broadcasts a COND_refresh condition, for every table closed.
- The caller must lock LOCK_open.
NOTE
Since mysql_ha_flush() is called when the base table has to be closed,
@@ -611,10 +634,12 @@ err0:
0 ok
*/
-int mysql_ha_flush(THD *thd, TABLE_LIST *tables, uint mode_flags)
+int mysql_ha_flush(THD *thd, TABLE_LIST *tables, uint mode_flags,
+ bool is_locked)
{
TABLE_LIST *tmp_tables;
TABLE **table_ptr;
+ bool did_lock= FALSE;
DBUG_ENTER("mysql_ha_flush");
DBUG_PRINT("enter", ("tables: %p mode_flags: 0x%02x", tables, mode_flags));
@@ -640,6 +665,12 @@ int mysql_ha_flush(THD *thd, TABLE_LIST *tables, uint mode_flags)
(*table_ptr)->s->db,
(*table_ptr)->s->table_name,
(*table_ptr)->alias));
+ /* The first time it is required, lock for close_thread_table(). */
+ if (! did_lock && ! is_locked)
+ {
+ VOID(pthread_mutex_lock(&LOCK_open));
+ did_lock= TRUE;
+ }
mysql_ha_flush_table(thd, table_ptr, mode_flags);
continue;
}
@@ -658,6 +689,12 @@ int mysql_ha_flush(THD *thd, TABLE_LIST *tables, uint mode_flags)
if ((mode_flags & MYSQL_HA_FLUSH_ALL) ||
((*table_ptr)->s->version != refresh_version))
{
+ /* The first time it is required, lock for close_thread_table(). */
+ if (! did_lock && ! is_locked)
+ {
+ VOID(pthread_mutex_lock(&LOCK_open));
+ did_lock= TRUE;
+ }
mysql_ha_flush_table(thd, table_ptr, mode_flags);
continue;
}
@@ -665,6 +702,10 @@ int mysql_ha_flush(THD *thd, TABLE_LIST *tables, uint mode_flags)
}
}
+ /* Release the lock if it was taken by this function. */
+ if (did_lock)
+ VOID(pthread_mutex_unlock(&LOCK_open));
+
DBUG_RETURN(0);
}
@@ -696,8 +737,8 @@ static int mysql_ha_flush_table(THD *thd, TABLE **table_ptr, uint mode_flags)
table->alias, mode_flags));
if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash,
- (byte*) (*table_ptr)->alias,
- strlen((*table_ptr)->alias) + 1)))
+ (byte*) table->alias,
+ strlen(table->alias) + 1)))
{
if (! (mode_flags & MYSQL_HA_REOPEN_ON_USAGE))
{
@@ -712,6 +753,7 @@ static int mysql_ha_flush_table(THD *thd, TABLE **table_ptr, uint mode_flags)
}
(*table_ptr)->file->ha_index_or_rnd_end();
+ safe_mutex_assert_owner(&LOCK_open);
if (close_thread_table(thd, table_ptr))
{
/* Tell threads waiting for refresh that something has happened */