1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-29 05:21:33 +03:00

Bug#14397 - OPTIMIZE TABLE with an open HANDLER causes a crash

Version for 4.0.
It fixes two 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".
This commit is contained in:
ingo@mysql.com
2005-11-03 18:24:12 +01:00
parent 983b7ad153
commit 1b99d30c23
7 changed files with 102 additions and 12 deletions

View File

@ -357,6 +357,7 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables,
ha_rows select_limit,ha_rows offset_limit)
{
TABLE_LIST *hash_tables;
TABLE **table_ptr;
TABLE *table;
int err;
int keyno=-1;
@ -379,6 +380,27 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables,
DBUG_PRINT("info-in-hash",("'%s'.'%s' as '%s' tab %p",
hash_tables->db, hash_tables->real_name,
hash_tables->alias, table));
/* Table might have been flushed. */
if (table && (table->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)
{}
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)
{
/*
@ -593,6 +615,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
@ -600,7 +623,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,
@ -610,10 +632,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));
@ -637,6 +661,12 @@ int mysql_ha_flush(THD *thd, TABLE_LIST *tables, uint mode_flags)
(*table_ptr)->table_cache_key,
(*table_ptr)->real_name,
(*table_ptr)->table_name));
/* 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;
}
@ -655,6 +685,12 @@ int mysql_ha_flush(THD *thd, TABLE_LIST *tables, uint mode_flags)
if ((mode_flags & MYSQL_HA_FLUSH_ALL) ||
((*table_ptr)->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;
}
@ -662,6 +698,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);
}
@ -693,8 +733,8 @@ static int mysql_ha_flush_table(THD *thd, TABLE **table_ptr, uint mode_flags)
table->table_name, mode_flags));
if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash,
(byte*) (*table_ptr)->table_name,
strlen((*table_ptr)->table_name) + 1)))
(byte*) table->table_name,
strlen(table->table_name) + 1)))
{
if (! (mode_flags & MYSQL_HA_REOPEN_ON_USAGE))
{
@ -708,6 +748,7 @@ static int mysql_ha_flush_table(THD *thd, TABLE **table_ptr, uint mode_flags)
}
}
safe_mutex_assert_owner(&LOCK_open);
if (close_thread_table(thd, table_ptr))
{
/* Tell threads waiting for refresh that something has happened */