[Commits] bzr commit into Mariadb 5.2, with Maria 2.0:maria/5.2 branch (monty:2844)

Michael Widenius monty at askmonty.org
Fri Nov 5 12:38:02 EET 2010


#At lp:maria/5.2 based on revid:monty at askmonty.org-20101105095442-06oopuj8tbhn7kdr

 2844 Michael Widenius	2010-11-05 [merge]
      Automerge with 5.2
      added:
        mysql-test/r/merge_debug.result
        mysql-test/r/warnings_debug.result
        mysql-test/t/merge_debug.test
        mysql-test/t/warnings_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/protocol.cc
        sql/set_var.cc
        sql/sql_base.cc
        sql/sql_class.cc
        sql/sql_class.h
        sql/sql_error.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_dbug.c
        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/maria/maria_def.h
        storage/myisam/mi_check.c
        storage/myisam/mi_dbug.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
        storage/xtradb/include/os0sync.h
        storage/xtradb/include/srv0srv.h
        storage/xtradb/log/log0log.c
        storage/xtradb/os/os0sync.c
        storage/xtradb/srv/srv0srv.c

=== modified file 'client/mysql.cc'
--- a/client/mysql.cc	2010-10-01 15:27:32 +0000
+++ b/client/mysql.cc	2010-11-04 14:53:10 +0000
@@ -4091,7 +4091,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 21:18:02 +0000
+++ b/client/mysqltest.cc	2010-11-04 15:08:28 +0000
@@ -1255,15 +1255,6 @@ void die(const char *fmt, ...)
   DBUG_ENTER("die");
   DBUG_PRINT("enter", ("start_lineno: %d", start_lineno));
 
-  /*
-    Protect against dying twice
-    first time 'die' is called, try to write log files
-    second time, just exit
-  */
-  if (dying)
-    cleanup_and_exit(1);
-  dying= 1;
-
   /* Print the error message */
   fprintf(stderr, "mysqltest: ");
   if (cur_file && cur_file != file_stack)
@@ -1282,6 +1273,15 @@ void die(const char *fmt, ...)
   fprintf(stderr, "\n");
   fflush(stderr);
 
+  /*
+    Protect against dying twice
+    first time 'die' is called, try to write log files
+    second time, just exit
+  */
+  if (dying)
+    cleanup_and_exit(1);
+  dying= 1;
+
   log_file.show_tail(opt_tail_lines);
 
   /*
@@ -1876,7 +1876,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-03 19:02:23 +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
 
@@ -1826,11 +1826,11 @@ FILE *_db_fp_(void)
 
 BOOLEAN _db_keyword_(CODE_STATE *cs, const char *keyword, int strict)
 {
+  int match= strict ? INCLUDE : INCLUDE|MATCHED;
   get_code_state_if_not_set_or_return FALSE;
-  strict=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-05 19:56:11 +0000
+++ b/include/my_base.h	2010-11-04 14:53:10 +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-25 13:21:16 +0000
+++ b/mysql-test/mysql-test-run.pl	2010-11-04 14:53:10 +0000
@@ -4281,6 +4281,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-06-26 10:05:41 +0000
+++ b/mysql-test/r/merge.result	2010-11-05 10:37:51 +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";

=== added file 'mysql-test/r/warnings_debug.result'
--- a/mysql-test/r/warnings_debug.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/r/warnings_debug.result	2010-11-03 21:40:53 +0000
@@ -0,0 +1,10 @@
+drop table if exists t1;
+create table t1 (a int primary key) engine=innodb;
+SET SESSION debug="+d,warn_during_ha_commit_trans";
+INSERT INTO t1 VALUES (1);
+Warnings:
+Warning	1196	Some non-transactional changed tables couldn't be rolled back
+SHOW WARNINGS;
+Level	Code	Message
+Warning	1196	Some non-transactional changed tables couldn't be rolled back
+drop table t1;

=== 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";

=== added file 'mysql-test/t/warnings_debug.test'
--- a/mysql-test/t/warnings_debug.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/warnings_debug.test	2010-11-03 21:40:53 +0000
@@ -0,0 +1,19 @@
+--source include/have_innodb.inc
+--source include/have_debug.inc
+
+--disable_warnings
+drop table if exists t1;
+--enable_warnings
+
+create table t1 (a int primary key) engine=innodb;
+
+# Test that warnings produced during autocommit (after calling
+# set_ok_status()) are still reported to the client.
+SET SESSION debug="+d,warn_during_ha_commit_trans";
+INSERT INTO t1 VALUES (1);
+# The warning will be shown automatically by mysqltest; there was a bug where
+# this didn't happen because the warning was not counted when sending result
+# packet. Show the warnings manually also.
+SHOW WARNINGS;
+
+drop table t1;

=== modified file 'mysys/my_getopt.c'
--- a/mysys/my_getopt.c	2010-10-25 13:21:16 +0000
+++ b/mysys/my_getopt.c	2010-11-04 14:53:10 +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-03 12:14:02 +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()
@@ -379,6 +406,7 @@ void thr_lock_data_init(THR_LOCK *lock,T
   data->owner= 0;                               /* no owner yet */
   data->status_param=param;
   data->cond=0;
+  data->priority= 0;
 }
 
 
@@ -530,7 +558,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 +572,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 +837,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 +861,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 +1011,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 +1041,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 +1064,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 +1170,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 +1486,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 +1507,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 +1529,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 +1776,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-28 17:04:23 +0000
+++ b/sql/handler.cc	2010-11-05 10:37:51 +0000
@@ -1094,6 +1094,12 @@ int ha_commit_trans(THD *thd, bool all)
   my_xid xid= thd->transaction.xid_state.xid.get_my_xid();
   DBUG_ENTER("ha_commit_trans");
 
+  /* Just a random warning to test warnings pushed during autocommit. */
+  DBUG_EXECUTE_IF("warn_during_ha_commit_trans",
+    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+                 ER_WARNING_NOT_COMPLETE_ROLLBACK,
+                 ER(ER_WARNING_NOT_COMPLETE_ROLLBACK)););
+
   /*
     We must not commit the normal transaction if a statement
     transaction is pending. Otherwise statement transaction
@@ -2643,8 +2649,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));
 
@@ -2662,6 +2678,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:
@@ -2713,21 +2736,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:
@@ -2826,7 +2854,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);
@@ -2834,6 +2865,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-11-02 09:12:29 +0000
+++ b/sql/mysql_priv.h	2010-11-05 10:37:51 +0000
@@ -2057,7 +2057,7 @@ extern ulong slave_exec_mode_options;
 extern my_bool opt_readonly, lower_case_file_system;
 extern my_bool opt_userstat_running;
 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-11-02 09:12:29 +0000
+++ b/sql/mysqld.cc	2010-11-05 10:37:51 +0000
@@ -488,7 +488,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;
 my_bool opt_userstat_running;
 ulong log_output_options;
 my_bool opt_log_queries_not_using_indexes= 0;
@@ -6037,7 +6037,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,
@@ -6207,6 +6207,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/protocol.cc'
--- a/sql/protocol.cc	2010-09-11 18:43:48 +0000
+++ b/sql/protocol.cc	2010-11-04 15:08:28 +0000
@@ -203,7 +203,7 @@ net_send_ok(THD *thd,
   NET *net= &thd->net;
   uchar buff[MYSQL_ERRMSG_SIZE+10],*pos;
   bool error= FALSE;
-  DBUG_ENTER("my_ok");
+  DBUG_ENTER("net_send_ok");
 
   if (! net->vio)	// hack for re-parsing queries
   {

=== modified file 'sql/set_var.cc'
--- a/sql/set_var.cc	2010-10-10 14:18:11 +0000
+++ b/sql/set_var.cc	2010-11-05 10:37:51 +0000
@@ -4369,7 +4369,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-10-10 14:18:11 +0000
+++ b/sql/sql_base.cc	2010-11-05 10:37:51 +0000
@@ -2045,6 +2045,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);
     }
   }
@@ -3076,8 +3078,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);
@@ -3364,7 +3367,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.
@@ -3374,9 +3378,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
@@ -3411,7 +3416,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;
     /*
@@ -3560,7 +3565,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);
@@ -3898,6 +3903,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);
@@ -4018,8 +4024,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;
@@ -4053,12 +4061,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))
@@ -4286,7 +4295,6 @@ void detach_merge_children(TABLE *table,
 {
   TABLE_LIST *child_l;
   TABLE *parent= table->child_l ? table : table->parent;
-  bool first_detach;
   DBUG_ENTER("detach_merge_children");
   /*
     Either table->child_l or table->parent must be set. Parent must have
@@ -4304,7 +4312,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;
@@ -4316,38 +4324,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;
     }
   }
 
@@ -5144,9 +5164,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-11-02 09:03:33 +0000
+++ b/sql/sql_class.cc	2010-11-05 10:37:51 +0000
@@ -328,7 +328,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_class.h'
--- a/sql/sql_class.h	2010-10-28 17:04:23 +0000
+++ b/sql/sql_class.h	2010-11-05 10:37:51 +0000
@@ -1313,6 +1313,13 @@ public:
     return m_total_warn_count;
   }
 
+  /* Used to count any warnings pushed after calling set_ok_status(). */
+  void increment_warning()
+  {
+    if (m_status != DA_EMPTY)
+      m_total_warn_count++;
+  }
+
   Diagnostics_area() { reset_diagnostics_area(); }
 
 private:

=== modified file 'sql/sql_error.cc'
--- a/sql/sql_error.cc	2009-02-13 16:41:47 +0000
+++ b/sql/sql_error.cc	2010-11-03 21:40:53 +0000
@@ -159,6 +159,8 @@ MYSQL_ERROR *push_warning(THD *thd, MYSQ
   }
   thd->warn_count[(uint) level]++;
   thd->total_warn_count++;
+  /* Make sure we also count warnings pushed after calling set_ok_status(). */
+  thd->main_da.increment_warning();
   DBUG_RETURN(err);
 }
 

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2010-10-28 17:04:23 +0000
+++ b/sql/sql_parse.cc	2010-11-05 10:37:51 +0000
@@ -8014,6 +8014,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. */
@@ -8047,7 +8049,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-03 12:14:02 +0000
@@ -227,7 +227,8 @@ my_bool _ma_bitmap_init(MARIA_SHARE *sha
     The +1 is to add the bitmap page, as this doesn't have to be covered
   */
   bitmap->pages_covered= aligned_bit_blocks * 16 + 1;
-  bitmap->flush_all_requested= bitmap->non_flushable= 0;
+  bitmap->flush_all_requested= 0;
+  bitmap->non_flushable= 0;
 
   /* Update size for bits */
   /* TODO; Make this dependent of the row size */
@@ -311,8 +312,8 @@ my_bool _ma_bitmap_flush(MARIA_SHARE *sh
     pthread_mutex_lock(&share->bitmap.bitmap_lock);
     if (share->bitmap.changed)
     {
-      share->bitmap.changed= 0;
       res= write_changed_bitmap(share, &share->bitmap);
+      share->bitmap.changed= 0;
     }
     pthread_mutex_unlock(&share->bitmap.bitmap_lock);
   }
@@ -355,7 +356,7 @@ my_bool _ma_bitmap_flush_all(MARIA_SHARE
   pthread_mutex_lock(&bitmap->bitmap_lock);
   if (bitmap->changed || bitmap->changed_not_flushed)
   {
-    bitmap->flush_all_requested= TRUE;
+    bitmap->flush_all_requested++;
 #ifndef WRONG_BITMAP_FLUSH
     while (bitmap->non_flushable > 0)
     {
@@ -363,6 +364,7 @@ my_bool _ma_bitmap_flush_all(MARIA_SHARE
       pthread_cond_wait(&bitmap->bitmap_cond, &bitmap->bitmap_lock);
     }
 #endif
+    DBUG_ASSERT(bitmap->flush_all_requested == 1);
     /*
       Bitmap is in a flushable state: its contents in memory are reflected by
       log records (complete REDO-UNDO groups) and all bitmap pages are
@@ -391,7 +393,7 @@ my_bool _ma_bitmap_flush_all(MARIA_SHARE
         PCFLUSH_PINNED_AND_ERROR)
       res= TRUE;
     bitmap->changed_not_flushed= FALSE;
-    bitmap->flush_all_requested= FALSE;
+    bitmap->flush_all_requested--;
     /*
       Some well-behaved threads may be waiting for flush_all_requested to
       become false, wake them up.
@@ -405,6 +407,70 @@ my_bool _ma_bitmap_flush_all(MARIA_SHARE
 
 
 /**
+   @brief Lock bitmap from being used by another thread
+
+   @fn _ma_bitmap_lock()
+   @param  share               Table's share
+
+   @notes
+   This is a temporary solution for allowing someone to delete an inserted
+   duplicate-key row while someone else is doing concurrent inserts.
+   This is ok for now as duplicate key errors are not that common.
+
+   In the future we will add locks for row-pages to ensure two threads doesn't
+   work at the same time on the same page.
+*/
+
+void _ma_bitmap_lock(MARIA_SHARE *share)
+{
+  MARIA_FILE_BITMAP *bitmap= &share->bitmap;
+  DBUG_ENTER("_ma_bitmap_lock");
+
+  if (!share->now_transactional)
+    DBUG_VOID_RETURN;
+
+  pthread_mutex_lock(&bitmap->bitmap_lock);
+  bitmap->flush_all_requested++;
+  while (bitmap->non_flushable)
+  {
+    DBUG_PRINT("info", ("waiting for bitmap to be flushable"));
+    pthread_cond_wait(&bitmap->bitmap_cond, &bitmap->bitmap_lock);
+  }
+  /*
+    Ensure that _ma_bitmap_flush_all() and _ma_bitmap_lock() are blocked.
+    ma_bitmap_flushable() is blocked thanks to 'flush_all_requested'.
+  */
+  bitmap->non_flushable= 1;
+  pthread_mutex_unlock(&bitmap->bitmap_lock);
+  DBUG_VOID_RETURN;
+}
+  
+/**
+   @brief Unlock bitmap after _ma_bitmap_lock()
+
+   @fn _ma_bitmap_unlock()
+   @param  share               Table's share
+*/
+
+void _ma_bitmap_unlock(MARIA_SHARE *share)
+{
+  MARIA_FILE_BITMAP *bitmap= &share->bitmap;
+  DBUG_ENTER("_ma_bitmap_unlock");
+
+  if (!share->now_transactional)
+    DBUG_VOID_RETURN;
+  DBUG_ASSERT(bitmap->flush_all_requested > 0 && bitmap->non_flushable == 1);
+
+  pthread_mutex_lock(&bitmap->bitmap_lock);
+  bitmap->flush_all_requested--;
+  bitmap->non_flushable= 0;
+  pthread_mutex_unlock(&bitmap->bitmap_lock);
+  pthread_cond_broadcast(&bitmap->bitmap_cond);
+  DBUG_VOID_RETURN;
+}
+
+
+/**
   @brief Unpin all pinned bitmap pages
 
   @param  share            Table's share
@@ -633,11 +699,12 @@ static void _ma_print_bitmap_changes(MAR
 {
   uchar *pos, *end, *org_pos;
   ulong page;
+  DBUG_ENTER("_ma_print_bitmap_changes");
 
   end= bitmap->map + bitmap->used_size;
   DBUG_LOCK_FILE;
-  fprintf(DBUG_FILE,"\nBitmap page changes at page %lu\n",
-          (ulong) bitmap->page);
+  fprintf(DBUG_FILE,"\nBitmap page changes at page: %lu  bitmap: 0x%lx\n",
+          (ulong) bitmap->page, (long) bitmap->map);
 
   page= (ulong) bitmap->page+1;
   for (pos= bitmap->map, org_pos= bitmap->map + bitmap->block_size ;
@@ -666,6 +733,7 @@ static void _ma_print_bitmap_changes(MAR
   fputc('\n', DBUG_FILE);
   DBUG_UNLOCK_FILE;
   memcpy(bitmap->map + bitmap->block_size, bitmap->map, bitmap->block_size);
+  DBUG_VOID_RETURN;
 }
 
 
@@ -877,6 +945,7 @@ static void fill_block(MARIA_FILE_BITMAP
 {
   uint page, offset, tmp;
   uchar *data;
+  DBUG_ENTER("fill_block");
 
   /* For each 6 bytes we have 6*8/3= 16 patterns */
   page= ((uint) (best_data - bitmap->map)) / 6 * 16 + best_pos;
@@ -902,6 +971,7 @@ static void fill_block(MARIA_FILE_BITMAP
   int2store(data, tmp);
   bitmap->changed= 1;
   DBUG_EXECUTE("bitmap", _ma_print_bitmap_changes(bitmap););
+  DBUG_VOID_RETURN;
 }
 
 
@@ -1016,7 +1086,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)
   {
@@ -1510,6 +1584,8 @@ static void use_head(MARIA_HA *info, pgc
   MARIA_BITMAP_BLOCK *block;
   uchar *data;
   uint offset, tmp, offset_page;
+  DBUG_ENTER("use_head");
+
   DBUG_ASSERT(page % bitmap->pages_covered);
 
   block= dynamic_element(&info->bitmap_blocks, block_position,
@@ -1532,6 +1608,7 @@ static void use_head(MARIA_HA *info, pgc
   int2store(data, tmp);
   bitmap->changed= 1;
   DBUG_EXECUTE("bitmap", _ma_print_bitmap_changes(bitmap););
+  DBUG_VOID_RETURN;
 }
 
 

=== 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-04 14:57:30 +0000
@@ -1993,19 +1993,6 @@ static my_bool write_tail(MARIA_HA *info
   /* Keep BLOCKUSED_USE_ORG_BITMAP */
   block->used|= BLOCKUSED_USED | BLOCKUSED_TAIL;
 
-  /* Increase data file size, if extended */
-  position= (my_off_t) block->page * block_size;
-  if (share->state.state.data_file_length <= position)
-  {
-    /*
-      We are modifying a state member before writing the UNDO; this is a WAL
-      violation. But for data_file_length this is ok, as long as we change
-      data_file_length after writing any log record (FILE_ID/REDO/UNDO) (see
-      collect_tables()).
-    */
-    _ma_set_share_data_file_length(share, position + block_size);
-  }
-
   if (block_is_read)
   {
     /* Current page link is last element in pinned_pages */
@@ -2021,17 +2008,33 @@ static my_bool write_tail(MARIA_HA *info
     page_link->unlock= PAGECACHE_LOCK_READ_UNLOCK;
     res= 0;
   }
-  else if (!(res= pagecache_write(share->pagecache,
-                                  &info->dfile, block->page, 0,
-                                  row_pos.buff,share->page_type,
-                                  PAGECACHE_LOCK_READ,
-                                  PAGECACHE_PIN,
-                                  PAGECACHE_WRITE_DELAY, &page_link.link,
-                                  LSN_IMPOSSIBLE)))
+  else
   {
-    page_link.unlock= PAGECACHE_LOCK_READ_UNLOCK;
-    page_link.changed= 1;
-    push_dynamic(&info->pinned_pages, (void*) &page_link);
+    if (!(res= pagecache_write(share->pagecache,
+                               &info->dfile, block->page, 0,
+                               row_pos.buff,share->page_type,
+                               PAGECACHE_LOCK_READ,
+                               PAGECACHE_PIN,
+                               PAGECACHE_WRITE_DELAY, &page_link.link,
+                               LSN_IMPOSSIBLE)))
+    {
+      page_link.unlock= PAGECACHE_LOCK_READ_UNLOCK;
+      page_link.changed= 1;
+      push_dynamic(&info->pinned_pages, (void*) &page_link);
+    }
+
+    /* Increase data file size, if extended */
+    position= (my_off_t) block->page * block_size;
+    if (share->state.state.data_file_length <= position)
+    {
+      /*
+        We are modifying a state member before writing the UNDO; this is a WAL
+        violation. But for data_file_length this is ok, as long as we change
+        data_file_length after writing any log record (FILE_ID/REDO/UNDO) (see
+        collect_tables()).
+      */
+      _ma_set_share_data_file_length(share, position + block_size);
+    }
   }
   DBUG_RETURN(res);
 }
@@ -2068,7 +2071,7 @@ static my_bool write_full_pages(MARIA_HA
   uint data_size= FULL_PAGE_SIZE(block_size);
   uchar *buff= info->keyread_buff;
   uint page_count, sub_blocks;
-  my_off_t position;
+  my_off_t position, max_position;
   DBUG_ENTER("write_full_pages");
   DBUG_PRINT("enter", ("length: %lu  page: %lu  page_count: %lu",
                        (ulong) length, (ulong) block->page,
@@ -2080,9 +2083,7 @@ static my_bool write_full_pages(MARIA_HA
   page_count= block->page_count;
   sub_blocks= block->sub_blocks;
 
-  position= (my_off_t) (page + page_count) * block_size;
-  if (share->state.state.data_file_length < position)
-    _ma_set_share_data_file_length(share, position);
+  max_position= (my_off_t) (page + page_count) * block_size;
 
   /* Increase data file size, if extended */
 
@@ -2105,8 +2106,7 @@ static my_bool write_full_pages(MARIA_HA
                           (ulong) block->page, (ulong) block->page_count));
 
       position= (page + page_count + 1) * block_size;
-      if (share->state.state.data_file_length < position)
-        _ma_set_share_data_file_length(share, position);
+      set_if_bigger(max_position, position);
     }
     lsn_store(buff, lsn);
     buff[PAGE_TYPE_OFFSET]= (uchar) BLOB_PAGE;
@@ -2134,6 +2134,8 @@ static my_bool write_full_pages(MARIA_HA
     page++;
     DBUG_ASSERT(block->used & BLOCKUSED_USED);
   }
+  if (share->state.state.data_file_length < max_position)
+    _ma_set_share_data_file_length(share, max_position);
   DBUG_RETURN(0);
 }
 
@@ -2823,6 +2825,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 */
@@ -3117,11 +3123,6 @@ static my_bool write_block_record(MARIA_
   }
 #endif
 
-  /* Increase data file size, if extended */
-  position= (my_off_t) head_block->page * block_size;
-  if (share->state.state.data_file_length <= position)
-    _ma_set_share_data_file_length(share, position + block_size);
-
   if (head_block_is_read)
   {
     MARIA_PINNED_PAGE *page_link;
@@ -3150,6 +3151,11 @@ static my_bool write_block_record(MARIA_
     page_link.unlock= PAGECACHE_LOCK_READ_UNLOCK;
     page_link.changed= 1;
     push_dynamic(&info->pinned_pages, (void*) &page_link);
+
+    /* Increase data file size, if extended */
+    position= (my_off_t) head_block->page * block_size;
+    if (share->state.state.data_file_length <= position)
+      _ma_set_share_data_file_length(share, position + block_size);
   }
 
   if (share->now_transactional && (tmp_data_used || blob_full_pages_exists))
@@ -3588,7 +3594,7 @@ my_bool _ma_write_abort_block_record(MAR
   MARIA_SHARE *share= info->s;
   DBUG_ENTER("_ma_write_abort_block_record");
 
-  _ma_bitmap_flushable(info, 1);
+  _ma_bitmap_lock(share);  /* Lock bitmap from other insert threads */
   if (delete_head_or_tail(info,
                           ma_recordpos_to_page(info->cur_row.lastpos),
                           ma_recordpos_to_dir_entry(info->cur_row.lastpos), 1,
@@ -3626,7 +3632,7 @@ my_bool _ma_write_abort_block_record(MAR
                       &lsn, (void*) 0))
       res= 1;
   }
-  _ma_bitmap_flushable(info, -1);
+  _ma_bitmap_unlock(share);
   _ma_unpin_all_pages_and_finalize_row(info, lsn);
   DBUG_RETURN(res);
 }
@@ -5158,6 +5164,7 @@ my_bool _ma_scan_init_block_record(MARIA
   info->scan.number_of_rows= 0;
   info->scan.bitmap_pos= info->scan.bitmap_end;
   info->scan.bitmap_page= (pgcache_page_no_t) 0 - share->bitmap.pages_covered;
+  info->scan.max_page= share->state.state.data_file_length / share->block_size;
   /*
     We need to flush what's in memory (bitmap.map) to page cache otherwise, as
     we are going to read bitmaps from page cache in table scan (see
@@ -5359,6 +5366,11 @@ restart_bitmap_scan:
           page= (info->scan.bitmap_page + 1 +
                  (data - info->scan.bitmap_buff) / 6 * 16 + bit_pos - 1);
           info->scan.row_base_page= ma_recordpos(page, 0);
+          if (page >= info->scan.max_page)
+          {
+            DBUG_PRINT("info", ("Found end of file"));
+            DBUG_RETURN((my_errno= HA_ERR_END_OF_FILE));
+          }
           if (!(pagecache_read(share->pagecache,
                                &info->dfile,
                                page, 0, info->scan.page_buff,
@@ -7161,6 +7173,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-03 12:14:02 +0000
@@ -217,6 +217,8 @@ uint _ma_bitmap_get_page_bits(MARIA_HA *
 void _ma_bitmap_delete_all(MARIA_SHARE *share);
 int  _ma_bitmap_create_first(MARIA_SHARE *share);
 void _ma_bitmap_flushable(MARIA_HA *info, int non_flushable_inc);
+void _ma_bitmap_lock(MARIA_SHARE *share);
+void _ma_bitmap_unlock(MARIA_SHARE *share);
 void _ma_bitmap_set_pagecache_callbacks(PAGECACHE_FILE *file,
                                         MARIA_SHARE *share);
 #ifndef DBUG_OFF
@@ -281,7 +283,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_dbug.c'
--- a/storage/maria/ma_dbug.c	2010-08-07 12:27:23 +0000
+++ b/storage/maria/ma_dbug.c	2010-11-04 14:53:10 +0000
@@ -137,6 +137,7 @@ void _ma_print_keydata(FILE *stream, reg
       key=end;
       break;
     }
+#endif
     case HA_KEYTYPE_BIT:
     {
       uint i;
@@ -146,8 +147,6 @@ void _ma_print_keydata(FILE *stream, reg
       key= end;
       break;
     }
-
-#endif
     case HA_KEYTYPE_VARTEXT1:                   /* VARCHAR and TEXT */
     case HA_KEYTYPE_VARTEXT2:                   /* VARCHAR and TEXT */
     case HA_KEYTYPE_VARBINARY1:                 /* VARBINARY and BLOB */

=== 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/maria/maria_def.h'
--- a/storage/maria/maria_def.h	2010-10-10 14:18:11 +0000
+++ b/storage/maria/maria_def.h	2010-11-05 10:37:51 +0000
@@ -246,7 +246,7 @@ typedef struct st_maria_file_bitmap
   uint used_size;                      /* Size of bitmap head that is not 0 */
   my_bool changed;                     /* 1 if page needs to be written */
   my_bool changed_not_flushed;         /* 1 if some bitmap is not flushed */
-  my_bool flush_all_requested;         /**< If _ma_bitmap_flush_all waiting */
+  uint flush_all_requested;            /**< If _ma_bitmap_flush_all waiting */
   uint non_flushable;                  /**< 0 if bitmap and log are in sync */
   PAGECACHE_FILE file;		       /* datafile where bitmap is stored */
 
@@ -476,7 +476,7 @@ typedef struct st_maria_block_scan
 {
   uchar *bitmap_buff, *bitmap_pos, *bitmap_end, *page_buff;
   uchar *dir, *dir_end;
-  pgcache_page_no_t bitmap_page;
+  pgcache_page_no_t bitmap_page, max_page;
   ulonglong bits;
   uint number_of_rows, bit_pos;
   MARIA_RECORD_POS row_base_page;

=== modified file 'storage/myisam/mi_check.c'
--- a/storage/myisam/mi_check.c	2010-06-01 19:52:20 +0000
+++ b/storage/myisam/mi_check.c	2010-11-04 14:53:10 +0000
@@ -1338,7 +1338,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_dbug.c'
--- a/storage/myisam/mi_dbug.c	2010-08-07 12:27:23 +0000
+++ b/storage/myisam/mi_dbug.c	2010-11-04 14:53:10 +0000
@@ -131,6 +131,7 @@ void _mi_print_key(FILE *stream, registe
       key=end;
       break;
     }
+#endif
     case HA_KEYTYPE_BIT:
     {
       uint i;
@@ -140,8 +141,6 @@ void _mi_print_key(FILE *stream, registe
       key= end;
       break;
     }
-
-#endif
     case HA_KEYTYPE_VARTEXT1:                   /* VARCHAR and TEXT */
     case HA_KEYTYPE_VARTEXT2:                   /* VARCHAR and TEXT */
     case HA_KEYTYPE_VARBINARY1:                 /* VARBINARY and BLOB */

=== modified file 'storage/myisam/mi_extra.c'
--- a/storage/myisam/mi_extra.c	2010-06-26 10:05:41 +0000
+++ b/storage/myisam/mi_extra.c	2010-11-05 10:37:51 +0000
@@ -392,6 +392,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-09-11 18:43:48 +0000
+++ b/storage/myisam/mi_locking.c	2010-11-04 14:53:10 +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 &&
@@ -246,7 +248,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_flag & HA_OPEN_MERGE_TABLE) && (info->s)->kfile < 0)
     {
       error = HA_ERR_NO_SUCH_TABLE;
     }
@@ -275,9 +277,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 ||
@@ -308,9 +312,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",
@@ -344,6 +350,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)
 {
@@ -409,6 +433,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-06-26 10:05:41 +0000
+++ b/storage/myisam/myisamdef.h	2010-11-05 10:37:51 +0000
@@ -277,7 +277,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 */
@@ -303,12 +305,8 @@ 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 */
   index_cond_func_t index_cond_func;   /* Index condition function */
   void *index_cond_func_arg;           /* parameter for the func */
-#ifdef __WIN__
-  my_bool owned_by_merge;                       /* This MyISAM table is part of a merge union */
-#endif
 #ifdef THREAD
   THR_LOCK_DATA lock;
 #endif
@@ -717,6 +715,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-09-11 18:43:48 +0000
+++ b/storage/myisammrg/ha_myisammrg.cc	2010-11-04 14:53:10 +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;

=== modified file 'storage/xtradb/include/os0sync.h'
--- a/storage/xtradb/include/os0sync.h	2010-01-06 12:00:14 +0000
+++ b/storage/xtradb/include/os0sync.h	2010-11-03 21:40:53 +0000
@@ -189,7 +189,7 @@ os_event_wait_low(
 
 /**********************************************************//**
 Waits for an event object until it is in the signaled state or
-a timeout is exceeded. In Unix the timeout is always infinite.
+a timeout is exceeded.
 @return	0 if success, OS_SYNC_TIME_EXCEEDED if timeout was exceeded */
 UNIV_INTERN
 ulint

=== modified file 'storage/xtradb/include/srv0srv.h'
--- a/storage/xtradb/include/srv0srv.h	2010-10-19 12:16:15 +0000
+++ b/storage/xtradb/include/srv0srv.h	2010-11-03 21:40:53 +0000
@@ -57,6 +57,9 @@ extern const char	srv_mysql50_table_name
 thread starts running */
 extern os_event_t	srv_lock_timeout_thread_event;
 
+/* This event is set to tell the purge thread to shut down */
+extern os_event_t	srv_purge_thread_event;
+
 /* If the last data file is auto-extended, we add this many pages to it
 at a time */
 #define SRV_AUTO_EXTEND_INCREMENT	\

=== modified file 'storage/xtradb/log/log0log.c'
--- a/storage/xtradb/log/log0log.c	2010-09-03 15:41:57 +0000
+++ b/storage/xtradb/log/log0log.c	2010-11-03 21:40:53 +0000
@@ -3102,6 +3102,7 @@ logs_empty_and_mark_files_at_shutdown(vo
 	algorithm only works if the server is idle at shutdown */
 
 	srv_shutdown_state = SRV_SHUTDOWN_CLEANUP;
+	os_event_set(srv_purge_thread_event);
 loop:
 	os_thread_sleep(100000);
 

=== modified file 'storage/xtradb/os/os0sync.c'
--- a/storage/xtradb/os/os0sync.c	2010-01-06 12:00:14 +0000
+++ b/storage/xtradb/os/os0sync.c	2010-11-03 21:40:53 +0000
@@ -31,6 +31,9 @@ Created 9/6/1995 Heikki Tuuri
 
 #ifdef __WIN__
 #include <windows.h>
+#else
+#include <sys/time.h>
+#include <time.h>
 #endif
 
 #include "ut0mem.h"
@@ -407,14 +410,14 @@ os_event_wait_low(
 
 /**********************************************************//**
 Waits for an event object until it is in the signaled state or
-a timeout is exceeded. In Unix the timeout is always infinite.
+a timeout is exceeded.
 @return	0 if success, OS_SYNC_TIME_EXCEEDED if timeout was exceeded */
 UNIV_INTERN
 ulint
 os_event_wait_time(
 /*===============*/
 	os_event_t	event,	/*!< in: event to wait */
-	ulint		time)	/*!< in: timeout in microseconds, or
+	ulint		wtime)	/*!< in: timeout in microseconds, or
 				OS_SYNC_INFINITE_TIME */
 {
 #ifdef __WIN__
@@ -422,8 +425,8 @@ os_event_wait_time(
 
 	ut_a(event);
 
-	if (time != OS_SYNC_INFINITE_TIME) {
-		err = WaitForSingleObject(event->handle, (DWORD) time / 1000);
+	if (wtime != OS_SYNC_INFINITE_TIME) {
+		err = WaitForSingleObject(event->handle, (DWORD) wtime / 1000);
 	} else {
 		err = WaitForSingleObject(event->handle, INFINITE);
 	}
@@ -439,13 +442,47 @@ os_event_wait_time(
 		return(1000000); /* dummy value to eliminate compiler warn. */
 	}
 #else
-	UT_NOT_USED(time);
+	int	err;
+	int	ret = 0;
+	ulint	tmp;
+	ib_int64_t	old_count;
+	struct timeval tv_start;
+	struct timespec timeout;
+
+	if (wtime == OS_SYNC_INFINITE_TIME) {
+		os_event_wait(event);
+		return 0;
+	}
+
+	/* Compute the absolute point in time at which to time out. */
+	gettimeofday(&tv_start, NULL);
+	tmp = tv_start.tv_usec + wtime;
+	timeout.tv_sec = tv_start.tv_sec + (tmp / 1000000);
+	timeout.tv_nsec = (tmp % 1000000) * 1000;
+
+	os_fast_mutex_lock(&(event->os_mutex));
+	old_count = event->signal_count;
 
-	/* In Posix this is just an ordinary, infinite wait */
+	for (;;) {
+		if (event->is_set == TRUE || event->signal_count != old_count)
+			break;
+
+		err = pthread_cond_timedwait(&(event->cond_var),
+					     &(event->os_mutex), &timeout);
+		if (err == ETIMEDOUT) {
+			ret = OS_SYNC_TIME_EXCEEDED;
+			break;
+		}
+	}
 
-	os_event_wait(event);
+	os_fast_mutex_unlock(&(event->os_mutex));
+
+	if (srv_shutdown_state == SRV_SHUTDOWN_EXIT_THREADS) {
+
+		os_thread_exit(NULL);
+	}
 
-	return(0);
+	return ret;
 #endif
 }
 

=== modified file 'storage/xtradb/srv/srv0srv.c'
--- a/storage/xtradb/srv/srv0srv.c	2010-10-19 12:16:15 +0000
+++ b/storage/xtradb/srv/srv0srv.c	2010-11-03 21:40:53 +0000
@@ -704,6 +704,8 @@ UNIV_INTERN srv_slot_t*	srv_mysql_table
 
 UNIV_INTERN os_event_t	srv_lock_timeout_thread_event;
 
+UNIV_INTERN os_event_t	srv_purge_thread_event;
+
 UNIV_INTERN srv_sys_t*	srv_sys	= NULL;
 
 /* padding to prevent other memory update hotspots from residing on
@@ -1009,6 +1011,7 @@ srv_init(void)
 	}
 
 	srv_lock_timeout_thread_event = os_event_create(NULL);
+	srv_purge_thread_event = os_event_create(NULL);
 
 	for (i = 0; i < SRV_MASTER + 1; i++) {
 		srv_n_threads_active[i] = 0;
@@ -3337,9 +3340,10 @@ loop:
 		mutex_exit(&kernel_mutex);
 
 		sleep_ms = 10;
+		os_event_reset(srv_purge_thread_event);
 	}
 
-	os_thread_sleep( sleep_ms * 1000 );
+	os_event_wait_time(srv_purge_thread_event, sleep_ms * 1000);
 
 	history_len = trx_sys->rseg_history_len;
 	if (history_len > 1000)



More information about the commits mailing list