[Commits] bzr commit into MariaDB 5.1, with Maria 1.5:maria branch (monty:2964)

Michael Widenius monty at askmonty.org
Tue Nov 2 17:23:01 EET 2010


#At lp:maria based on revid:knielsen at knielsen-hq.org-20101029075645-mxpl750gto4zfdtb

 2964 Michael Widenius	2010-11-02
      Fix for: LP #634955: Assert in _ma_update_at_original_place()
      Added locking of lock mutex when updating status in external_unlock() for Aria and MyISAM tables.
      Fixed that 'source' command doesn't cause mysql command line tool to exit on error.
      DEBUG_EXECUTE() and DEBUG_EVALUATE_IF() should not execute things based on wildcards. (Allows one to run --debug with mysql-test-run scripts that uses @debug)
      Fixed several core dump, deadlock and crashed table bugs in handling of LOCK TABLE with MERGE tables:
      - Added priority of locks to avoid crashes with MERGE tables.
      - Added thr_lock_merge() to allow one to merge two results of thr_lock().
      Fixed 'not found row' bug in REPLACE with Aria tables.
      Mark MyISAM tables that are part of MERGE with HA_OPEN_MERGE_TABLE and set the locks to have priority THR_LOCK_MERGE_PRIV.
      - By sorting MERGE tables last in thr_multi_unlock() it's safer to release and relock them many times (can happen when TRIGGERS are created)
      Avoid printing (null) in debug file (to easier find out wrong NULL pointer usage with %s).
      added:
        mysql-test/r/merge_debug.result
        mysql-test/t/merge_debug.test
      modified:
        client/mysql.cc
        client/mysqltest.cc
        dbug/dbug.c
        include/my_base.h
        include/thr_lock.h
        mysql-test/mysql-test-run.pl
        mysql-test/r/merge.result
        mysql-test/r/udf_query_cache.result
        mysql-test/suite/parts/r/partition_repair_myisam.result
        mysql-test/t/merge.test
        mysql-test/t/udf_query_cache.test
        mysys/my_getopt.c
        mysys/my_symlink2.c
        mysys/thr_lock.c
        sql/handler.cc
        sql/lock.cc
        sql/mysql_priv.h
        sql/mysqld.cc
        sql/parse_file.cc
        sql/set_var.cc
        sql/sql_base.cc
        sql/sql_class.cc
        sql/sql_parse.cc
        sql/sql_trigger.cc
        storage/maria/ma_bitmap.c
        storage/maria/ma_blockrec.c
        storage/maria/ma_blockrec.h
        storage/maria/ma_locking.c
        storage/maria/ma_open.c
        storage/maria/ma_state.c
        storage/maria/ma_state.h
        storage/maria/ma_write.c
        storage/myisam/mi_check.c
        storage/myisam/mi_extra.c
        storage/myisam/mi_locking.c
        storage/myisam/mi_open.c
        storage/myisam/myisamdef.h
        storage/myisammrg/ha_myisammrg.cc
        storage/myisammrg/myrg_locking.c
        storage/myisammrg/myrg_open.c
        storage/xtradb/buf/buf0buf.c
        storage/xtradb/buf/buf0lru.c

per-file messages:
  client/mysql.cc
    Fixed that 'source' command doesn't cause mysql command line tool to exit on error.
  client/mysqltest.cc
    Don't send NULL to fn_format(). (Can cause crash on Solaris when using --debug)
  dbug/dbug.c
    DEBUG_EXECUTE() and DEBUG_EVALUATE_IF() should not execute things based on wildcards.
  include/my_base.h
    Added flag to signal if one opens a MERGE table.
    Added extra() command to signal that one is not part of a MERGE table anymore.
  include/thr_lock.h
    Added priority for locks (needed to fix bug in thr_lock when using MERGE tables)
    Added option to thr_unlock() if get_status() should be called.
    Added prototype for thr_merge_locks().
  mysql-test/mysql-test-run.pl
    Ignore crashed table warnings for tables named 'crashed'.
  mysql-test/r/merge.result
    Renamed triggers to make debugging easier.
    Added some CHECK TABLES to catch errors earlier.
    Additional tests.
  mysql-test/r/merge_debug.result
    Test of error handling when reopening MERGE tables.
  mysql-test/r/udf_query_cache.result
    Added missing flush status
  mysql-test/suite/parts/r/partition_repair_myisam.result
    Update results
  mysql-test/t/merge.test
    Renamed triggers to make debugging easier.
    Added some CHECK TABLES to catch errors earlier.
    Additional tests.
  mysql-test/t/merge_debug.test
    Test of error handling when reopening MERGE tables.
  mysql-test/t/udf_query_cache.test
    Added missing flush status
  mysys/my_getopt.c
    Removed not used variable
  mysys/my_symlink2.c
    Changed (null) to (NULL) to make it easier to find NULL arguments to DBUG_PRINT() functions.
    (On linux, NULL to sprintf is printed 'null')
  mysys/thr_lock.c
    Added priority of locks to avoid crashes with MERGE tables.
    Added thr_lock_merge() to allow one to merge two results of thr_lock().
    - This is needed for MyISAM as all locked table must share the same status. If not, you will not see newly inserted rows in other instances of the table.
    If calling thr_unlock() with THR_UNLOCK_UPDATE_STATUS, call update_status() and restore_status() for the locks. This is needed in some rare cases where we call thr_unlock() followed by thr_lock() without calling external_unlock/external_lock in between.
    Simplify loop in thr_multi_lock().
    Added 'start_trans', which is called at end of thr_multi_lock() when all locks are taken.
    - This was needed by Aria to ensure that transaction is started when we got all locks, not at get_status(). Without this, some rows could not be visible when we lock two tables at the same time, causing REPLACE using two tables to fail unexpectedly.
  sql/handler.cc
    Add an assert() in handler::print_error() for "impossible errors" (like table is crashed) when --debug-assert-if-crashed-table is used.
  sql/lock.cc
    Simplify mysql_lock_tables() code if get_lock_data() returns 0 locks.
    Added new parameter to thr_multi_unlock()
    In mysql_unlock_read_tables(), call first externa_unlock(), then thr_multi_unlock();  This is same order as we do in mysql_unlock_tables().
    Don't abort locks in mysql_lock_abort() for merged tables when a MERGE table is deleted; Would cause a spin lock.
    Added call to thr_merge_locks() in mysql_lock_merge() to ensure consistency in thr_locks().
    - New locks of same type and table is stored after the old lock to ensure that we get the status from the original lock.
  sql/mysql_priv.h
    Added debug_assert_if_crashed_table
  sql/mysqld.cc
    Added --debug-assert-if-crashed-table
  sql/parse_file.cc
    Don't print '(null)' in DBUG_PRINT of no dir given
  sql/set_var.cc
    Increase default size of buffer for @debug variable.
  sql/sql_base.cc
    In case of error from reopen_table() in reopen_tables(), call unlock_open_table() and restart loop.
    - This fixed bug when we twice deleted same table from open_cache.
    Don't take name lock for already name locked table in open_unireg_entry().
    - Fixed bug when doing repair in reopen_table().
    - In detach_merge_children(), always detach if 'clear_refs' is given. We can't trust parent->children_attached as this function can be called twice, first time with clear_refs set to 0.
  sql/sql_class.cc
    Changed printing of (null) to "" in set_thd_proc_info()
  sql/sql_parse.cc
    Added DBUG
  sql/sql_trigger.cc
    Don't call unlink_open_table() if reopen_table() fails as the table may already be freed.
  storage/maria/ma_bitmap.c
    Fixed DBUG_ASSERT() in allocate_tail()
  storage/maria/ma_blockrec.c
    Fixed wrong calculation of row length for very small rows in undo_row_update().
    - Fixes ASSERT() when doing undo.
  storage/maria/ma_blockrec.h
    Added _ma_block_start_trans() and _ma_block_start_trans_no_versioning()
  storage/maria/ma_locking.c
    Call _ma_update_status_with_lock() when releasing write locks.
    - Fixes potential problem with updating status without the proper lock.
  storage/maria/ma_open.c
    Changed to use start_trans() instead of get_status() to ensure that we see all rows in all locked tables when we got the locks.
    - Fixed 'not found row' bug in REPLACE with Aria tables.
  storage/maria/ma_state.c
    Added _ma_update_status_with_lock() and _ma_block_start_trans().
    This is to ensure that we see all rows in all locked tables when we got the locks.
  storage/maria/ma_state.h
    Added _ma_update_status_with_lock()
  storage/maria/ma_write.c
    More DBUG_PRINT
  storage/myisam/mi_check.c
    Fixed error message
  storage/myisam/mi_extra.c
    Added HA_EXTRA_DETACH_CHILD:
    - Detach MyISAM table to not be part of MERGE table (remove flag & lock priority).
  storage/myisam/mi_locking.c
    Call mi_update_status_with_lock() when releasing write locks.
    - Fixes potential problem with updating status without the proper lock.
    Change to use new HA_OPEN_MERGE_TABLE flag to test if MERGE table.
    Added mi_fix_status(), called by thr_merge().
  storage/myisam/mi_open.c
    Added marker if part of MERGE table.
    Call mi_fix_status() in thr_lock() for transactional tables.
  storage/myisam/myisamdef.h
    Change my_once_flag to uint, as it stored different values than just 0/1
    Added 'open_flag' to store state given to mi_open()
  storage/myisammrg/ha_myisammrg.cc
    Add THR_LOCK_MERGE_PRIV to THR_LOCK_DATA to get MERGE locks sorted after other types of locks.
  storage/myisammrg/myrg_locking.c
    Remove windows specific code.
  storage/myisammrg/myrg_open.c
    Use HA_OPEN_MERGE_TABLE to mi_open().
    Set HA_OPEN_MERGE_TABLE for linked MyISAM tables.
  storage/xtradb/buf/buf0buf.c
    Fixed compiler warning
  storage/xtradb/buf/buf0lru.c
    Initialize variable that could be used not initialized.
=== modified file 'client/mysql.cc'
--- a/client/mysql.cc	2010-08-27 14:12:44 +0000
+++ b/client/mysql.cc	2010-11-02 15:22:57 +0000
@@ -4082,7 +4082,7 @@ static int com_source(String *buffer, ch
     If we got an error during source operation, don't abort the client
     if ignore_errors is set
   */
-  if (error && !batch_abort_on_error && ignore_errors)
+  if (error && ignore_errors)
     error= -1;                                  // Ignore error
   return error;
 }

=== modified file 'client/mysqltest.cc'
--- a/client/mysqltest.cc	2010-10-29 07:56:45 +0000
+++ b/client/mysqltest.cc	2010-11-02 15:22:57 +0000
@@ -1875,7 +1875,7 @@ void check_result()
     if (access(reject_file, W_OK) == 0)
     {
       /* Result file directory is writable, save reject file there */
-      fn_format(reject_file, result_file_name, NULL,
+      fn_format(reject_file, result_file_name, "",
                 ".reject", MY_REPLACE_EXT);
     }
     else

=== modified file 'dbug/dbug.c'
--- a/dbug/dbug.c	2010-10-13 15:15:43 +0000
+++ b/dbug/dbug.c	2010-11-02 15:22:57 +0000
@@ -286,7 +286,7 @@ typedef struct _db_code_state_ {
 #define ListDel(A,B,C) ListAddDel(A,B,C,EXCLUDE)
 static struct link *ListAddDel(struct link *, const char *, const char *, int);
 static struct link *ListCopy(struct link *);
-static int InList(struct link *linkp,const char *cp);
+static int InList(struct link *linkp,const char *cp, int exact_match);
 static uint ListFlags(struct link *linkp);
 static void FreeList(struct link *linkp);
 
@@ -1581,13 +1581,13 @@ static struct link *ListCopy(struct link
  *
  */
 
-static int InList(struct link *linkp, const char *cp)
+static int InList(struct link *linkp, const char *cp, int exact_match)
 {
   int result;
 
   for (result=MATCHED; linkp != NULL; linkp= linkp->next_link)
   {
-    if (!fnmatch(linkp->str, cp, 0))
+    if (!(exact_match ? strcmp(linkp->str,cp) : fnmatch(linkp->str, cp, 0)))
       return linkp->flags;
     if (!(linkp->flags & EXCLUDE))
       result=NOT_MATCHED;
@@ -1752,8 +1752,8 @@ void _db_end_()
 static int DoTrace(CODE_STATE *cs)
 {
   if ((cs->stack->maxdepth == 0 || cs->level <= cs->stack->maxdepth) &&
-      InList(cs->stack->processes, cs->process) & (MATCHED|INCLUDE))
-    switch(InList(cs->stack->functions, cs->func)) {
+      InList(cs->stack->processes, cs->process, 0) & (MATCHED|INCLUDE))
+    switch(InList(cs->stack->functions, cs->func, 0)) {
     case INCLUDE|SUBDIR:  return ENABLE_TRACE;
     case INCLUDE:         return DO_TRACE;
     case MATCHED|SUBDIR:
@@ -1790,10 +1790,10 @@ static int DoTrace(CODE_STATE *cs)
 #ifndef THREAD
 static BOOLEAN DoProfile(CODE_STATE *cs)
 {
-  return PROFILING &&
-         cs->level <= cs->stack->maxdepth &&
-         InList(cs->stack->p_functions, cs->func) & (INCLUDE|MATCHED) &&
-         InList(cs->stack->processes, cs->process) & (INCLUDE|MATCHED);
+  return (PROFILING &&
+          cs->level <= cs->stack->maxdepth &&
+          InList(cs->stack->p_functions, cs->func, 0) & (INCLUDE|MATCHED) &&
+          InList(cs->stack->processes, cs->process, 0) & (INCLUDE|MATCHED));
 }
 #endif
 
@@ -1827,10 +1827,10 @@ FILE *_db_fp_(void)
 BOOLEAN _db_keyword_(CODE_STATE *cs, const char *keyword, int strict)
 {
   get_code_state_if_not_set_or_return FALSE;
-  strict=strict ? INCLUDE : INCLUDE|MATCHED;
+  int match= strict ? INCLUDE : INCLUDE|MATCHED;
 
-  return DEBUGGING && DoTrace(cs) & DO_TRACE &&
-         InList(cs->stack->keywords, keyword) & strict;
+  return (DEBUGGING && DoTrace(cs) & DO_TRACE &&
+          InList(cs->stack->keywords, keyword, strict) & match);
 }
 
 /*

=== modified file 'include/my_base.h'
--- a/include/my_base.h	2010-08-02 16:39:45 +0000
+++ b/include/my_base.h	2010-11-02 15:22:57 +0000
@@ -50,6 +50,7 @@
 #define HA_OPEN_COPY			256     /* Open copy (for repair) */
 /* Internal temp table, used for temporary results */
 #define HA_OPEN_INTERNAL_TABLE          512
+#define HA_OPEN_MERGE_TABLE		1024
 
 /* The following is parameter to ha_rkey() how to use key */
 
@@ -196,6 +197,7 @@ enum ha_extra_function {
   */
   HA_EXTRA_ATTACH_CHILDREN,
   HA_EXTRA_DETACH_CHILDREN,
+  HA_EXTRA_DETACH_CHILD,
   /* Inform handler we will force a close as part of flush */
   HA_EXTRA_PREPARE_FOR_FORCED_CLOSE
 };

=== modified file 'include/thr_lock.h'
--- a/include/thr_lock.h	2009-09-07 20:50:10 +0000
+++ b/include/thr_lock.h	2010-11-02 15:22:57 +0000
@@ -82,6 +82,12 @@ enum enum_thr_lock_result { THR_LOCK_SUC
                             THR_LOCK_WAIT_TIMEOUT= 2, THR_LOCK_DEADLOCK= 3 };
 
 
+/* Priority for locks */
+#define THR_LOCK_LATE_PRIV  1	/* For locks to be merged with org lock */
+#define THR_LOCK_MERGE_PRIV 2	/* For merge tables */
+
+#define THR_UNLOCK_UPDATE_STATUS 1
+
 extern ulong max_write_lock_count;
 extern ulong table_lock_wait_timeout;
 extern my_bool thr_lock_inited;
@@ -116,9 +122,10 @@ typedef struct st_thr_lock_data {
   struct st_thr_lock_data *next,**prev;
   struct st_thr_lock *lock;
   pthread_cond_t *cond;
-  enum thr_lock_type type;
   void *status_param;			/* Param to status functions */
   void *debug_print_param;
+  enum thr_lock_type type;
+  uint priority;
 } THR_LOCK_DATA;
 
 struct st_lock_list {
@@ -138,8 +145,10 @@ typedef struct st_thr_lock {
   void (*get_status)(void*, my_bool);	/* When one gets a lock */
   void (*copy_status)(void*,void*);
   void (*update_status)(void*);		/* Before release of write */
-  void (*restore_status)(void*);         /* Before release of read */
+  void (*restore_status)(void*);        /* Before release of read */
+  my_bool (*start_trans)(void*);	/* When all locks are taken */
   my_bool (*check_status)(void *);
+  void   (*fix_status)(void *, void *);/* For thr_merge_locks() */
   my_bool allow_multiple_concurrent_insert;
 } THR_LOCK;
 
@@ -154,13 +163,11 @@ void thr_lock_init(THR_LOCK *lock);
 void thr_lock_delete(THR_LOCK *lock);
 void thr_lock_data_init(THR_LOCK *lock,THR_LOCK_DATA *data,
 			void *status_param);
-enum enum_thr_lock_result thr_lock(THR_LOCK_DATA *data,
-                                   THR_LOCK_OWNER *owner,
-                                   enum thr_lock_type lock_type);
-void thr_unlock(THR_LOCK_DATA *data);
+void thr_unlock(THR_LOCK_DATA *data, uint unlock_flags);
 enum enum_thr_lock_result thr_multi_lock(THR_LOCK_DATA **data,
                                          uint count, THR_LOCK_OWNER *owner);
-void thr_multi_unlock(THR_LOCK_DATA **data,uint count);
+void thr_multi_unlock(THR_LOCK_DATA **data,uint count, uint unlock_flags);
+void thr_merge_locks(THR_LOCK_DATA **data, uint org_count, uint new_count);
 void thr_abort_locks(THR_LOCK *lock, my_bool upgrade_lock);
 my_bool thr_abort_locks_for_thread(THR_LOCK *lock, my_thread_id thread);
 void thr_print_locks(void);		/* For debugging */

=== modified file 'mysql-test/mysql-test-run.pl'
--- a/mysql-test/mysql-test-run.pl	2010-10-19 13:58:35 +0000
+++ b/mysql-test/mysql-test-run.pl	2010-11-02 15:22:57 +0000
@@ -4284,6 +4284,7 @@ sub extract_warning_lines ($) {
      qr/Slave SQL thread retried transaction/,
      qr/Slave \(additional info\)/,
      qr/Incorrect information in file/,
+     qr/Incorrect key file for table .*crashed.*/,
      qr/Slave I\/O: Get master SERVER_ID failed with error:.*/,
      qr/Slave I\/O: Get master clock failed with error:.*/,
      qr/Slave I\/O: Get master COLLATION_SERVER failed with error:.*/,

=== modified file 'mysql-test/r/merge.result'
--- a/mysql-test/r/merge.result	2010-04-28 12:52:24 +0000
+++ b/mysql-test/r/merge.result	2010-11-02 15:22:57 +0000
@@ -1574,7 +1574,7 @@ UNLOCK TABLES;
 #
 # Trigger on parent
 DELETE FROM t4 WHERE c1 = 4;
-CREATE TRIGGER t4_ai AFTER INSERT ON t4 FOR EACH ROW SET @a=1;
+CREATE TRIGGER t4_ai1 AFTER INSERT ON t4 FOR EACH ROW SET @a=1;
 SET @a=0;
 INSERT INTO t4 VALUES (4);
 SELECT @a;
@@ -1586,10 +1586,13 @@ c1
 2
 3
 4
-DROP TRIGGER t4_ai;
+DROP TRIGGER t4_ai1;
+CHECK TABLE t3;
+Table	Op	Msg_type	Msg_text
+test.t3	check	status	OK
 # Trigger on parent under LOCK TABLES
 LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE;
-CREATE TRIGGER t4_ai AFTER INSERT ON t4 FOR EACH ROW SET @a=1;
+CREATE TRIGGER t4_ai2 AFTER INSERT ON t4 FOR EACH ROW SET @a=1;
 SET @a=0;
 INSERT INTO t4 VALUES (4);
 SELECT @a;
@@ -1602,12 +1605,15 @@ c1
 3
 4
 4
-DROP TRIGGER t4_ai;
+DROP TRIGGER t4_ai2;
 UNLOCK TABLES;
+CHECK TABLE t3;
+Table	Op	Msg_type	Msg_text
+test.t3	check	status	OK
 #
 # Trigger on child
 DELETE FROM t4 WHERE c1 = 4;
-CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW SET @a=1;
+CREATE TRIGGER t3_ai3 AFTER INSERT ON t3 FOR EACH ROW SET @a=1;
 SET @a=0;
 INSERT INTO t4 VALUES (4);
 SELECT @a;
@@ -1624,10 +1630,13 @@ c1
 3
 4
 33
-DROP TRIGGER t3_ai;
+DROP TRIGGER t3_ai3;
+CHECK TABLE t3;
+Table	Op	Msg_type	Msg_text
+test.t3	check	status	OK
 # Trigger on child under LOCK TABLES
 LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE;
-CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW SET @a=1;
+CREATE TRIGGER t3_ai4 AFTER INSERT ON t3 FOR EACH ROW SET @a=1;
 SET @a=0;
 INSERT INTO t4 VALUES (4);
 SELECT @a;
@@ -1647,11 +1656,17 @@ c1
 33
 33
 DELETE FROM t4 WHERE c1 = 33;
-DROP TRIGGER t3_ai;
+DROP TRIGGER t3_ai4;
+CHECK TABLE t3;
+Table	Op	Msg_type	Msg_text
+test.t3	check	status	OK
 #
 # Trigger with table use on child
 DELETE FROM t4 WHERE c1 = 4;
-CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22);
+CREATE TRIGGER t3_ai5 AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22);
+SELECT COUNT(*) FROM t2;
+COUNT(*)
+1
 INSERT INTO t4 VALUES (4);
 SELECT * FROM t4 ORDER BY c1;
 c1
@@ -1670,10 +1685,15 @@ c1
 33
 DELETE FROM t4 WHERE c1 = 22;
 DELETE FROM t4 WHERE c1 = 33;
-DROP TRIGGER t3_ai;
+DROP TRIGGER t3_ai5;
+UNLOCK TABLES;
+CHECK TABLE t2,t3;
+Table	Op	Msg_type	Msg_text
+test.t2	check	status	OK
+test.t3	check	status	OK
 # Trigger with table use on child under LOCK TABLES
 LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE;
-CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22);
+CREATE TRIGGER t3_ai6 AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22);
 INSERT INTO t4 VALUES (4);
 SELECT * FROM t4 ORDER BY c1;
 c1
@@ -1692,10 +1712,44 @@ c1
 4
 22
 33
-DROP TRIGGER t3_ai;
+DROP TRIGGER t3_ai6;
+UNLOCK TABLES;
+check table t2,t3,t4;
+Table	Op	Msg_type	Msg_text
+test.t2	check	status	OK
+test.t3	check	status	OK
+test.t4	check	status	OK
 DELETE FROM t4 WHERE c1 = 22;
 DELETE FROM t4 WHERE c1 = 33;
+# Trigger with table use on child under different LOCK TABLES
+DELETE FROM t4 WHERE c1 = 4;
+LOCK TABLES t4 WRITE,t3 WRITE, t2 WRITE, t1 WRITE;
+CREATE TRIGGER t3_ai7 AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22);
+INSERT INTO t4 VALUES (4);
+SELECT * FROM t4 ORDER BY c1;
+c1
+1
+2
+3
+4
+INSERT INTO t3 VALUES (33);
+SELECT * FROM t4 ORDER BY c1;
+c1
+1
+2
+3
+4
+22
+33
+DROP TRIGGER t3_ai7;
 UNLOCK TABLES;
+check table t2,t3,t4;
+Table	Op	Msg_type	Msg_text
+test.t2	check	status	OK
+test.t3	check	status	OK
+test.t4	check	status	OK
+DELETE FROM t4 WHERE c1 = 22;
+DELETE FROM t4 WHERE c1 = 33;
 #
 # Repair
 #
@@ -1711,7 +1765,6 @@ c1
 2
 3
 4
-4
 LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE;
 REPAIR TABLE t4;
 Table	Op	Msg_type	Msg_text
@@ -1725,7 +1778,6 @@ c1
 2
 3
 4
-4
 UNLOCK TABLES;
 #
 # Optimize
@@ -1742,7 +1794,6 @@ c1
 2
 3
 4
-4
 LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE;
 OPTIMIZE TABLE t4;
 Table	Op	Msg_type	Msg_text
@@ -1756,14 +1807,13 @@ c1
 2
 3
 4
-4
 UNLOCK TABLES;
 #
 # Checksum
 #
 CHECKSUM TABLE t4;
 Table	Checksum
-test.t4	46622073
+test.t4	149057747
 CHECKSUM TABLE t2;
 Table	Checksum
 test.t2	3700403066
@@ -1773,11 +1823,10 @@ c1
 2
 3
 4
-4
 LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE;
 CHECKSUM TABLE t4;
 Table	Checksum
-test.t4	46622073
+test.t4	149057747
 CHECKSUM TABLE t2;
 Table	Checksum
 test.t2	3700403066
@@ -1787,7 +1836,6 @@ c1
 2
 3
 4
-4
 UNLOCK TABLES;
 #
 # Insert delayed
@@ -1801,7 +1849,6 @@ c1
 2
 3
 4
-4
 33
 LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE;
 INSERT DELAYED INTO t4 VALUES(444);
@@ -1814,7 +1861,6 @@ c1
 2
 3
 4
-4
 33
 UNLOCK TABLES;
 DROP TABLE t1, t2, t3, t4;

=== added file 'mysql-test/r/merge_debug.result'
--- a/mysql-test/r/merge_debug.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/r/merge_debug.result	2010-11-02 15:22:57 +0000
@@ -0,0 +1,24 @@
+set global storage_engine=myisam;
+set session storage_engine=myisam;
+drop table if exists crashed,t2,t3,t4;
+SET @orig_debug=@@debug;
+CREATE TABLE crashed (c1 INT);
+CREATE TABLE t2 (c1 INT);
+CREATE TABLE t3 (c1 INT);
+CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(crashed,t2,t3) INSERT_METHOD=LAST;
+INSERT INTO crashed VALUES (10);
+INSERT INTO t2 VALUES (20);
+INSERT INTO t3 VALUES (30);
+LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, crashed WRITE;
+SET GLOBAL debug="+d,*,myisam_pretend_crashed_table_on_open";
+CREATE TRIGGER t1_ai AFTER INSERT ON crashed FOR EACH ROW INSERT INTO t2 VALUES(29);
+SET GLOBAL debug=@orig_debug;
+INSERT INTO t4 VALUES (39);
+ERROR HY000: Table 't4' was not locked with LOCK TABLES
+INSERT INTO crashed VALUES (11);
+ERROR HY000: Table 'crashed' was not locked with LOCK TABLES
+INSERT INTO t2 VALUES (21);
+INSERT INTO t3 VALUES (31);
+UNLOCK TABLES;
+DROP TRIGGER t1_ai;
+DROP TABLE t4,crashed,t2,t3;

=== modified file 'mysql-test/r/udf_query_cache.result'
--- a/mysql-test/r/udf_query_cache.result	2010-01-11 13:15:28 +0000
+++ b/mysql-test/r/udf_query_cache.result	2010-11-02 15:22:57 +0000
@@ -3,6 +3,7 @@ CREATE FUNCTION metaphon RETURNS STRING
 create table t1 (a char);
 set GLOBAL query_cache_size=1355776;
 reset query cache;
+flush status;
 select metaphon('MySQL') from t1;
 metaphon('MySQL')
 show status like "Qcache_hits";

=== modified file 'mysql-test/suite/parts/r/partition_repair_myisam.result'
--- a/mysql-test/suite/parts/r/partition_repair_myisam.result	2008-08-19 09:44:22 +0000
+++ b/mysql-test/suite/parts/r/partition_repair_myisam.result	2010-11-02 15:22:57 +0000
@@ -408,7 +408,7 @@ ALTER TABLE t1_will_crash CHECK PARTITIO
 Table	Op	Msg_type	Msg_text
 test.t1_will_crash	check	warning	Size of datafile is: 868       Should be: 604
 test.t1_will_crash	check	error	Record-count is not ok; is 8   Should be: 7
-test.t1_will_crash	check	warning	Found 10 key parts. Should be: 7
+test.t1_will_crash	check	warning	Found 10 parts. Should be: 7
 test.t1_will_crash	check	error	Partition p6 returned error
 test.t1_will_crash	check	error	Corrupt
 ALTER TABLE t1_will_crash REPAIR PARTITION p6;

=== modified file 'mysql-test/t/merge.test'
--- a/mysql-test/t/merge.test	2010-04-28 12:52:24 +0000
+++ b/mysql-test/t/merge.test	2010-11-02 15:22:57 +0000
@@ -1119,35 +1119,38 @@ UNLOCK TABLES;
 --echo #
 --echo # Trigger on parent
 DELETE FROM t4 WHERE c1 = 4;
-CREATE TRIGGER t4_ai AFTER INSERT ON t4 FOR EACH ROW SET @a=1;
+CREATE TRIGGER t4_ai1 AFTER INSERT ON t4 FOR EACH ROW SET @a=1;
 SET @a=0;
 INSERT INTO t4 VALUES (4);
 SELECT @a;
 SELECT * FROM t4 ORDER BY c1;
-DROP TRIGGER t4_ai;
+DROP TRIGGER t4_ai1;
+CHECK TABLE t3;
 --echo # Trigger on parent under LOCK TABLES
 LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE;
-CREATE TRIGGER t4_ai AFTER INSERT ON t4 FOR EACH ROW SET @a=1;
+CREATE TRIGGER t4_ai2 AFTER INSERT ON t4 FOR EACH ROW SET @a=1;
 SET @a=0;
 INSERT INTO t4 VALUES (4);
 SELECT @a;
 SELECT * FROM t4 ORDER BY c1;
-DROP TRIGGER t4_ai;
+DROP TRIGGER t4_ai2;
 UNLOCK TABLES;
+CHECK TABLE t3;
 --echo #
 --echo # Trigger on child
 DELETE FROM t4 WHERE c1 = 4;
-CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW SET @a=1;
+CREATE TRIGGER t3_ai3 AFTER INSERT ON t3 FOR EACH ROW SET @a=1;
 SET @a=0;
 INSERT INTO t4 VALUES (4);
 SELECT @a;
 INSERT INTO t3 VALUES (33);
 SELECT @a;
 SELECT * FROM t4 ORDER BY c1;
-DROP TRIGGER t3_ai;
+DROP TRIGGER t3_ai3;
+CHECK TABLE t3;
 --echo # Trigger on child under LOCK TABLES
 LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE;
-CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW SET @a=1;
+CREATE TRIGGER t3_ai4 AFTER INSERT ON t3 FOR EACH ROW SET @a=1;
 SET @a=0;
 INSERT INTO t4 VALUES (4);
 SELECT @a;
@@ -1155,29 +1158,47 @@ INSERT INTO t3 VALUES (33);
 SELECT @a;
 SELECT * FROM t4 ORDER BY c1;
 DELETE FROM t4 WHERE c1 = 33;
-DROP TRIGGER t3_ai;
+DROP TRIGGER t3_ai4;
+CHECK TABLE t3;
 --echo #
 --echo # Trigger with table use on child
 DELETE FROM t4 WHERE c1 = 4;
-CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22);
+CREATE TRIGGER t3_ai5 AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22);
+SELECT COUNT(*) FROM t2;
 INSERT INTO t4 VALUES (4);
 SELECT * FROM t4 ORDER BY c1;
 INSERT INTO t3 VALUES (33);
 SELECT * FROM t4 ORDER BY c1;
 DELETE FROM t4 WHERE c1 = 22;
 DELETE FROM t4 WHERE c1 = 33;
-DROP TRIGGER t3_ai;
+DROP TRIGGER t3_ai5;
+UNLOCK TABLES;
+CHECK TABLE t2,t3; 
 --echo # Trigger with table use on child under LOCK TABLES
 LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, t1 WRITE;
-CREATE TRIGGER t3_ai AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22);
+CREATE TRIGGER t3_ai6 AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22);
 INSERT INTO t4 VALUES (4);
 SELECT * FROM t4 ORDER BY c1;
 INSERT INTO t3 VALUES (33);
 SELECT * FROM t4 ORDER BY c1;
-DROP TRIGGER t3_ai;
+DROP TRIGGER t3_ai6;
+UNLOCK TABLES;
+check table t2,t3,t4;
 DELETE FROM t4 WHERE c1 = 22;
 DELETE FROM t4 WHERE c1 = 33;
+--echo # Trigger with table use on child under different LOCK TABLES
+DELETE FROM t4 WHERE c1 = 4;
+LOCK TABLES t4 WRITE,t3 WRITE, t2 WRITE, t1 WRITE;
+CREATE TRIGGER t3_ai7 AFTER INSERT ON t3 FOR EACH ROW INSERT INTO t2 VALUES(22);
+INSERT INTO t4 VALUES (4);
+SELECT * FROM t4 ORDER BY c1;
+INSERT INTO t3 VALUES (33);
+SELECT * FROM t4 ORDER BY c1;
+DROP TRIGGER t3_ai7;
 UNLOCK TABLES;
+check table t2,t3,t4;
+DELETE FROM t4 WHERE c1 = 22;
+DELETE FROM t4 WHERE c1 = 33;
 #
 --echo #
 --echo # Repair

=== added file 'mysql-test/t/merge_debug.test'
--- a/mysql-test/t/merge_debug.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/merge_debug.test	2010-11-02 15:22:57 +0000
@@ -0,0 +1,42 @@
+#
+# Test failures with MERGE
+#
+
+--source include/have_debug.inc
+
+let $default=`select @@global.storage_engine`;
+set global storage_engine=myisam;
+set session storage_engine=myisam;
+
+--disable_warnings
+drop table if exists crashed,t2,t3,t4;
+--enable_warnings
+
+SET @orig_debug=@@debug;
+
+#
+# Check that MariaDB handles reopen that fails without crashing
+#
+CREATE TABLE crashed (c1 INT);
+CREATE TABLE t2 (c1 INT);
+CREATE TABLE t3 (c1 INT);
+CREATE TABLE t4 (c1 INT) ENGINE=MRG_MYISAM UNION=(crashed,t2,t3) INSERT_METHOD=LAST;
+INSERT INTO crashed VALUES (10);
+INSERT INTO t2 VALUES (20);
+INSERT INTO t3 VALUES (30);
+
+LOCK TABLES t3 WRITE, t2 WRITE, t4 WRITE, crashed WRITE;
+SET GLOBAL debug="+d,*,myisam_pretend_crashed_table_on_open";
+--disable_warnings
+CREATE TRIGGER t1_ai AFTER INSERT ON crashed FOR EACH ROW INSERT INTO t2 VALUES(29);
+--enable_warnings
+SET GLOBAL debug=@orig_debug;
+--error ER_TABLE_NOT_LOCKED
+INSERT INTO t4 VALUES (39);
+--error ER_TABLE_NOT_LOCKED
+INSERT INTO crashed VALUES (11);
+INSERT INTO t2 VALUES (21);
+INSERT INTO t3 VALUES (31);
+UNLOCK TABLES;
+DROP TRIGGER t1_ai;
+DROP TABLE t4,crashed,t2,t3;

=== modified file 'mysql-test/t/udf_query_cache.test'
--- a/mysql-test/t/udf_query_cache.test	2010-06-10 09:11:52 +0000
+++ b/mysql-test/t/udf_query_cache.test	2010-11-02 15:22:57 +0000
@@ -20,6 +20,7 @@ create table t1 (a char);
 
 set GLOBAL query_cache_size=1355776;
 reset query cache;
+flush status;
 
 select metaphon('MySQL') from t1;
 show status like "Qcache_hits";

=== modified file 'mysys/my_getopt.c'
--- a/mysys/my_getopt.c	2010-10-19 13:58:35 +0000
+++ b/mysys/my_getopt.c	2010-11-02 15:22:57 +0000
@@ -614,7 +614,6 @@ static int setval(const struct my_option
 		  my_bool set_maximum_value)
 {
   int err= 0;
-  int pos;
 
   if (value && argument)
   {

=== modified file 'mysys/my_symlink2.c'
--- a/mysys/my_symlink2.c	2009-09-07 20:50:10 +0000
+++ b/mysys/my_symlink2.c	2010-11-02 15:22:57 +0000
@@ -34,8 +34,8 @@ File my_create_with_symlink(const char *
   char abs_linkname[FN_REFLEN];
   DBUG_ENTER("my_create_with_symlink");
   DBUG_PRINT("enter", ("linkname: %s  filename: %s",
-                       linkname ? linkname : "(null)",
-                       filename ? filename : "(null)"));
+                       linkname ? linkname : "(NULL)",
+                       filename ? filename : "(NULL)"));
 
   if (my_disable_symlinks)
   {

=== modified file 'mysys/thr_lock.c'
--- a/mysys/thr_lock.c	2010-08-27 14:12:44 +0000
+++ b/mysys/thr_lock.c	2010-11-02 15:22:57 +0000
@@ -63,6 +63,11 @@ update_status:
         A storage engine should also call update_status internally
         in the ::external_lock(F_UNLCK) method.
         In MyISAM and CSV this functions updates the length of the datafile.
+        MySQL does in some exceptional cases (when doing DLL statements on
+        open tables calls thr_unlock() followed by thr_lock() without calling
+        ::external_lock() in between. In this case thr_unlock() is called with
+        the THR_UNLOCK_UPDATE_STATUS flag and thr_unlock() will call
+        update_status for write locks.
 get_status:
 	When one gets a lock this functions is called.
 	In MyISAM this stores the number of rows and size of the datafile
@@ -105,8 +110,30 @@ static inline pthread_cond_t *get_cond(v
   return &my_thread_var->suspend;
 }
 
+
+/*
+  Priority for locks (decides in which order locks are locked)
+  We want all write locks to be first, followed by read locks.
+  Locks from MERGE tables has a little lower priority than other
+  locks, to allow one to release merge tables without having
+  to unlock and re-lock other locks.
+  The lower the number, the higher the priority for the lock.
+  Read locks should have 4, write locks should have 0.
+  UNLOCK is 8, to force these last in thr_merge_locks.
+  For MERGE tables we add 2 (THR_LOCK_MERGE_PRIV) to the lock priority.
+  THR_LOCK_LATE_PRIV (1) is used when one locks other tables to be merged
+  with existing locks. This way we prioritize the original locks over the
+  new locks.
+*/
+
+static uint lock_priority[(uint)TL_WRITE_ONLY+1] =
+{ 8, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0};
+
+#define LOCK_CMP(A,B) ((uchar*) ((A)->lock) + lock_priority[(uint) (A)->type] + (A)->priority < (uchar*) ((B)->lock) + lock_priority[(uint) (B)->type] + (B)->priority)
+
+
 /*
-** For the future (now the thread specific cond is alloced by my_pthread.c)
+  For the future (now the thread specific cond is alloced by my_pthread.c)
 */
 
 my_bool init_thr_lock()
@@ -530,7 +557,7 @@ wait_for_lock(struct st_lock_list *wait,
 }
 
 
-enum enum_thr_lock_result
+static enum enum_thr_lock_result
 thr_lock(THR_LOCK_DATA *data, THR_LOCK_OWNER *owner,
          enum thr_lock_type lock_type)
 {
@@ -544,6 +571,7 @@ thr_lock(THR_LOCK_DATA *data, THR_LOCK_O
   data->cond=0;					/* safety */
   data->type=lock_type;
   data->owner= owner;                           /* Must be reset ! */
+  data->priority&= ~THR_LOCK_LATE_PRIV;
   VOID(pthread_mutex_lock(&lock->mutex));
   DBUG_PRINT("lock",("data: 0x%lx  thread: 0x%lx  lock: 0x%lx  type: %d",
                      (long) data, data->owner->info->thread_id,
@@ -808,7 +836,7 @@ static inline void free_all_read_locks(T
 
 	/* Unlock lock and free next thread on same lock */
 
-void thr_unlock(THR_LOCK_DATA *data)
+void thr_unlock(THR_LOCK_DATA *data, uint unlock_flags)
 {
   THR_LOCK *lock=data->lock;
   enum thr_lock_type lock_type=data->type;
@@ -832,6 +860,21 @@ void thr_unlock(THR_LOCK_DATA *data)
   }
   else
     lock->write.last=data->prev;
+ 
+  if (unlock_flags & THR_UNLOCK_UPDATE_STATUS)
+  {
+    /* External lock was not called; Update or restore status */
+    if (lock_type >= TL_WRITE_CONCURRENT_INSERT)
+    {
+      if (lock->update_status)
+        (*lock->update_status)(data->status_param);
+    }
+    else
+    {
+      if (lock->restore_status)
+        (*lock->restore_status)(data->status_param);
+    }
+  }
   if (lock_type == TL_READ_NO_INSERT)
     lock->read_no_write_count--;
   data->type=TL_UNLOCK;				/* Mark unlocked */
@@ -967,14 +1010,12 @@ end:
 
 
 /*
-** Get all locks in a specific order to avoid dead-locks
-** Sort acording to lock position and put write_locks before read_locks if
-** lock on same lock.
+  Get all locks in a specific order to avoid dead-locks
+  Sort acording to lock position and put write_locks before read_locks if
+  lock on same lock. Locks on MERGE tables has lower priority than other
+  locks of the same type. See comment for lock_priority.
 */
 
-
-#define LOCK_CMP(A,B) ((uchar*) (A->lock) - (uint) ((A)->type) < (uchar*) (B->lock)- (uint) ((B)->type))
-
 static void sort_locks(THR_LOCK_DATA **data,uint count)
 {
   THR_LOCK_DATA **pos,**end,**prev,*tmp;
@@ -999,18 +1040,22 @@ static void sort_locks(THR_LOCK_DATA **d
 enum enum_thr_lock_result
 thr_multi_lock(THR_LOCK_DATA **data, uint count, THR_LOCK_OWNER *owner)
 {
-  THR_LOCK_DATA **pos,**end;
+  THR_LOCK_DATA **pos, **end, **first_lock;
   DBUG_ENTER("thr_multi_lock");
   DBUG_PRINT("lock",("data: 0x%lx  count: %d", (long) data, count));
+
   if (count > 1)
     sort_locks(data,count);
+  else if (count == 0)
+    DBUG_RETURN(THR_LOCK_SUCCESS);
+
   /* lock everything */
   for (pos=data,end=data+count; pos < end ; pos++)
   {
     enum enum_thr_lock_result result= thr_lock(*pos, owner, (*pos)->type);
     if (result != THR_LOCK_SUCCESS)
     {						/* Aborted */
-      thr_multi_unlock(data,(uint) (pos-data));
+      thr_multi_unlock(data,(uint) (pos-data), 0);
       DBUG_RETURN(result);
     }
 #ifdef MAIN
@@ -1018,63 +1063,103 @@ thr_multi_lock(THR_LOCK_DATA **data, uin
 	   (long) pos[0]->lock, pos[0]->type); fflush(stdout);
 #endif
   }
+
   /*
-    Ensure that all get_locks() have the same status
+    Call start_trans for all locks.
     If we lock the same table multiple times, we must use the same
-    status_param!
+    status_param; We ensure this by calling copy_status() for all
+    copies of the same tables.
   */
-#if !defined(DONT_USE_RW_LOCKS)
-  if (count > 1)
+  if ((*data)->lock->start_trans)
+    ((*data)->lock->start_trans)((*data)->status_param);
+  for (first_lock=data, pos= data+1 ; pos < end ; pos++)
+  {
+    /* Get the current status (row count, checksum, trid etc) */
+    if ((*pos)->lock->start_trans)
+      (*(*pos)->lock->start_trans)((*pos)->status_param);
+    /*
+      If same table as previous table use pointer to previous status
+      information to ensure that all read/write tables shares same
+      state.
+    */
+    if (pos[0]->lock == pos[-1]->lock && pos[0]->lock->copy_status)
+      (pos[0]->lock->copy_status)((*pos)->status_param,
+                                  (*first_lock)->status_param);
+    else
+    {
+      /* Different lock, use this as base for next lock */
+      first_lock= pos;
+    }
+  }
+  DBUG_RETURN(THR_LOCK_SUCCESS);
+}
+
+
+/**
+  Merge two sets of locks.
+
+  @param data       All locks. First old locks, then new locks.
+  @param old_count  Original number of locks. These are first in 'data'.
+  @param new_count  How many new locks
+
+  The merge is needed if the new locks contains same tables as the old
+  locks, in which case we have to ensure that same tables shares the
+  same status (as after a thr_multi_lock()).
+*/
+
+void thr_merge_locks(THR_LOCK_DATA **data, uint old_count, uint new_count)
+{
+  THR_LOCK_DATA **pos, **end, **first_lock= 0;
+  DBUG_ENTER("thr_merge_lock");
+
+  /* Remove marks on old locks to make them sort before new ones */
+  for (pos=data, end= pos + old_count; pos < end ; pos++)
+    (*pos)->priority&= ~THR_LOCK_LATE_PRIV;
+
+  /* Mark new locks with LATE_PRIV to make them sort after org ones */
+  for (pos=data + old_count, end= pos + new_count; pos < end ; pos++)
+    (*pos)->priority|= THR_LOCK_LATE_PRIV;
+
+  sort_locks(data, old_count + new_count);
+
+  for (pos=data ; pos < end ; pos++)
   {
-    THR_LOCK_DATA *last_lock= end[-1];
-    pos=end-1;
-    do
-    {
-      pos--;
-      if (last_lock->lock == (*pos)->lock &&
-	  last_lock->lock->copy_status)
-      {
-	if (last_lock->type <= TL_READ_NO_INSERT)
-	{
-	  THR_LOCK_DATA **read_lock;
-	  /*
-	    If we are locking the same table with read locks we must ensure
-	    that all tables share the status of the last write lock or
-	    the same read lock.
-	  */
-	  for (;
-	       (*pos)->type <= TL_READ_NO_INSERT &&
-		 pos != data &&
-		 pos[-1]->lock == (*pos)->lock ;
-	       pos--) ;
+    /* Check if lock was unlocked before */
+    if (pos[0]->type == TL_UNLOCK || ! pos[0]->lock->fix_status)
+    {
+      DBUG_PRINT("info", ("lock skipped.  unlocked: %d  fix_status: %d",
+                          pos[0]->type == TL_UNLOCK,
+                          pos[0]->lock->fix_status == 0));
+      continue;
+    }
 
-	  read_lock = pos+1;
-	  do
-	  {
-	    (last_lock->lock->copy_status)((*read_lock)->status_param,
-					   (*pos)->status_param);
-	  } while (*(read_lock++) != last_lock);
-	  last_lock= (*pos);			/* Point at last write lock */
-	}
-	else
-	  (*last_lock->lock->copy_status)((*pos)->status_param,
-					  last_lock->status_param);
-      }
-      else
-	last_lock=(*pos);
-    } while (pos != data);
+    /*
+      If same table as previous table use pointer to previous status
+      information to ensure that all read/write tables shares same
+      state.
+    */
+    if (first_lock && pos[0]->lock == first_lock[0]->lock)
+      (pos[0]->lock->fix_status)((*first_lock)->status_param,
+                                 (*pos)->status_param);
+    else
+    {
+      /* Different lock, use this as base for next lock */
+      first_lock= pos;
+      (pos[0]->lock->fix_status)((*first_lock)->status_param, 0);
+    }
   }
-#endif
-  DBUG_RETURN(THR_LOCK_SUCCESS);
+  DBUG_VOID_RETURN;
 }
 
-  /* free all locks */
 
-void thr_multi_unlock(THR_LOCK_DATA **data,uint count)
+/* Unlock all locks */
+
+void thr_multi_unlock(THR_LOCK_DATA **data,uint count, uint unlock_flags)
 {
   THR_LOCK_DATA **pos,**end;
   DBUG_ENTER("thr_multi_unlock");
-  DBUG_PRINT("lock",("data: 0x%lx  count: %d", (long) data, count));
+  DBUG_PRINT("lock",("data: 0x%lx  count: %d  flags: %u", (long) data, count,
+                     unlock_flags));
 
   for (pos=data,end=data+count; pos < end ; pos++)
   {
@@ -1084,7 +1169,7 @@ void thr_multi_unlock(THR_LOCK_DATA **da
     fflush(stdout);
 #endif
     if ((*pos)->type != TL_UNLOCK)
-      thr_unlock(*pos);
+      thr_unlock(*pos, unlock_flags);
     else
     {
       DBUG_PRINT("lock",("Free lock: data: 0x%lx  thread: 0x%lx  lock: 0x%lx",
@@ -1400,6 +1485,7 @@ my_bool thr_upgrade_write_delay_lock(THR
                                      enum thr_lock_type new_lock_type)
 {
   THR_LOCK *lock=data->lock;
+  enum enum_thr_lock_result res;
   DBUG_ENTER("thr_upgrade_write_delay_lock");
 
   pthread_mutex_lock(&lock->mutex);
@@ -1420,6 +1506,8 @@ my_bool thr_upgrade_write_delay_lock(THR
       if (lock->get_status)
 	(*lock->get_status)(data->status_param, 0);
       pthread_mutex_unlock(&lock->mutex);
+      if (lock->start_trans)
+	(*lock->start_trans)(data->status_param);
       DBUG_RETURN(0);
     }
 
@@ -1440,7 +1528,10 @@ my_bool thr_upgrade_write_delay_lock(THR
   {
     check_locks(lock,"waiting for lock",0);
   }
-  DBUG_RETURN(wait_for_lock(&lock->write_wait,data,1));
+  res= wait_for_lock(&lock->write_wait,data,1);
+  if (res == THR_LOCK_SUCCESS && lock->start_trans)
+    DBUG_RETURN((*lock->start_trans)(data->status_param));
+  DBUG_RETURN(0);
 }
 
 
@@ -1684,7 +1775,7 @@ static void *test_thread(void *arg)
       }
     }
     pthread_mutex_unlock(&LOCK_thread_count);
-    thr_multi_unlock(multi_locks,lock_counts[param]);
+    thr_multi_unlock(multi_locks,lock_counts[param], THR_UNLOCK_UPDATE_STATUS);
   }
 
   printf("Thread %s (%d) ended\n",my_thread_name(),param); fflush(stdout);

=== modified file 'sql/handler.cc'
--- a/sql/handler.cc	2010-10-20 10:58:43 +0000
+++ b/sql/handler.cc	2010-11-02 15:22:57 +0000
@@ -2631,8 +2631,18 @@ void handler::print_keydup_error(uint ke
     - table->s->path
     - table->alias
 */
+
+#ifndef DBUG_OFF
+#define SET_FATAL_ERROR fatal_error=1
+#else
+#define SET_FATAL_ERROR
+#endif
+
 void handler::print_error(int error, myf errflag)
 {
+#ifndef DBUG_OFF
+  bool fatal_error= 0;
+#endif
   DBUG_ENTER("handler::print_error");
   DBUG_PRINT("enter",("error: %d",error));
 
@@ -2650,6 +2660,13 @@ void handler::print_error(int error, myf
   case HA_ERR_KEY_NOT_FOUND:
   case HA_ERR_NO_ACTIVE_RECORD:
   case HA_ERR_END_OF_FILE:
+    /*
+      This errors is not not normally fatal (for example for reads). However
+      if you get it during an update or delete, then its fatal.
+      As the user is calling print_error() (which is not done on read), we
+      assume something when wrong with the update or delete.
+    */
+    SET_FATAL_ERROR;
     textno=ER_KEY_NOT_FOUND;
     break;
   case HA_ERR_WRONG_MRG_TABLE_DEF:
@@ -2701,21 +2718,26 @@ void handler::print_error(int error, myf
     textno=ER_DUP_UNIQUE;
     break;
   case HA_ERR_RECORD_CHANGED:
+    SET_FATAL_ERROR;
     textno=ER_CHECKREAD;
     break;
   case HA_ERR_CRASHED:
+    SET_FATAL_ERROR;
     textno=ER_NOT_KEYFILE;
     break;
   case HA_ERR_WRONG_IN_RECORD:
+    SET_FATAL_ERROR;
     textno= ER_CRASHED_ON_USAGE;
     break;
   case HA_ERR_CRASHED_ON_USAGE:
+    SET_FATAL_ERROR;
     textno=ER_CRASHED_ON_USAGE;
     break;
   case HA_ERR_NOT_A_TABLE:
     textno= error;
     break;
   case HA_ERR_CRASHED_ON_REPAIR:
+    SET_FATAL_ERROR;
     textno=ER_CRASHED_ON_REPAIR;
     break;
   case HA_ERR_OUT_OF_MEM:
@@ -2814,7 +2836,10 @@ void handler::print_error(int error, myf
 	if (temporary)
 	  my_error(ER_GET_TEMPORARY_ERRMSG, MYF(0), error, str.ptr(), engine);
 	else
+        {
+          SET_FATAL_ERROR;
 	  my_error(ER_GET_ERRMSG, MYF(0), error, str.ptr(), engine);
+        }
       }
       else
 	my_error(ER_GET_ERRNO,errflag,error);
@@ -2822,6 +2847,7 @@ void handler::print_error(int error, myf
     }
   }
   my_error(textno, errflag, table_share->table_name.str, error);
+  DBUG_ASSERT(!fatal_error || !debug_assert_if_crashed_table);
   DBUG_VOID_RETURN;
 }
 

=== modified file 'sql/lock.cc'
--- a/sql/lock.cc	2010-02-10 19:06:24 +0000
+++ b/sql/lock.cc	2010-11-02 15:22:57 +0000
@@ -211,7 +211,8 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd,
   for (;;)
   {
     if (! (sql_lock= get_lock_data(thd, tables, count, GET_LOCK_STORE_LOCKS,
-                                   &write_lock_used)))
+                                   &write_lock_used)) ||
+        ! sql_lock->table_count)
       break;
 
     if (global_read_lock && write_lock_used &&
@@ -257,8 +258,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd,
 
     thd_proc_info(thd, "System lock");
     DBUG_PRINT("info", ("thd->proc_info %s", thd->proc_info));
-    if (sql_lock->table_count && lock_external(thd, sql_lock->table,
-                                               sql_lock->table_count))
+    if (lock_external(thd, sql_lock->table, sql_lock->table_count))
     {
       /* Clear the lock type of all lock data to avoid reusage. */
       reset_lock_data(sql_lock);
@@ -279,8 +279,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd,
                                                      thd->lock_id)];
     if (rc > 1)                                 /* a timeout or a deadlock */
     {
-      if (sql_lock->table_count)
-        VOID(unlock_external(thd, sql_lock->table, sql_lock->table_count));
+      VOID(unlock_external(thd, sql_lock->table, sql_lock->table_count));
       my_error(rc, MYF(0));
       my_free((uchar*) sql_lock,MYF(0));
       sql_lock= 0;
@@ -388,7 +387,7 @@ void mysql_unlock_tables(THD *thd, MYSQL
   if (sql_lock->table_count)
     VOID(unlock_external(thd,sql_lock->table,sql_lock->table_count));
   if (sql_lock->lock_count)
-    thr_multi_unlock(sql_lock->locks,sql_lock->lock_count);
+    thr_multi_unlock(sql_lock->locks,sql_lock->lock_count, 0);
   my_free((uchar*) sql_lock,MYF(0));
   DBUG_VOID_RETURN;
 }
@@ -418,25 +417,8 @@ void mysql_unlock_read_tables(THD *thd,
   uint i,found;
   DBUG_ENTER("mysql_unlock_read_tables");
 
-  /* Move all write locks first */
-  THR_LOCK_DATA **lock=sql_lock->locks;
-  for (i=found=0 ; i < sql_lock->lock_count ; i++)
-  {
-    if (sql_lock->locks[i]->type >= TL_WRITE_ALLOW_READ)
-    {
-      swap_variables(THR_LOCK_DATA *, *lock, sql_lock->locks[i]);
-      lock++;
-      found++;
-    }
-  }
-  /* unlock the read locked tables */
-  if (i != found)
-  {
-    thr_multi_unlock(lock,i-found);
-    sql_lock->lock_count= found;
-  }
+  /* Call external lock for all tables to be unlocked */
 
-  /* Then do the same for the external locks */
   /* Move all write locked tables first */
   TABLE **table=sql_lock->table;
   for (i=found=0 ; i < sql_lock->table_count ; i++)
@@ -455,6 +437,27 @@ void mysql_unlock_read_tables(THD *thd,
     VOID(unlock_external(thd,table,i-found));
     sql_lock->table_count=found;
   }
+
+  /* Call thr_unlock() for all tables to be unlocked */
+
+  /* Move all write locks first */
+  THR_LOCK_DATA **lock=sql_lock->locks;
+  for (i=found=0 ; i < sql_lock->lock_count ; i++)
+  {
+    if (sql_lock->locks[i]->type >= TL_WRITE_ALLOW_READ)
+    {
+      swap_variables(THR_LOCK_DATA *, *lock, sql_lock->locks[i]);
+      lock++;
+      found++;
+    }
+  }
+  /* unlock the read locked tables */
+  if (i != found)
+  {
+    thr_multi_unlock(lock, i-found, 0);
+    sql_lock->lock_count= found;
+  }
+
   /* Fix the lock positions in TABLE */
   table= sql_lock->table;
   found= 0;
@@ -582,8 +585,21 @@ void mysql_lock_abort(THD *thd, TABLE *t
   if ((locked= get_lock_data(thd, &table, 1, GET_LOCK_UNLOCK,
                              &write_lock_used)))
   {
-    for (uint i=0; i < locked->lock_count; i++)
-      thr_abort_locks(locked->locks[i]->lock, upgrade_lock);
+    if (table->children_attached)
+    {
+      /*
+        Don't abort locks for underlying tables just because merge table
+        is deleted. Doing would cause anyone accessing these tables to
+        spin in open_table/close_table forever until lock is released.
+      */
+      thr_multi_unlock(locked->locks, locked->lock_count,
+                       THR_UNLOCK_UPDATE_STATUS);
+    }
+    else
+    {
+      for (uint i=0; i < locked->lock_count; i++)
+        thr_abort_locks(locked->locks[i]->lock, upgrade_lock);
+    }
     my_free((uchar*) locked,MYF(0));
   }
   DBUG_VOID_RETURN;
@@ -624,21 +640,36 @@ bool mysql_lock_abort_for_thread(THD *th
 }
 
 
+/**
+  Merge two thr_lock:s
+  mysql_lock_merge()
+
+  @param a	Original locks
+  @param b	New locks
+
+  @retval	New lock structure that contains a and b
+
+  @note
+  a and b are freed with my_free()
+*/
+
 MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b)
 {
   MYSQL_LOCK *sql_lock;
   TABLE **table, **end_table;
   DBUG_ENTER("mysql_lock_merge");
+  DBUG_PRINT("enter", ("a->lock_count: %u  b->lock_count: %u",
+                       a->lock_count, b->lock_count));
 
   if (!(sql_lock= (MYSQL_LOCK*)
 	my_malloc(sizeof(*sql_lock)+
-		  sizeof(THR_LOCK_DATA*)*(a->lock_count+b->lock_count)+
+		  sizeof(THR_LOCK_DATA*)*((a->lock_count+b->lock_count)*2) +
 		  sizeof(TABLE*)*(a->table_count+b->table_count),MYF(MY_WME))))
     DBUG_RETURN(0);				// Fatal error
   sql_lock->lock_count=a->lock_count+b->lock_count;
   sql_lock->table_count=a->table_count+b->table_count;
   sql_lock->locks=(THR_LOCK_DATA**) (sql_lock+1);
-  sql_lock->table=(TABLE**) (sql_lock->locks+sql_lock->lock_count);
+  sql_lock->table=(TABLE**) (sql_lock->locks+sql_lock->lock_count*2);
   memcpy(sql_lock->locks,a->locks,a->lock_count*sizeof(*a->locks));
   memcpy(sql_lock->locks+a->lock_count,b->locks,
 	 b->lock_count*sizeof(*b->locks));
@@ -659,6 +690,18 @@ MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK
     (*table)->lock_data_start+= a->lock_count;
   }
 
+  /*
+    Ensure that locks of the same tables share same data structures if we
+    reopen a table that is already open. This can happen for example with
+    MERGE tables.
+  */
+
+  /* Copy the lock data array. thr_merge_lock() reorders its content */
+  memcpy(sql_lock->locks + sql_lock->lock_count, sql_lock->locks,
+         sql_lock->lock_count * sizeof(*sql_lock->locks));
+  thr_merge_locks(sql_lock->locks + sql_lock->lock_count,
+                  a->lock_count, b->lock_count);
+
   /* Delete old, not needed locks */
   my_free((uchar*) a,MYF(0));
   my_free((uchar*) b,MYF(0));
@@ -832,7 +875,7 @@ static MYSQL_LOCK *get_lock_data(THD *th
 
   /*
     Allocating twice the number of pointers for lock data for use in
-    thr_mulit_lock(). This function reorders the lock data, but cannot
+    thr_multi_lock(). This function reorders the lock data, but cannot
     update the table values. So the second part of the array is copied
     from the first part immediately before calling thr_multi_lock().
   */
@@ -1062,11 +1105,13 @@ int lock_table_name(THD *thd, TABLE_LIST
 
 void unlock_table_name(THD *thd, TABLE_LIST *table_list)
 {
+  DBUG_ENTER("unlock_table_name");
   if (table_list->table)
   {
     hash_delete(&open_cache, (uchar*) table_list->table);
     broadcast_refresh();
   }
+  DBUG_VOID_RETURN;
 }
 
 

=== modified file 'sql/mysql_priv.h'
--- a/sql/mysql_priv.h	2010-09-03 16:20:30 +0000
+++ b/sql/mysql_priv.h	2010-11-02 15:22:57 +0000
@@ -1989,7 +1989,7 @@ extern my_bool opt_slave_compressed_prot
 extern ulong slave_exec_mode_options;
 extern my_bool opt_readonly, lower_case_file_system;
 extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs;
-extern my_bool opt_secure_auth;
+extern my_bool opt_secure_auth, debug_assert_if_crashed_table;
 extern char* opt_secure_file_priv;
 extern my_bool opt_log_slow_admin_statements, opt_log_slow_slave_statements;
 extern my_bool sp_automatic_privileges, opt_noacl;

=== modified file 'sql/mysqld.cc'
--- a/sql/mysqld.cc	2010-10-20 19:42:17 +0000
+++ b/sql/mysqld.cc	2010-11-02 15:22:57 +0000
@@ -455,7 +455,7 @@ static pthread_cond_t COND_thread_cache,
 /* Global variables */
 
 bool opt_update_log, opt_bin_log, opt_ignore_builtin_innodb= 0;
-my_bool opt_log, opt_slow_log;
+my_bool opt_log, opt_slow_log, debug_assert_if_crashed_table;
 ulong log_output_options;
 my_bool opt_log_queries_not_using_indexes= 0;
 bool opt_error_log= IF_WIN(1,0);
@@ -5962,7 +5962,7 @@ enum options_mysqld
   OPT_SECURE_FILE_PRIV,
   OPT_MIN_EXAMINED_ROW_LIMIT,
   OPT_LOG_SLOW_SLAVE_STATEMENTS,
-  OPT_DEBUG_CRC, OPT_DEBUG_ON, OPT_OLD_MODE,
+  OPT_DEBUG_CRC, OPT_DEBUG_ON, OPT_DEBUG_ASSERT_IF_CRASHED_TABLE, OPT_OLD_MODE,
   OPT_TEST_IGNORE_WRONG_OPTIONS, OPT_TEST_RESTART,
 #if defined(ENABLED_DEBUG_SYNC)
   OPT_DEBUG_SYNC_TIMEOUT,
@@ -6131,6 +6131,10 @@ struct my_option my_long_options[] =
    0, GET_ULONG, REQUIRED_ARG, 0, 0, ~(ulong) 0L, 0, 0, 0},
   {"debug-flush", OPT_DEBUG_FLUSH, "Default debug log with flush after write",
    0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+  {"debug-assert-if-crashed-table", OPT_DEBUG_ASSERT_IF_CRASHED_TABLE,
+   "Do an assert in handler::print_error() if we get a crashed table",
+   &debug_assert_if_crashed_table, &debug_assert_if_crashed_table,
+   0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
 #endif
   {"default-character-set", OPT_DEFAULT_CHARACTER_SET_OLD, 
    "Set the default character set (deprecated option, use --character-set-server instead).",

=== modified file 'sql/parse_file.cc'
--- a/sql/parse_file.cc	2009-06-19 08:24:43 +0000
+++ b/sql/parse_file.cc	2010-11-02 15:22:57 +0000
@@ -216,7 +216,7 @@ sql_create_definition_file(const LEX_STR
   File_option *param;
   DBUG_ENTER("sql_create_definition_file");
   DBUG_PRINT("enter", ("Dir: %s, file: %s, base 0x%lx",
-		       dir ? dir->str : "(null)",
+		       dir ? dir->str : "",
                        file_name->str, (ulong) base));
 
   if (dir)

=== modified file 'sql/set_var.cc'
--- a/sql/set_var.cc	2010-08-27 14:12:44 +0000
+++ b/sql/set_var.cc	2010-11-02 15:22:57 +0000
@@ -4341,7 +4341,7 @@ bool sys_var_thd_dbug::update(THD *thd,
 
 uchar *sys_var_thd_dbug::value_ptr(THD *thd, enum_var_type type, LEX_STRING *b)
 {
-  char buf[256];
+  char buf[1024];
   if (type == OPT_GLOBAL)
   {
     DBUG_EXPLAIN_INITIAL(buf, sizeof(buf));

=== modified file 'sql/sql_base.cc'
--- a/sql/sql_base.cc	2010-09-03 16:20:30 +0000
+++ b/sql/sql_base.cc	2010-11-02 15:22:57 +0000
@@ -2032,6 +2032,8 @@ static void unlink_open_merge(THD *thd,
         Remove parent from open_tables list and close it.
         This includes detaching and hence clearing parent references.
       */
+      DBUG_PRINT("info", ("Closing parent to '%s'.'%s'",
+                          table->s->db.str, table->s->table_name.str));
       close_thread_table(thd, prv_p);
     }
   }
@@ -3061,8 +3063,9 @@ bool reopen_table(TABLE *table)
   TABLE_LIST table_list;
   THD *thd= table->in_use;
   DBUG_ENTER("reopen_table");
-  DBUG_PRINT("tcache", ("table: '%s'.'%s' 0x%lx", table->s->db.str,
-                        table->s->table_name.str, (long) table));
+  DBUG_PRINT("tcache", ("table: '%s'.'%s'  table: 0x%lx  share: 0x%lx",
+                        table->s->db.str, table->s->table_name.str,
+                        (long) table, (long) table->s));
 
   DBUG_ASSERT(table->s->ref_count == 0);
   DBUG_ASSERT(!table->sort.io_cache);
@@ -3349,7 +3352,8 @@ bool reopen_tables(THD *thd, bool get_lo
       merge_table_found= TRUE;
     if (!tables || (!db_stat && reopen_table(table)))
     {
-      my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias);
+      my_error(ER_CANT_REOPEN_TABLE, MYF(0),
+               table->alias ? table->alias : table->s->table_name.str);
       /*
         If we could not allocate 'tables', we may close open tables
         here. If a MERGE table is affected, detach the children first.
@@ -3359,9 +3363,10 @@ bool reopen_tables(THD *thd, bool get_lo
         that they cannot be moved into the unused_tables chain with
         these pointers set.
       */
-      if (table->child_l || table->parent)
-        detach_merge_children(table, TRUE);
-      VOID(hash_delete(&open_cache,(uchar*) table));
+      unlink_open_table(thd, table, 0);
+      /* Restart loop */
+      prev= &thd->open_tables;
+      next= *prev;
       error=1;
     }
     else
@@ -3396,7 +3401,7 @@ bool reopen_tables(THD *thd, bool get_lo
   }
   DBUG_PRINT("tcache", ("open tables to lock: %u",
                         (uint) (tables_ptr - tables)));
-  if (tables != tables_ptr)			// Should we get back old locks
+  if (tables != tables_ptr)  // Should we get back old locks
   {
     MYSQL_LOCK *lock;
     /*
@@ -3545,7 +3550,7 @@ bool table_is_used(TABLE *table, bool wa
     char *key= table->s->table_cache_key.str;
     uint key_length= table->s->table_cache_key.length;
 
-    DBUG_PRINT("loop", ("table_name: %s", table->alias));
+    DBUG_PRINT("loop", ("table_name: %s", table->alias ? table->alias : ""));
     HASH_SEARCH_STATE state;
     for (TABLE *search= (TABLE*) hash_first(&open_cache, (uchar*) key,
                                              key_length, &state);
@@ -3883,6 +3888,7 @@ static int open_unireg_entry(THD *thd, T
   int error;
   TABLE_SHARE *share;
   uint discover_retry_count= 0;
+  bool locked_table;
   DBUG_ENTER("open_unireg_entry");
 
   safe_mutex_assert_owner(&LOCK_open);
@@ -4003,8 +4009,10 @@ retry:
     }
     if (!entry->s || !entry->s->crashed)
       goto err;
-     // Code below is for repairing a crashed file
-     if ((error= lock_table_name(thd, table_list, TRUE)))
+
+    // Code below is for repairing a crashed file
+    locked_table= table_list->table != 0;
+    if (! locked_table && (error= lock_table_name(thd, table_list, TRUE)))
      {
        if (error < 0)
  	goto err;
@@ -4038,12 +4046,13 @@ retry:
      else
        thd->clear_error();			// Clear error message
      pthread_mutex_lock(&LOCK_open);
-     unlock_table_name(thd, table_list);
+     if (!locked_table)
+       unlock_table_name(thd, table_list);
  
      if (error)
        goto err;
      break;
-   }
+  }
 
   if (Table_triggers_list::check_n_load(thd, share->db.str,
                                         share->table_name.str, entry, 0))
@@ -4289,7 +4298,7 @@ void detach_merge_children(TABLE *table,
     children attached yet. Also this is called for every child and the
     parent from close_thread_tables().
   */
-  if ((first_detach= parent->children_attached))
+  if (parent->children_attached)
   {
     VOID(parent->file->extra(HA_EXTRA_DETACH_CHILDREN));
     parent->children_attached= FALSE;
@@ -4301,38 +4310,50 @@ void detach_merge_children(TABLE *table,
 
   if (clear_refs)
   {
-    /* In any case clear the own parent reference. (***) */
-    table->parent= NULL;
+    if (table->parent)
+    {
+      /* In any case clear the own parent reference. (***) */
+      table->parent= NULL;
+      table->file->extra(HA_EXTRA_DETACH_CHILD);
+    }
 
     /*
-      On the first detach, clear all references. If this table is the
-      parent, we still may need to clear the child references. The first
-      detach might not have done this.
+      Clear all references. If this table is the parent, we still may
+      need to clear the child references. The first detach might not
+      have done this.
     */
-    if (first_detach || (table == parent))
+    for (child_l= parent->child_l; ; child_l= child_l->next_global)
     {
-      /* Clear TABLE references to force new assignment at next open. */
-      for (child_l= parent->child_l; ; child_l= child_l->next_global)
+      /*
+        Do not DBUG_ASSERT(child_l->table); open_tables might be
+        incomplete or we may have been called twice.
+
+        Clear the parent reference of the children only on the first
+        detach. The children might already be closed. They will clear
+        it themselves when this function is called for them with
+        'clear_refs' true. See above "(***)".
+      */
+      if (child_l->table)
       {
+        if (child_l->table->parent)
+        {
+          child_l->table->parent= NULL;
+          if (child_l->table->db_stat)
+            child_l->table->file->extra(HA_EXTRA_DETACH_CHILD);
+        }
         /*
-          Do not DBUG_ASSERT(child_l->table); open_tables might be
-          incomplete.
-
-          Clear the parent reference of the children only on the first
-          detach. The children might already be closed. They will clear
-          it themseves when this function is called for them with
-          'clear_refs' true. See above "(***)".
+          Set alias to "" to ensure that table is not used if we are in
+          LOCK TABLES
         */
-        if (first_detach && child_l->table)
-          child_l->table->parent= NULL;
+        ((char*) child_l->table->alias)[0]= 0;
 
         /* Clear the table reference to force new assignment at next open. */
         child_l->table= NULL;
-
-        /* Break when this was the last child. */
-        if (&child_l->next_global == parent->child_last_l)
-          break;
       }
+
+      /* Break when this was the last child. */
+      if (&child_l->next_global == parent->child_last_l)
+        break;
     }
   }
 
@@ -5129,9 +5150,11 @@ bool open_normal_and_derived_tables(THD
 
 static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table)
 {
+  DBUG_ENTER("mark_real_tables_as_free_for_reuse");
   for (; table; table= table->next_global)
     if (!table->placeholder())
       table->table->query_id= 0;
+  DBUG_VOID_RETURN;
 }
 
 

=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc	2010-08-27 14:12:44 +0000
+++ b/sql/sql_class.cc	2010-11-02 15:22:57 +0000
@@ -273,7 +273,7 @@ const char *set_thd_proc_info(THD *thd,
 
   const char *old_info= thd->proc_info;
   DBUG_PRINT("proc_info", ("%s:%d  %s", calling_file, calling_line,
-                           (info != NULL) ? info : "(null)"));
+                           (info != NULL) ? info : ""));
 #if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER)
   thd->profiling.status_change(info, calling_function, calling_file, calling_line);
 #endif

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2010-10-19 13:58:35 +0000
+++ b/sql/sql_parse.cc	2010-11-02 15:22:57 +0000
@@ -7999,6 +7999,8 @@ bool parse_sql(THD *thd,
                Object_creation_ctx *creation_ctx)
 {
   bool mysql_parse_status;
+  DBUG_ENTER("parse_sql");
+
   DBUG_ASSERT(thd->m_parser_state == NULL);
 
   /* Backup creation context. */
@@ -8032,7 +8034,7 @@ bool parse_sql(THD *thd,
 
   /* That's it. */
 
-  return mysql_parse_status || thd->is_fatal_error;
+  DBUG_RETURN(mysql_parse_status || thd->is_fatal_error);
 }
 
 /**

=== modified file 'sql/sql_trigger.cc'
--- a/sql/sql_trigger.cc	2010-09-03 16:20:30 +0000
+++ b/sql/sql_trigger.cc	2010-11-02 15:22:57 +0000
@@ -498,9 +498,6 @@ bool mysql_create_or_drop_trigger(THD *t
     thd->in_lock_tables= 1;
     if (reopen_tables(thd, 1, 1))
     {
-      /* To be safe remove this table from the set of LOCKED TABLES */
-      unlink_open_table(thd, tables->table, FALSE);
-
       /*
         Ignore reopen_tables errors for now. It's better not leave master/slave
         in a inconsistent state.

=== modified file 'storage/maria/ma_bitmap.c'
--- a/storage/maria/ma_bitmap.c	2010-10-07 09:51:34 +0000
+++ b/storage/maria/ma_bitmap.c	2010-11-02 15:22:57 +0000
@@ -1016,7 +1016,11 @@ static my_bool allocate_tail(MARIA_FILE_
   DBUG_PRINT("enter", ("size: %u", size));
 
   LINT_INIT(best_pos);
-  DBUG_ASSERT(size <= MAX_TAIL_SIZE(bitmap->block_size));
+  /*
+    We have to add DIR_ENTRY_SIZE here as this is not part of the data size
+    See call to allocate_tail() in find_tail().
+  */
+  DBUG_ASSERT(size <= MAX_TAIL_SIZE(bitmap->block_size) + DIR_ENTRY_SIZE);
 
   for (; data < end; data += 6)
   {

=== modified file 'storage/maria/ma_blockrec.c'
--- a/storage/maria/ma_blockrec.c	2010-09-10 20:27:26 +0000
+++ b/storage/maria/ma_blockrec.c	2010-11-02 15:22:57 +0000
@@ -2823,6 +2823,10 @@ static my_bool write_block_record(MARIA_
     data+= diff_length;
     head_length= share->base.min_block_length;
   }
+  /*
+    If this is a redo entry (ie, undo_lsn != LSN_ERROR) then we should have
+    written exactly head_length bytes (same as original record).
+  */
   DBUG_ASSERT(undo_lsn == LSN_ERROR || head_length == row_pos->length);
   int2store(row_pos->dir + 2, head_length);
   /* update empty space at start of block */
@@ -7161,6 +7165,7 @@ my_bool _ma_apply_undo_row_update(MARIA_
     header+= HA_CHECKSUM_STORE_SIZE;
   }
   length_on_head_page= uint2korr(header);
+  set_if_bigger(length_on_head_page, share->base.min_block_length);
   header+= 2;
   extent_count= pagerange_korr(header);
   header+= PAGERANGE_STORE_SIZE;

=== modified file 'storage/maria/ma_blockrec.h'
--- a/storage/maria/ma_blockrec.h	2010-09-10 20:27:26 +0000
+++ b/storage/maria/ma_blockrec.h	2010-11-02 15:22:57 +0000
@@ -281,7 +281,8 @@ my_bool write_hook_for_commit(enum trans
                               TRN *trn, MARIA_HA *tbl_info, LSN *lsn,
                               void *hook_arg);
 void _ma_block_get_status(void *param, my_bool concurrent_insert);
-void _ma_block_get_status_no_versioning(void *param, my_bool concurrent_ins);
+my_bool _ma_block_start_trans(void* param);
+my_bool _ma_block_start_trans_no_versioning(void *param);
 void _ma_block_update_status(void *param);
 void _ma_block_restore_status(void *param);
 my_bool _ma_block_check_status(void *param);

=== modified file 'storage/maria/ma_locking.c'
--- a/storage/maria/ma_locking.c	2010-02-10 19:06:24 +0000
+++ b/storage/maria/ma_locking.c	2010-11-02 15:22:57 +0000
@@ -63,7 +63,7 @@ int maria_lock_database(MARIA_HA *info,
       {
 	count= --share->w_locks;
         if (share->lock.update_status)
-          (*share->lock.update_status)(info);
+          _ma_update_status_with_lock(info);
       }
       --share->tot_locks;
       if (info->lock_type == F_WRLCK && !share->w_locks)

=== modified file 'storage/maria/ma_open.c'
--- a/storage/maria/ma_open.c	2010-09-10 20:27:26 +0000
+++ b/storage/maria/ma_open.c	2010-11-02 15:22:57 +0000
@@ -874,8 +874,8 @@ MARIA_HA *maria_open(const char *name, i
           share->have_versioning= 1;
           share->row_is_visible=     _ma_row_visible_transactional_table;
           share->lock.get_status=    _ma_block_get_status;
-          share->lock.update_status= _ma_block_update_status;
           share->lock.check_status=  _ma_block_check_status;
+          share->lock.start_trans=   _ma_block_start_trans;
           /*
             We can for the moment only allow multiple concurrent inserts
             only if there is no auto-increment key.  To lift this restriction
@@ -903,7 +903,7 @@ MARIA_HA *maria_open(const char *name, i
       else if (share->now_transactional)
       {
         DBUG_ASSERT(share->data_file_type == BLOCK_RECORD);
-        share->lock.get_status=     _ma_block_get_status_no_versioning;
+        share->lock.start_trans=    _ma_block_start_trans_no_versioning;
       }
     }
 #endif

=== modified file 'storage/maria/ma_state.c'
--- a/storage/maria/ma_state.c	2010-08-07 14:42:30 +0000
+++ b/storage/maria/ma_state.c	2010-11-02 15:22:57 +0000
@@ -335,6 +335,25 @@ void _ma_update_status(void* param)
 }
 
 
+/*
+  Same as ma_update_status() but take a lock in the table lock, to protect
+  against someone calling ma_get_status() from thr_lock() at the same time.
+*/
+
+void _ma_update_status_with_lock(MARIA_HA *info)
+{
+  my_bool locked= 0;
+  if (info->state == &info->state_save)
+  {
+    locked= 1;
+    pthread_mutex_lock(&info->s->lock.mutex);
+  }
+  (*info->s->lock.update_status)(info);
+  if (locked)
+    pthread_mutex_unlock(&info->s->lock.mutex);
+}
+
+
 void _ma_restore_status(void *param)
 {
   MARIA_HA *info= (MARIA_HA*) param;
@@ -585,7 +604,13 @@ void _ma_block_get_status(void* param, m
   {
     DBUG_ASSERT(info->lock.type != TL_WRITE_CONCURRENT_INSERT);
   }
+  DBUG_VOID_RETURN;
+}
 
+
+my_bool _ma_block_start_trans(void* param)
+{
+  MARIA_HA *info=(MARIA_HA*) param;
   if (info->s->lock_key_trees)
   {
     /*
@@ -593,24 +618,22 @@ void _ma_block_get_status(void* param, m
       out of memory conditions)
       TODO: Fix this by having one extra state pre-allocated
     */
-    (void) _ma_setup_live_state(info);
+    return _ma_setup_live_state(info);
   }
-  else
+
+  /*
+    Info->trn is set if this table is already handled and we are
+    called from maria_versioning()
+  */
+  if (info->s->base.born_transactional && !info->trn)
   {
     /*
-      Info->trn is set if this table is already handled and we are
-      called from maria_versioning()
+      Assume for now that this doesn't fail (It can only fail in
+      out of memory conditions)
     */
-    if (info->s->base.born_transactional && !info->trn)
-    {
-      /*
-        Assume for now that this doesn't fail (It can only fail in
-        out of memory conditions)
-      */
-      (void) maria_create_trn_hook(info);
-    }
+    return maria_create_trn_hook(info) != 0;
   }
-  DBUG_VOID_RETURN;
+  return 0;
 }
 
 
@@ -639,13 +662,10 @@ my_bool _ma_block_check_status(void *par
 
 /* Get status when transactional but not versioned */
 
-void _ma_block_get_status_no_versioning(void* param,
-                                        my_bool concurrent_insert
-                                        __attribute__((unused)))
+my_bool _ma_block_start_trans_no_versioning(void* param)
 {
   MARIA_HA *info=(MARIA_HA*) param;
   DBUG_ENTER("_ma_block_get_status_no_version");
-  DBUG_PRINT("enter", ("concurrent_insert %d", concurrent_insert));
   DBUG_ASSERT(info->s->base.born_transactional);
 
   info->state->changed= 0;              /* from _ma_reset_update_flag() */
@@ -655,9 +675,9 @@ void _ma_block_get_status_no_versioning(
       Assume for now that this doesn't fail (It can only fail in
       out of memory conditions)
     */
-    (void) maria_create_trn_hook(info);
+    DBUG_RETURN(maria_create_trn_hook(info));
   }
-  DBUG_VOID_RETURN;
+  DBUG_RETURN(0);
 }
 
 

=== modified file 'storage/maria/ma_state.h'
--- a/storage/maria/ma_state.h	2008-12-22 00:17:37 +0000
+++ b/storage/maria/ma_state.h	2010-11-02 15:22:57 +0000
@@ -62,6 +62,7 @@ MARIA_STATE_HISTORY *_ma_remove_not_visi
 void _ma_reset_state(MARIA_HA *info);
 void _ma_get_status(void* param, my_bool concurrent_insert);
 void _ma_update_status(void* param);
+void _ma_update_status_with_lock(MARIA_HA *info);
 void _ma_restore_status(void *param);
 void _ma_copy_status(void* to, void *from);
 void _ma_reset_update_flag(void *param, my_bool concurrent_insert);

=== modified file 'storage/maria/ma_write.c'
--- a/storage/maria/ma_write.c	2010-10-13 15:15:43 +0000
+++ b/storage/maria/ma_write.c	2010-11-02 15:22:57 +0000
@@ -678,7 +678,6 @@ static int w_search(register MARIA_HA *i
     }
     else /* not HA_FULLTEXT, normal HA_NOSAME key */
     {
-      DBUG_PRINT("warning", ("Duplicate key"));
       /*
         TODO
         When the index will support true versioning - with multiple
@@ -696,6 +695,12 @@ static int w_search(register MARIA_HA *i
       info->dup_key_trid= _ma_trid_from_key(&tmp_key);
       info->dup_key_pos= dup_key_pos;
       my_errno= HA_ERR_FOUND_DUPP_KEY;
+      DBUG_PRINT("warning",
+                 ("Duplicate key. dup_key_trid: %lu  pos %lu  visible: %d",
+                  (ulong) info->dup_key_trid,
+                  (ulong) info->dup_key_pos,
+                  info->trn ? trnman_can_read_from(info->trn,
+                                                   info->dup_key_trid) : 2));
       goto err;
     }
   }

=== modified file 'storage/myisam/mi_check.c'
--- a/storage/myisam/mi_check.c	2010-04-28 12:52:24 +0000
+++ b/storage/myisam/mi_check.c	2010-11-02 15:22:57 +0000
@@ -1337,7 +1337,7 @@ int chk_data_link(HA_CHECK *param, MI_IN
   if (splits != info->s->state.split)
   {
     mi_check_print_warning(param,
-			   "Found %10s key parts. Should be: %s",
+			   "Found %10s parts. Should be: %s",
 			   llstr(splits,llbuff),
 			   llstr(info->s->state.split,llbuff2));
   }

=== modified file 'storage/myisam/mi_extra.c'
--- a/storage/myisam/mi_extra.c	2010-04-28 12:52:24 +0000
+++ b/storage/myisam/mi_extra.c	2010-11-02 15:22:57 +0000
@@ -390,6 +390,11 @@ int mi_extra(MI_INFO *info, enum ha_extr
     share->is_log_table= TRUE;
     pthread_mutex_unlock(&share->intern_lock);
     break;
+  case HA_EXTRA_DETACH_CHILD: /* When used with MERGE tables */
+    info->open_flag&=     ~HA_OPEN_MERGE_TABLE;
+    info->lock.priority&= ~THR_LOCK_MERGE_PRIV;
+    break;
+    
   case HA_EXTRA_KEY_CACHE:
   case HA_EXTRA_NO_KEY_CACHE:
   default:

=== modified file 'storage/myisam/mi_locking.c'
--- a/storage/myisam/mi_locking.c	2010-08-27 14:12:44 +0000
+++ b/storage/myisam/mi_locking.c	2010-11-02 15:22:57 +0000
@@ -22,6 +22,8 @@
 
 #include "ftdefs.h"
 
+static void mi_update_status_with_lock(MI_INFO *info);
+
 	/* lock table by F_UNLCK, F_RDLCK or F_WRLCK */
 
 int mi_lock_database(MI_INFO *info, int lock_type)
@@ -62,7 +64,7 @@ int mi_lock_database(MI_INFO *info, int
       else
       {
 	count= --share->w_locks;
-        mi_update_status(info);
+        mi_update_status_with_lock(info);
       }
       --share->tot_locks;
       if (info->lock_type == F_WRLCK && !share->w_locks &&
@@ -244,7 +246,7 @@ int mi_lock_database(MI_INFO *info, int
        a crash on windows if the table is renamed and 
        later on referenced by the merge table.
      */
-    if( info->owned_by_merge && (info->s)->kfile < 0 )
+    if ((info->open_flags & HA_OPEN_MERGE_TABLE) && (info->s)->kfile < 0)
     {
       error = HA_ERR_NO_SUCH_TABLE;
     }
@@ -273,9 +275,11 @@ void mi_get_status(void* param, my_bool
 {
   MI_INFO *info=(MI_INFO*) param;
   DBUG_ENTER("mi_get_status");
-  DBUG_PRINT("info",("key_file: %ld  data_file: %ld  concurrent_insert: %d",
-		     (long) info->s->state.state.key_file_length,
-		     (long) info->s->state.state.data_file_length,
+  DBUG_PRINT("info",("name: %s  key_file: %lu  data_file: %lu  rows: %lu  concurrent_insert: %d",
+                     info->s->index_file_name,
+		     (ulong) info->s->state.state.key_file_length,
+		     (ulong) info->s->state.state.data_file_length,
+		     (ulong) info->s->state.state.records,
                      concurrent_insert));
 #ifndef DBUG_OFF
   if (info->state->key_file_length > info->s->state.state.key_file_length ||
@@ -306,9 +310,11 @@ void mi_update_status(void* param)
   if (info->state == &info->save_state)
   {
 #ifndef DBUG_OFF
-    DBUG_PRINT("info",("updating status:  key_file: %ld  data_file: %ld",
-		       (long) info->state->key_file_length,
-		       (long) info->state->data_file_length));
+    DBUG_PRINT("info",
+               ("updating status:  key_file: %lu  data_file: %lu  rows: %lu",
+                (ulong) info->state->key_file_length,
+                (ulong) info->state->data_file_length,
+                (ulong) info->state->records));
     if (info->state->key_file_length < info->s->state.state.key_file_length ||
 	info->state->data_file_length < info->s->state.state.data_file_length)
       DBUG_PRINT("warning",("old info:  key_file: %ld  data_file: %ld",
@@ -342,6 +348,24 @@ void mi_update_status(void* param)
   DBUG_VOID_RETURN;
 }
 
+/*
+  Same as mi_update_status() but take a lock in the table lock, to protect
+  against someone calling mi_get_status() from thr_lock() at the same time.
+*/
+
+static void mi_update_status_with_lock(MI_INFO *info)
+{
+  my_bool locked= 0;
+  if (info->state == &info->save_state)
+  {
+    locked= 1;
+    pthread_mutex_lock(&info->s->lock.mutex);
+  }
+  mi_update_status(info);
+  if (locked)
+    pthread_mutex_unlock(&info->s->lock.mutex);
+}
+
 
 void mi_restore_status(void *param)
 {
@@ -407,6 +431,32 @@ my_bool mi_check_status(void *param)
 }
 
 
+/**
+  Fix status for thr_lock_merge()
+
+  @param  org_table
+  @param  new_table that should point on org_lock.  new_table is 0
+          in case this is the first occurence of the table in the lock
+          structure.
+*/
+
+void mi_fix_status(MI_INFO *org_table, MI_INFO *new_table)
+{
+  DBUG_ENTER("mi_fix_status");
+  if (!new_table)
+  {
+    /* First in group. Set state as in mi_get_status() */
+    org_table->state= &org_table->save_state;
+  }
+  else
+  {
+    /* Set new_table to use state from org_table (first lock of this table) */
+    new_table->state= org_table->state;
+  }
+  DBUG_VOID_RETURN;
+}
+
+
 /****************************************************************************
  ** functions to read / write the state
 ****************************************************************************/

=== modified file 'storage/myisam/mi_open.c'
--- a/storage/myisam/mi_open.c	2010-08-27 14:12:44 +0000
+++ b/storage/myisam/mi_open.c	2010-11-02 15:22:57 +0000
@@ -119,7 +119,7 @@ MI_INFO *mi_open(const char *name, int m
                                                  dflt_key_cache);
 
     DBUG_EXECUTE_IF("myisam_pretend_crashed_table_on_open",
-                    if (strstr(name, "/t1"))
+                    if (strstr(name, "/crashed"))
                     {
                       my_errno= HA_ERR_CRASHED;
                       goto err;
@@ -556,6 +556,7 @@ MI_INFO *mi_open(const char *name, int m
 	share->lock.update_status=mi_update_status;
         share->lock.restore_status= mi_restore_status;
 	share->lock.check_status=mi_check_status;
+        share->lock.fix_status= (void (*)(void *, void *)) mi_fix_status;
       }
     }
 #endif
@@ -606,6 +607,7 @@ MI_INFO *mi_open(const char *name, int m
   info.s=share;
   info.lastpos= HA_OFFSET_ERROR;
   info.update= (short) (HA_STATE_NEXT_FOUND+HA_STATE_PREV_FOUND);
+  info.open_flag= open_flags;
   info.opt_flag=READ_CHECK_USED;
   info.this_unique= (ulong) info.dfile; /* Uniq number in process */
   if (share->data_file_type == COMPRESSED_RECORD)

=== modified file 'storage/myisam/myisamdef.h'
--- a/storage/myisam/myisamdef.h	2010-04-28 12:52:24 +0000
+++ b/storage/myisam/myisamdef.h	2010-11-02 15:22:57 +0000
@@ -274,7 +274,9 @@ struct st_myisam_info
   */
   ulong packed_length, blob_length;     /* Length of found, packed record */
   int dfile;                            /* The datafile */
+  uint open_flag;                       /* Parameters for open */
   uint opt_flag;                        /* Optim. for space/speed */
+  uint once_flags;                      /* For MYISAMMRG */
   uint update;                          /* If file changed since open */
   int lastinx;                          /* Last used index */
   uint lastkey_length;                  /* Length of key in lastkey */
@@ -300,10 +302,6 @@ struct st_myisam_info
   my_bool page_changed;
   /* If info->buff has to be reread for rnext */
   my_bool buff_used;
-  my_bool once_flags;                   /* For MYISAMMRG */
-#ifdef __WIN__
-  my_bool owned_by_merge;                       /* This MyISAM table is part of a merge union */
-#endif
 #ifdef THREAD
   THR_LOCK_DATA lock;
 #endif
@@ -712,6 +710,7 @@ void mi_update_status(void *param);
 void mi_restore_status(void *param);
 void mi_copy_status(void *to, void *from);
 my_bool mi_check_status(void *param);
+void mi_fix_status(MI_INFO *org_table, MI_INFO *new_table);
 void mi_disable_non_unique_index(MI_INFO *info, ha_rows rows);
 
 extern MI_INFO *test_if_reopen(char *filename);

=== modified file 'storage/myisammrg/ha_myisammrg.cc'
--- a/storage/myisammrg/ha_myisammrg.cc	2010-08-27 14:12:44 +0000
+++ b/storage/myisammrg/ha_myisammrg.cc	2010-11-02 15:22:57 +0000
@@ -86,8 +86,6 @@
 
   On parent open the storage engine structures are allocated and initialized.
   They stay with the open table until its final close.
-
-
 */
 
 #ifdef USE_PRAGMA_IMPLEMENTATION
@@ -1070,6 +1068,8 @@ THR_LOCK_DATA **ha_myisammrg::store_lock
        open_table != file->end_table ;
        open_table++)
   {
+    open_table->table->lock.priority|= THR_LOCK_MERGE_PRIV;
+
     *(to++)= &open_table->table->lock;
     if (lock_type != TL_IGNORE && open_table->table->lock.type == TL_UNLOCK)
       open_table->table->lock.type=lock_type;

=== modified file 'storage/myisammrg/myrg_locking.c'
--- a/storage/myisammrg/myrg_locking.c	2007-07-25 14:56:17 +0000
+++ b/storage/myisammrg/myrg_locking.c	2010-11-02 15:22:57 +0000
@@ -27,15 +27,8 @@ int myrg_lock_database(MYRG_INFO *info,
   error=0;
   for (file=info->open_tables ; file != info->end_table ; file++) 
   {
-#ifdef __WIN__
-    /*
-      Make sure this table is marked as owned by a merge table.
-      The semaphore is never released as long as table remains
-      in memory. This should be refactored into a more generic
-      approach (observer pattern)
-     */
-    (file->table)->owned_by_merge = TRUE;
-#endif
+    DBUG_ASSERT(file->table->open_flag & HA_OPEN_MERGE_TABLE);
+
     if ((new_error=mi_lock_database(file->table,lock_type)))
     {
       error=new_error;

=== modified file 'storage/myisammrg/myrg_open.c'
--- a/storage/myisammrg/myrg_open.c	2010-08-27 14:12:44 +0000
+++ b/storage/myisammrg/myrg_open.c	2010-11-02 15:22:57 +0000
@@ -27,8 +27,9 @@
 	if handle_locking is 0 then exit with error if some table is locked
 	if handle_locking is 1 then wait if table is locked
 
-        NOTE: This function is not used in the MySQL server. It is for
-        MERGE use independent from MySQL. Currently there is some code
+        NOTE: This function is only used in the MySQL server when a
+        table is cloned. It is also used for usage of MERGE
+        independent from MySQL. Currently there is some code
         duplication between myrg_open() and myrg_parent_open() +
         myrg_attach_children(). Please duplicate changes in these
         functions or make common sub-functions.
@@ -93,7 +94,8 @@ MYRG_INFO *myrg_open(const char *name, i
     }
     else
       fn_format(buff, buff, "", "", 0);
-    if (!(isam=mi_open(buff,mode,(handle_locking?HA_OPEN_WAIT_IF_LOCKED:0))))
+    if (!(isam=mi_open(buff,mode,(handle_locking?HA_OPEN_WAIT_IF_LOCKED:0) |
+                       HA_OPEN_MERGE_TABLE)))
     {
       if (handle_locking & HA_OPEN_FOR_REPAIR)
       {
@@ -430,6 +432,8 @@ int myrg_attach_children(MYRG_INFO *m_in
     m_info->open_tables[child_nr].table= myisam;
     m_info->open_tables[child_nr].file_offset= (my_off_t) file_offset;
     file_offset+= myisam->state->data_file_length;
+    /* Mark as MERGE table */
+    myisam->open_flag|= HA_OPEN_MERGE_TABLE;
 
     /* Check table definition match. */
     if (m_info->reclength != myisam->s->base.reclength)

=== modified file 'storage/xtradb/buf/buf0buf.c'
--- a/storage/xtradb/buf/buf0buf.c	2010-10-19 12:16:15 +0000
+++ b/storage/xtradb/buf/buf0buf.c	2010-11-02 15:22:57 +0000
@@ -1116,7 +1116,7 @@ init_again:
 			if (shm_info->buf_pool_backup.LRU_old)
 				shm_info->buf_pool_backup.LRU_old =
 					(buf_page_t*)((byte*)(shm_info->buf_pool_backup.LRU_old)
-						+ (((void*)shm_info->buf_pool_backup.LRU_old > previous_frame_address)
+						+ (((byte*)shm_info->buf_pool_backup.LRU_old > previous_frame_address)
 						  ? logi_offset : blocks_offset));
 
 			UT_LIST_OFFSET(unzip_LRU, buf_block_t, shm_info->buf_pool_backup.unzip_LRU,

=== modified file 'storage/xtradb/buf/buf0lru.c'
--- a/storage/xtradb/buf/buf0lru.c	2010-10-19 12:16:15 +0000
+++ b/storage/xtradb/buf/buf0lru.c	2010-11-02 15:22:57 +0000
@@ -2265,7 +2265,7 @@ buf_LRU_file_restore(void)
 	ulint		req = 0;
 	ibool		terminated = FALSE;
 	ibool		ret = FALSE;
-	dump_record_t*	records;
+	dump_record_t*	records= 0;
 	ulint		size;
 	ulint		size_high;
 	ulint		length;



More information about the commits mailing list