List:Commits« Previous MessageNext Message »
From:Mattias Jonsson Date:February 20 2009 12:39pm
Subject:bzr commit into mysql-6.0-bugteam branch (mattias.jonsson:3069)
View as plain text  
#At file:///Users/mattiasj/clones/bzrroot/topush-60-bugteam/ based on revid:serge.kozlov@stripped

 3069 Mattias Jonsson	2009-02-20 [merge]
      merge from mysql-5.1-bugteam to mysql-6.0-bugteam

    added:
      mysql-test/include/wait_show_condition.inc
      mysql-test/r/innodb_bug42419.result
      mysql-test/t/innodb_bug42419.test
    modified:
      mysql-test/r/query_cache_debug.result
      mysql-test/suite/parts/r/partition_auto_increment_ndb.result
      mysql-test/t/innodb_mysql.test
      mysql-test/t/partition.test
      mysql-test/t/query_cache_debug.test
      sql/item.cc
      sql/sql_cache.cc
      sql/sql_insert.cc
      sql/sql_select.cc
      storage/myisam/ha_myisam.cc
      storage/myisam/mi_locking.c
      storage/myisam/myisamdef.h
=== added file 'mysql-test/include/wait_show_condition.inc'
--- a/mysql-test/include/wait_show_condition.inc	1970-01-01 00:00:00 +0000
+++ b/mysql-test/include/wait_show_condition.inc	2009-02-20 09:12:06 +0000
@@ -0,0 +1,78 @@
+# include/wait_show_condition.inc
+#
+# SUMMARY
+#
+#    Waits until the show statement ($show_statement) has at least within one of
+#    the rows of the result set for the field ($field) a value which fulfils
+#    a condition ($condition), or the operation times out.
+#    
+#
+# USAGE
+#
+#    let $show_statement= SHOW PROCESSLIST;
+#    let $field= State;
+#    let $condition= = 'Updating';
+#    --source include/wait_show_condition.inc
+#
+#   OR
+#
+#    let $wait_timeout= 60; # Override default of 30 seconds with 60.
+#    let $show_statement= SHOW PROCESSLIST;
+#    let $field= State;
+#    let $condition= = 'Updating';
+#    --source include/wait_show_condition.inc
+#
+# Please do not use this use routine if you can replace the SHOW statement
+# with a select. In such a case include/wait_condition.inc is recommended.
+#
+# Created: 2009-02-18 mleich
+#
+
+let $max_run_time= 30;
+if ($wait_timeout)
+{
+  let $max_run_time= $wait_timeout;
+}
+# Reset $wait_timeout so that its value won't be used on subsequent
+# calls, and default will be used instead.
+let $wait_timeout= 0;
+
+# The smallest timespan till UNIX_TIMESTAMP() gets incremented is ~0 seconds.
+# We add one second to avoid the case that somebody measures timespans on a
+# real clock with fractions of seconds, detects that n seconds are sufficient,
+# assigns n to this routine and suffers because he sometimes gets n - 1
+# seconds in reality.
+inc $max_run_time;
+
+let $found= 0;
+let $max_end_time= `SELECT UNIX_TIMESTAMP() + $max_run_time`;
+while (`SELECT UNIX_TIMESTAMP() <= $max_end_time AND $found = 0`)
+{
+   # Sleep a bit to avoid too heavy load.
+   real_sleep 0.2;
+   let $rowno= 1;
+   let $process_result= 1;
+   while (`SELECT $process_result = 1 AND $found = 0`)
+   {
+      let $field_value= query_get_value($show_statement, $field, $rowno);
+      if (`SELECT '$field_value' $condition`)
+      {
+         let $found= 1;
+      }
+      if (`SELECT '$field_value' = 'No such row'`)
+      {
+         # We are behind the last row of the result set.
+         let $process_result= 0;
+      }
+      inc $rowno;
+   }
+}
+if (!$found)
+{
+  echo # Timeout in include/wait_show_condition.inc for $wait_condition;
+  echo #         show_statement : $show_statement;
+  echo #         field          : $field;
+  echo #         condition      : $condition;
+  echo #         max_run_time   : $max_run_time;
+}
+

=== added file 'mysql-test/r/innodb_bug42419.result'
--- a/mysql-test/r/innodb_bug42419.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/r/innodb_bug42419.result	2009-02-20 09:12:06 +0000
@@ -0,0 +1,17 @@
+CREATE TABLE t1 (a INT AUTO_INCREMENT PRIMARY KEY, b INT) ENGINE = InnoDB;
+INSERT INTO t1 VALUES (1,1),(2,2),(3,3);
+COMMIT;
+SET AUTOCOMMIT = 0;
+CREATE TEMPORARY TABLE t1_tmp ( b INT );
+INSERT INTO t1_tmp (b) SELECT b FROM t1 WHERE a = 3;
+INSERT INTO t1_tmp (b) SELECT b FROM t1 WHERE a = 2;
+SET AUTOCOMMIT = 0;
+CREATE TEMPORARY TABLE t2_tmp ( a int, new_a int );
+INSERT INTO t2_tmp VALUES (1,51),(2,52),(3,53);
+UPDATE t1 SET a = (SELECT new_a FROM t2_tmp WHERE t2_tmp.a = t1.a) WHERE a = 1;
+UPDATE t1 SET a = (SELECT new_a FROM t2_tmp WHERE t2_tmp.a = t1.a) WHERE a = 2;
+INSERT INTO t1_tmp (b) SELECT b FROM t1 WHERE a = 1;
+ERROR 40001: Deadlock found when trying to get lock; try restarting transaction
+Reap the server message for connection user2 UPDATE t1 ...
+UPDATE t1 SET a = (SELECT new_a FROM t2_tmp WHERE t2_tmp.a = t1.a) WHERE a = 3;
+DROP TABLE t1;

=== modified file 'mysql-test/r/query_cache_debug.result'
--- a/mysql-test/r/query_cache_debug.result	2008-04-01 22:43:17 +0000
+++ b/mysql-test/r/query_cache_debug.result	2009-02-19 21:09:35 +0000
@@ -22,3 +22,52 @@ Qcache_queries_in_cache	0
 set global query_cache_size= 0;
 use test;
 drop table t1;
+SET @old_concurrent_insert= @@GLOBAL.concurrent_insert;
+SET @old_query_cache_size= @@GLOBAL.query_cache_size;
+DROP TABLE IF EXISTS t1, t2;
+CREATE TABLE t1 (a INT);
+CREATE TABLE t2 (a INT);
+INSERT INTO t1 VALUES (1),(2),(3);
+SET GLOBAL concurrent_insert= 1;
+SET GLOBAL query_cache_size= 1024*512;
+SET GLOBAL query_cache_type= ON;
+# Switch to connection con1
+SET SESSION debug='+d,wait_after_query_cache_invalidate';
+# Send concurrent insert, will wait in the query cache table invalidate
+INSERT INTO t1 VALUES (4);
+# Switch to connection default
+# Wait for concurrent insert to reach the debug point
+# Switch to connection con2
+# Send SELECT that shouldn't be cached
+SELECT * FROM t1;
+a
+1
+2
+3
+# Switch to connection default
+# Notify the concurrent insert to proceed
+SELECT ID FROM INFORMATION_SCHEMA.PROCESSLIST
+WHERE STATE = 'wait_after_query_cache_invalidate' INTO @thread_id;
+KILL QUERY @thread_id;
+# Switch to connection con1
+# Gather insert result
+SHOW STATUS LIKE "Qcache_queries_in_cache";
+Variable_name	Value
+Qcache_queries_in_cache	0
+# Test that it's cacheable
+SELECT * FROM t1;
+a
+1
+2
+3
+4
+SHOW STATUS LIKE "Qcache_queries_in_cache";
+Variable_name	Value
+Qcache_queries_in_cache	1
+# Disconnect
+# Restore defaults
+RESET QUERY CACHE;
+DROP TABLE t1,t2;
+SET GLOBAL concurrent_insert= DEFAULT;
+SET GLOBAL query_cache_size= DEFAULT;
+SET GLOBAL query_cache_type= DEFAULT;

=== modified file 'mysql-test/suite/parts/r/partition_auto_increment_ndb.result'
--- a/mysql-test/suite/parts/r/partition_auto_increment_ndb.result	2009-02-11 12:11:20 +0000
+++ b/mysql-test/suite/parts/r/partition_auto_increment_ndb.result	2009-02-20 12:37:37 +0000
@@ -649,6 +649,7 @@ INSERT INTO t1 VALUES (3, NULL);
 INSERT INTO t1 VALUES (3, NULL), (2, 0), (2, NULL);
 INSERT INTO t1 VALUES (2, 2);
 # ERROR (only OK if Blackhole/NDB) should give ER_DUP_KEY or ER_DUP_ENTRY
+# mysql_errno: 0
 INSERT INTO t1 VALUES (2, 22), (2, NULL);
 SELECT * FROM t1 ORDER BY c1,c2;
 c1	c2

=== added file 'mysql-test/t/innodb_bug42419.test'
--- a/mysql-test/t/innodb_bug42419.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/innodb_bug42419.test	2009-02-20 09:12:06 +0000
@@ -0,0 +1,77 @@
+#
+# Testcase for InnoDB
+# Bug#42419 Server crash with "Pure virtual method called" on two concurrent connections
+#
+
+--source include/have_innodb.inc
+
+let $innodb_lock_wait_timeout= query_get_value(SHOW VARIABLES LIKE 'innodb_lock_wait_timeout%', Value, 1);
+if (`SELECT $innodb_lock_wait_timeout < 10`)
+{
+   --echo # innodb_lock_wait_timeout must be >= 10 seconds
+   --echo # so that this test can work all time fine on an overloaded testing box
+   SHOW VARIABLES LIKE 'innodb_lock_wait_timeout';
+   exit;
+}
+
+# Save the initial number of concurrent sessions
+--source include/count_sessions.inc
+
+# First session
+connection default;
+
+
+--enable_warnings
+CREATE TABLE t1 (a INT AUTO_INCREMENT PRIMARY KEY, b INT) ENGINE = InnoDB;
+
+INSERT INTO t1 VALUES (1,1),(2,2),(3,3);
+COMMIT;
+SET AUTOCOMMIT = 0;
+
+CREATE TEMPORARY TABLE t1_tmp ( b INT );
+
+INSERT INTO t1_tmp (b) SELECT b FROM t1 WHERE a = 3;
+INSERT INTO t1_tmp (b) SELECT b FROM t1 WHERE a = 2;
+
+# Second session
+connect (user2,localhost,root,,,$MASTER_MYPORT,$MASTER_MYSOCK);
+
+SET AUTOCOMMIT = 0;
+
+CREATE TEMPORARY TABLE t2_tmp ( a int, new_a int );
+INSERT INTO t2_tmp VALUES (1,51),(2,52),(3,53);
+
+UPDATE t1 SET a = (SELECT new_a FROM t2_tmp WHERE t2_tmp.a = t1.a) WHERE a = 1;
+send
+UPDATE t1 SET a = (SELECT new_a FROM t2_tmp WHERE t2_tmp.a = t1.a) WHERE a = 2;
+
+# The last update will wait for a lock held by the first session
+
+# First session
+connection default;
+
+# Poll till the UPDATE of the second session waits for lock
+let $show_statement= SHOW PROCESSLIST;
+let $field= State;
+let $condition= = 'Updating';
+--source include/wait_show_condition.inc
+
+# If the testing box is overloadeded and innodb_lock_wait_timeout is too small
+# we might get here ER_LOCK_WAIT_TIMEOUT.
+--error ER_LOCK_DEADLOCK
+INSERT INTO t1_tmp (b) SELECT b FROM t1 WHERE a = 1;
+
+# Second session
+connection user2;
+--echo Reap the server message for connection user2 UPDATE t1 ...
+reap;
+
+# The server crashed when executing this UPDATE or the succeeding SQL command.
+UPDATE t1 SET a = (SELECT new_a FROM t2_tmp WHERE t2_tmp.a = t1.a) WHERE a = 3;
+
+connection default;
+disconnect user2;
+DROP TABLE t1;
+
+# Wait till all disconnects are completed
+--source include/wait_until_count_sessions.inc

=== modified file 'mysql-test/t/innodb_mysql.test'
--- a/mysql-test/t/innodb_mysql.test	2009-02-12 18:27:05 +0000
+++ b/mysql-test/t/innodb_mysql.test	2009-02-20 12:37:37 +0000
@@ -91,6 +91,7 @@ INSERT INTO foo2 SELECT * FROM foo;
 
 DROP TABLE foo, bar, foo2;
 
+
 #
 # Bug#41348: INSERT INTO tbl SELECT * FROM temp_tbl overwrites locking type of temp table
 #

=== modified file 'mysql-test/t/partition.test'
--- a/mysql-test/t/partition.test	2009-02-13 16:30:54 +0000
+++ b/mysql-test/t/partition.test	2009-02-20 12:37:37 +0000
@@ -19,7 +19,6 @@ drop table if exists t1, t2;
 #
 # Bug#36001: Partitions: spelling and using some error messages
 #
-
 --error ER_FOREIGN_KEY_ON_PARTITIONED
 CREATE TABLE t1 (a INT, FOREIGN KEY (a) REFERENCES t0 (a))
 ENGINE=MyISAM

=== modified file 'mysql-test/t/query_cache_debug.test'
--- a/mysql-test/t/query_cache_debug.test	2008-10-20 09:16:47 +0000
+++ b/mysql-test/t/query_cache_debug.test	2009-02-20 12:37:37 +0000
@@ -45,3 +45,71 @@ set global query_cache_size= 0;
 use test;
 drop table t1;
 
+#
+# Bug#41098: Query Cache returns wrong result with concurrent insert
+#
+
+SET @old_concurrent_insert= @@GLOBAL.concurrent_insert;
+SET @old_query_cache_size= @@GLOBAL.query_cache_size;
+
+--disable_warnings
+DROP TABLE IF EXISTS t1, t2;
+--enable_warnings
+CREATE TABLE t1 (a INT);
+CREATE TABLE t2 (a INT);
+INSERT INTO t1 VALUES (1),(2),(3);
+
+SET GLOBAL concurrent_insert= 1;
+SET GLOBAL query_cache_size= 1024*512;
+SET GLOBAL query_cache_type= ON;
+
+connect(con1,localhost,root,,test,,);
+connect(con2,localhost,root,,test,,);
+
+connection con1;
+--echo # Switch to connection con1
+SET SESSION debug='+d,wait_after_query_cache_invalidate';
+--echo # Send concurrent insert, will wait in the query cache table invalidate
+--send INSERT INTO t1 VALUES (4)
+
+connection default;
+--echo # Switch to connection default
+--echo # Wait for concurrent insert to reach the debug point
+let $wait_condition=
+  SELECT COUNT(*) = 1 FROM INFORMATION_SCHEMA.PROCESSLIST
+  WHERE STATE = "wait_after_query_cache_invalidate" AND
+        INFO = "INSERT INTO t1 VALUES (4)";
+--source include/wait_condition.inc
+
+connection con2;
+--echo # Switch to connection con2
+--echo # Send SELECT that shouldn't be cached
+SELECT * FROM t1;
+
+connection default;
+--echo # Switch to connection default
+--echo # Notify the concurrent insert to proceed
+SELECT ID FROM INFORMATION_SCHEMA.PROCESSLIST
+  WHERE STATE = 'wait_after_query_cache_invalidate' INTO @thread_id;
+KILL QUERY @thread_id;
+
+connection con1;
+--echo # Switch to connection con1
+--echo # Gather insert result
+--reap
+SHOW STATUS LIKE "Qcache_queries_in_cache";
+--echo # Test that it's cacheable
+SELECT * FROM t1;
+SHOW STATUS LIKE "Qcache_queries_in_cache";
+
+--echo # Disconnect
+disconnect con1;
+disconnect con2;
+
+connection default;
+--echo # Restore defaults
+RESET QUERY CACHE;
+DROP TABLE t1,t2;
+SET GLOBAL concurrent_insert= DEFAULT;
+SET GLOBAL query_cache_size= DEFAULT;
+SET GLOBAL query_cache_type= DEFAULT;

=== modified file 'sql/item.cc'
--- a/sql/item.cc	2009-02-19 14:48:28 +0000
+++ b/sql/item.cc	2009-02-20 12:37:37 +0000
@@ -1765,7 +1765,6 @@ bool agg_item_set_converter(DTCollation 
 bool agg_item_charsets(DTCollation &coll, const char *fname,
                        Item **args, uint nargs, uint flags, int item_sep)
 {
-  Item **arg, *safe_args[2];
   if (agg_item_collations(coll, fname, args, nargs, flags, item_sep))
     return TRUE;
 

=== modified file 'sql/sql_cache.cc'
--- a/sql/sql_cache.cc	2009-02-13 16:30:54 +0000
+++ b/sql/sql_cache.cc	2009-02-20 12:37:37 +0000
@@ -1540,6 +1540,9 @@ void Query_cache::invalidate(THD *thd, T
       invalidate_table(thd, tables_used);
   }
 
+  DBUG_EXECUTE_IF("wait_after_query_cache_invalidate",
+                  debug_wait_for_kill("wait_after_query_cache_invalidate"););
+
   DBUG_VOID_RETURN;
 }
 

=== modified file 'sql/sql_insert.cc'
--- a/sql/sql_insert.cc	2009-02-16 21:18:45 +0000
+++ b/sql/sql_insert.cc	2009-02-20 12:37:37 +0000
@@ -928,20 +928,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *t
     }
     DBUG_ASSERT(transactional_table || !changed || 
                 thd->transaction.stmt.modified_non_trans_table);
-
-    if (thd->lock)
-    {
-      /*
-        Invalidate the table in the query cache if something changed
-        after unlocking when changes become fisible.
-        TODO: this is workaround. right way will be move invalidating in
-        the unlock procedure.
-      */
-      if (lock_type ==  TL_WRITE_CONCURRENT_INSERT && changed)
-      {
-        query_cache_invalidate3(thd, table_list, 1);
-      }
-    }
   }
   thd_proc_info(thd, "end");
   /*

=== modified file 'sql/sql_select.cc'
--- a/sql/sql_select.cc	2009-02-16 21:18:45 +0000
+++ b/sql/sql_select.cc	2009-02-20 12:37:37 +0000
@@ -3995,11 +3995,12 @@ static uint get_tmp_table_rec_length(Lis
 */
 
 static bool
-make_join_statistics(JOIN *join, TABLE_LIST *tables, COND *conds,
+make_join_statistics(JOIN *join, TABLE_LIST *tables_arg, COND *conds,
 		     DYNAMIC_ARRAY *keyuse_array)
 {
   int error;
   TABLE *table;
+  TABLE_LIST *tables= tables_arg;
   uint i,table_count,const_count,key;
   table_map found_const_table_map, all_table_map, found_ref, refs;
   key_map const_ref, eq_part;
@@ -4039,7 +4040,7 @@ make_join_statistics(JOIN *join, TABLE_L
     if ((error= table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK)))
     {
       table->file->print_error(error, MYF(0));
-      DBUG_RETURN(1);
+      goto error;
     }
     table->quick_keys.clear_all();
     table->reginfo.join_tab=s;
@@ -4135,7 +4136,7 @@ make_join_statistics(JOIN *join, TABLE_L
       {
         join->tables=0;			// Don't use join->table
         my_message(ER_WRONG_OUTER_JOIN, ER(ER_WRONG_OUTER_JOIN), MYF(0));
-        DBUG_RETURN(1);
+        goto error;
       }
       s->key_dependent= s->dependent;
     }
@@ -4145,7 +4146,7 @@ make_join_statistics(JOIN *join, TABLE_L
     if (update_ref_and_keys(join->thd, keyuse_array, stat, join->tables,
                             conds, join->cond_equal,
                             ~outer_join, join->select_lex, &sargables))
-      DBUG_RETURN(1);
+      goto error;
 
   /* Read tables with 0 or 1 rows (system tables) */
   join->const_table_map= 0;
@@ -4161,7 +4162,7 @@ make_join_statistics(JOIN *join, TABLE_L
     if ((tmp=join_read_const_table(s, p_pos)))
     {
       if (tmp > 0)
-	DBUG_RETURN(1);			// Fatal error
+	goto error;		// Fatal error
     }
     else
       found_const_table_map|= s->table->map;
@@ -4233,7 +4234,7 @@ make_join_statistics(JOIN *join, TABLE_L
 	  if ((tmp= join_read_const_table(s, join->positions+const_count-1)))
 	  {
 	    if (tmp > 0)
-	      DBUG_RETURN(1);			// Fatal error
+	      goto error;			// Fatal error
 	  }
 	  else
 	    found_const_table_map|= table->map;
@@ -4289,12 +4290,12 @@ make_join_statistics(JOIN *join, TABLE_L
 	        set_position(join,const_count++,s,start_keyuse);
 	        if (create_ref_for_key(join, s, start_keyuse,
 				       found_const_table_map))
-		  DBUG_RETURN(1);
+                  goto error;
 	        if ((tmp=join_read_const_table(s,
                                                join->positions+const_count-1)))
 	        {
 		  if (tmp > 0)
-		    DBUG_RETURN(1);			// Fatal error
+		    goto error;			// Fatal error
 	        }
 	        else
 		  found_const_table_map|= table->map;
@@ -4381,7 +4382,7 @@ make_join_statistics(JOIN *join, TABLE_L
 			  *s->on_expr_ref ? *s->on_expr_ref : conds,
 			  1, &error);
       if (!select)
-        DBUG_RETURN(1);
+        goto error;
       records= get_quick_record_count(join->thd, select, s->table,
 				      &s->const_keys, join->row_limit);
       s->quick=select->quick;
@@ -4432,7 +4433,7 @@ make_join_statistics(JOIN *join, TABLE_L
   if (join->const_tables != join->tables)
   {
     if (choose_plan(join, all_table_map & ~join->const_table_map))
-      DBUG_RETURN(TRUE);
+      goto error;
   }
   else
   {
@@ -4442,6 +4443,17 @@ make_join_statistics(JOIN *join, TABLE_L
   }
   /* Generate an execution plan from the found optimal join order. */
   DBUG_RETURN(join->thd->killed || get_best_combination(join));
+
+error:
+  /*
+    Need to clean up join_tab from TABLEs in case of error.
+    They won't get cleaned up by JOIN::cleanup() because JOIN::join_tab
+    may not be assigned yet by this function (which is building join_tab).
+    Dangling TABLE::reginfo.join_tab may cause part_of_refkey to choke. 
+  */
+  for (tables= tables_arg; tables; tables= tables->next_leaf)
+    tables->table->reginfo.join_tab= NULL;
+  DBUG_RETURN (1);
 }
 
 

=== modified file 'storage/myisam/ha_myisam.cc'
--- a/storage/myisam/ha_myisam.cc	2009-02-11 12:11:20 +0000
+++ b/storage/myisam/ha_myisam.cc	2009-02-20 12:37:37 +0000
@@ -2162,6 +2162,15 @@ my_bool ha_myisam::register_query_cache_
     }
   }
 
+  /*
+    This query execution might have started after the query cache was flushed
+    by a concurrent INSERT. In this case, don't cache this statement as the
+    data file length difference might not be visible yet if the tables haven't
+    been unlocked by the concurrent insert thread.
+  */
+  if (file->state->uncacheable)
+    DBUG_RETURN(FALSE);
+
   /* It is ok to try to cache current statement. */
   DBUG_RETURN(TRUE);
 }

=== modified file 'storage/myisam/mi_locking.c'
--- a/storage/myisam/mi_locking.c	2008-10-31 18:02:34 +0000
+++ b/storage/myisam/mi_locking.c	2009-02-20 12:37:37 +0000
@@ -287,6 +287,8 @@ void mi_get_status(void* param, my_bool 
   info->save_state=info->s->state.state;
   info->state= &info->save_state;
   info->append_insert_at_end= concurrent_insert;
+  if (concurrent_insert)
+    info->s->state.state.uncacheable= TRUE;
   DBUG_VOID_RETURN;
 }
 

=== modified file 'storage/myisam/myisamdef.h'
--- a/storage/myisam/myisamdef.h	2009-02-13 16:30:54 +0000
+++ b/storage/myisam/myisamdef.h	2009-02-20 12:37:37 +0000
@@ -43,6 +43,7 @@ typedef struct st_mi_status_info
   my_off_t key_file_length;
   my_off_t data_file_length;
   ha_checksum checksum;
+  my_bool uncacheable;                  /* Active concurrent insert */
 } MI_STATUS_INFO;
 
 typedef struct st_mi_state_info


Attachment: [text/bzr-bundle] bzr/mattias.jonsson@sun.com-20090220123737-au6r3dcaette6t01.bundle
Thread
bzr commit into mysql-6.0-bugteam branch (mattias.jonsson:3069)Mattias Jonsson20 Feb