diff options
author | unknown <ingo@mysql.com> | 2005-11-15 21:57:02 +0100 |
---|---|---|
committer | unknown <ingo@mysql.com> | 2005-11-15 21:57:02 +0100 |
commit | 013b3d8ab33b052d504a2e3fa63a85c919c649cc (patch) | |
tree | 119cf106bff33787b0aad32bfaf568b654dd7115 /sql/sql_handler.cc | |
parent | d9c7aaf23ff30675682775864c269bde84a3bc03 (diff) | |
download | mariadb-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.cc | 50 |
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 */ |