List:Commits« Previous MessageNext Message »
From:Ingo Struewing Date:December 5 2007 4:36pm
Subject:bk commit into 5.1 tree (istruewing:1.2623) BUG#30273
View as plain text  
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 ;
Thread
bk commit into 5.1 tree (istruewing:1.2623) BUG#30273Ingo Struewing5 Dec