From: Date: December 5 2007 4:36pm Subject: bk commit into 5.1 tree (istruewing:1.2623) BUG#30273 List-Archive: http://lists.mysql.com/commits/39321 X-Bug: 30273 Message-Id: Below is the list of changes that have just been committed into a local 5.1 repository of istruewing. When istruewing does a push these changes will be propagated to the main repository and, within 24 hours after the push, to the public repository. For information on how to access the public repository see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html ChangeSet@stripped, 2007-12-05 16:36:52+01:00, istruewing@stripped +2 -0 Bug#30273 - merge tables: Can't lock file (errno: 155) The patch for Bug#26379 (Combination of FLUSH TABLE and REPAIR TABLE corrupts a MERGE table) fixed this bug too. However it revealed a new bug that crashed the server. Flushing a merge table at the moment when it is between open and attach of children crashed the server. The flushing thread wants to abort locks on the flushed table. It calls ha_myisammrg::lock_count() and ha_myisammrg::store_lock() on the TABLE object of the other thread. Changed ha_myisammrg::lock_count() and ha_myisammrg::store_lock() to accept non-attached children. The test case follows in another changeset. It requires some effort to make the problem repeatable. sql/lock.cc@stripped, 2007-12-05 16:36:51+01:00, istruewing@stripped +17 -3 Bug#30273 - merge tables: Can't lock file (errno: 155) Changed get_lock_data() so that the final lock_count is taken from the number of locks returned from handler::store_lock() instead of from handler::lock_count(). storage/myisammrg/ha_myisammrg.cc@stripped, 2007-12-05 16:36:51+01:00, istruewing@stripped +25 -2 Bug#30273 - merge tables: Can't lock file (errno: 155) Changed ha_myisammrg::lock_count() and ha_myisammrg::store_lock() to accept non-attached children. diff -Nrup a/sql/lock.cc b/sql/lock.cc --- a/sql/lock.cc 2007-08-27 15:19:53 +02:00 +++ b/sql/lock.cc 2007-12-05 16:36:51 +01:00 @@ -847,9 +847,6 @@ static MYSQL_LOCK *get_lock_data(THD *th locks= locks_buf= sql_lock->locks= (THR_LOCK_DATA**) (sql_lock + 1); to= table_buf= sql_lock->table= (TABLE**) (locks + tables * 2); sql_lock->table_count=lock_count; - sql_lock->lock_count=tables; - DBUG_PRINT("info", ("sql_lock->table_count %d sql_lock->lock_count %d", - sql_lock->table_count, sql_lock->lock_count)); for (i=0 ; i < count ; i++) { @@ -889,6 +886,23 @@ static MYSQL_LOCK *get_lock_data(THD *th for ( ; org_locks != locks ; org_locks++) (*org_locks)->debug_print_param= (void *) table; } + /* + We do not use 'tables', because there are cases where store_lock() + returns less locks than lock_count() claimed. This can happen when + a FLUSH TABLES tries to abort locks from a MERGE table of another + thread. When that thread has just opened the table, but not yet + attached its children, it cannot return the locks. lock_count() + always returns the number of locks that an attached table has. + This is done to avoid the reverse situation: If lock_count() would + return 0 for a non-attached MERGE table, and that table becomes + attached between the calls to lock_count() and store_lock(), then + we would have allocated too little memory for the lock data. Now + we may allocate too much, but better safe than memory overrun. + And in the FLUSH case, the memory is released quickly anyway. + */ + sql_lock->lock_count= locks - locks_buf; + DBUG_PRINT("info", ("sql_lock->table_count %d sql_lock->lock_count %d", + sql_lock->table_count, sql_lock->lock_count)); DBUG_RETURN(sql_lock); } diff -Nrup a/storage/myisammrg/ha_myisammrg.cc b/storage/myisammrg/ha_myisammrg.cc --- a/storage/myisammrg/ha_myisammrg.cc 2007-11-15 20:25:41 +01:00 +++ b/storage/myisammrg/ha_myisammrg.cc 2007-12-05 16:36:51 +01:00 @@ -901,7 +901,22 @@ int ha_myisammrg::external_lock(THD *thd uint ha_myisammrg::lock_count(void) const { - DBUG_ASSERT(this->file->children_attached); + /* + Return the real lock count even if the children are not attached. + This method is used for allocating memory. If we would return 0 + to another thread (e.g. doing FLUSH TABLE), and attach the children + before the other thread calls store_lock(), then we would return + more locks in store_lock() than we claimed by lock_count(). The + other tread would overrun its memory. + + This is however a change in the handler interface. lock_count() + can now return a higher number than store_lock() stores locks. + This is more safe than the reverse implementation would be. + + @todo If matching numbers are vital, then attaching of MERGE + children must be done under LOCK_open, and lock_count() return 0 + when the children are not attached. + */ return file->tables; } @@ -911,7 +926,15 @@ THR_LOCK_DATA **ha_myisammrg::store_lock enum thr_lock_type lock_type) { MYRG_TABLE *open_table; - DBUG_ASSERT(this->file->children_attached); + + /* + When MERGE table is open, but not yet attached, other threads + could flush it, which means call mysql_lock_abort_for_thread() + on this threads TABLE. 'children_attached' is FALSE in this + situaton. Since the table is not locked, return no lock data. + */ + if (!this->file->children_attached) + return to; for (open_table=file->open_tables ; open_table != file->end_table ;