List:Commits« Previous MessageNext Message »
From:marko.makela Date:April 23 2012 10:53am
Subject:bzr push into mysql-trunk-wl5545 branch (marko.makela:3900 to 3903) WL#5545
View as plain text  
 3903 Marko Mäkelä	2012-04-23
      WL#5545: Clean up innodb-index-online.test
      according to suggestions from Matthias Leich.

    modified:
      mysql-test/suite/innodb/r/innodb-index-online.result
      mysql-test/suite/innodb/t/innodb-index-online.test
 3902 Marko Mäkelä	2012-04-23
      WL#5545 testing: Mix column renaming and online index creation.
      
      Fix Bug#13990602 BOGUS DUPLICATE KEY ERROR FOR NULL REPORTED
      DURING ONLINE INDEX CREATION
      
      row_log_apply_op_low(): Do not report duplicates if the row contains
      NULL values. (Online index creation applies to secondary indexes only,
      and the primary key columns never contain NULL values.)
      
      Approved by Jimmy Yang.

    modified:
      mysql-test/suite/innodb/r/innodb-index-online.result
      mysql-test/suite/innodb/t/innodb-index-online.test
      storage/innobase/handler/ha_innodb.cc
      storage/innobase/row/row0log.cc
 3901 Marko Mäkelä	2012-04-23 [merge]
      Merge mysql-trunk to mysql-trunk-wl5545.

    added:
      mysql-test/r/bug12427262.result
      mysql-test/suite/innodb_fts/r/innodb_fts_opt.result
      mysql-test/suite/innodb_fts/t/innodb_fts_opt.test
      mysql-test/suite/rpl/r/rpl_parallel_show_binlog_events_purge_logs.result
      mysql-test/suite/rpl/t/rpl_parallel_show_binlog_events_purge_logs.test
      mysql-test/t/bug12427262.test
    modified:
      VERSION
      include/ft_global.h
      include/mysql/thread_pool_priv.h
      mysql-test/collections/default.push
      mysql-test/lib/My/CoreDump.pm
      mysql-test/r/archive.result
      mysql-test/r/explain.result
      mysql-test/r/fulltext.result
      mysql-test/r/group_by.result
      mysql-test/r/type_temporal_fractional.result
      mysql-test/suite/binlog/r/binlog_multi_engine.result
      mysql-test/suite/innodb/r/innodb-index-online.result
      mysql-test/suite/innodb/t/innodb-index-online.test
      mysql-test/suite/rpl/t/rpl_parallel_change_master.test
      mysql-test/t/archive.test
      mysql-test/t/fulltext.test
      mysql-test/t/group_by.test
      mysql-test/t/type_temporal_fractional.test
      sql/binlog.cc
      sql/ha_ndbcluster_binlog.cc
      sql/handler.h
      sql/item.cc
      sql/item.h
      sql/item_func.cc
      sql/item_func.h
      sql/mysqld.cc
      sql/mysqld.h
      sql/opt_sum.cc
      sql/rpl_mi.cc
      sql/scheduler.cc
      sql/sql_class.cc
      sql/sql_optimizer.cc
      sql/sql_optimizer.h
      sql/sql_parse.cc
      sql/sql_show.cc
      sql/sql_show.h
      sql/sql_string.h
      storage/archive/ha_archive.cc
      storage/innobase/btr/btr0cur.cc
      storage/innobase/handler/ha_innodb.cc
      storage/innobase/handler/ha_innodb.h
      storage/innobase/include/btr0btr.h
      storage/innobase/include/btr0pcur.ic
      storage/innobase/include/read0read.h
      storage/innobase/include/read0read.ic
      storage/innobase/read/read0read.cc
      storage/innobase/row/row0merge.cc
      storage/innobase/trx/trx0trx.cc
      strings/decimal.c
 3900 Marko Mäkelä	2012-04-19 [merge]
      Merge mysql-trunk to mysql-trunk-wl5545.

    added:
      mysql-test/r/myisam_row_rpl.result
      mysql-test/t/myisam_row_rpl-master.opt
      mysql-test/t/myisam_row_rpl-slave.opt
      mysql-test/t/myisam_row_rpl.test
    modified:
      CMakeLists.txt
      README
      client/mysql.cc
      client/mysql_upgrade.c
      client/mysqladmin.cc
      client/mysqlbinlog.cc
      client/mysqlcheck.c
      client/mysqldump.c
      client/mysqlimport.c
      client/mysqlshow.c
      client/mysqlslap.c
      client/mysqltest.cc
      cmake/os/Windows.cmake
      config.h.cmake
      extra/innochecksum.cc
      extra/perror.c
      include/mysql_com.h
      include/welcome_copyright_notice.h
      mysql-test/include/assert_command_output.inc
      mysql-test/r/derived.result
      mysql-test/r/log_tables.result
      mysql-test/r/rewrite_general_log.result
      mysql-test/r/tablespace.result
      mysql-test/suite/binlog/r/binlog_grant.result
      mysql-test/suite/binlog/t/binlog_grant.test
      mysql-test/suite/innodb/r/innodb-index-online.result
      mysql-test/suite/innodb/t/innodb-alter-discard.test
      mysql-test/suite/innodb/t/innodb-index-online.test
      mysql-test/suite/innodb_fts/r/innodb_fts_misc.result
      mysql-test/suite/innodb_fts/t/innodb_fts_misc.test
      mysql-test/suite/perfschema/r/digest_table_full.result
      mysql-test/suite/perfschema/r/statement_digest.result
      mysql-test/suite/perfschema/r/statement_digest_consumers.result
      mysql-test/suite/perfschema/r/statement_digest_long_query.result
      mysql-test/suite/rpl/r/rpl_corruption.result
      mysql-test/suite/rpl/r/rpl_gtid_mode.result
      mysql-test/suite/rpl/t/rpl_corruption.test
      mysql-test/suite/rpl/t/rpl_gtid_mode.test
      mysql-test/t/derived.test
      mysql-test/t/rewrite_general_log.test
      mysql-test/t/tablespace.test
      packaging/WiX/custom_ui.wxs
      sql/field.h
      sql/gen_lex_hash.cc
      sql/handler.h
      sql/item.h
      sql/item_func.cc
      sql/item_func.h
      sql/lex.h
      sql/log_event.cc
      sql/log_event.h
      sql/mysqld.cc
      sql/rpl_info_file.cc
      sql/rpl_mi.cc
      sql/set_var.cc
      sql/sql_derived.cc
      sql/sql_executor.cc
      sql/sql_optimizer.cc
      sql/sql_optimizer.h
      sql/sql_parse.cc
      sql/sql_select.cc
      sql/sql_show.cc
      sql/sql_string.h
      sql/sql_table.cc
      sql/sql_yacc.yy
      sql/table.cc
      sql/unireg.cc
      storage/innobase/pars/lexyy.cc
      storage/innobase/pars/pars0lex.l
      storage/myisam/ha_myisam.cc
      storage/perfschema/gen_pfs_lex_token.cc
=== modified file 'VERSION'
--- a/VERSION	revid:marko.makela@stripped0120419090901-f20rhn6fgm8lwfk6
+++ b/VERSION	revid:marko.makela@stripped
@@ -1,4 +1,4 @@
 MYSQL_VERSION_MAJOR=5
 MYSQL_VERSION_MINOR=6
 MYSQL_VERSION_PATCH=6
-MYSQL_VERSION_EXTRA=-m8
+MYSQL_VERSION_EXTRA=-m9

=== modified file 'include/ft_global.h'
--- a/include/ft_global.h	revid:marko.makela@stripped20419090901-f20rhn6fgm8lwfk6
+++ b/include/ft_global.h	revid:marko.makela@strippede3
@@ -40,11 +40,32 @@ struct _ft_vft
   void      (*reinit_search)(FT_INFO *);
 };
 
+typedef struct st_ft_info_ext FT_INFO_EXT;
+struct _ft_vft_ext
+{
+  uint      (*get_version)();        // Extended API version
+  ulonglong (*get_flags)();
+  ulonglong (*get_docid)(FT_INFO_EXT *);
+  ulonglong (*count_matches)(FT_INFO_EXT *);
+};
+
+/* Flags for extended FT API */
+#define FTS_ORDERED_RESULT                (LL(1) << 1)
+#define FTS_DOCID_IN_RESULT               (LL(1) << 2)
+
+#define FTS_DOC_ID_COL_NAME "FTS_DOC_ID"
+
 #ifndef FT_CORE
 struct st_ft_info
 {
   struct _ft_vft *please; /* INTERCAL style :-) */
 };
+
+struct st_ft_info_ext
+{
+  struct _ft_vft     *please; /* INTERCAL style :-) */
+  struct _ft_vft_ext *could_you;
+};
 #endif
 
 extern const char *ft_stopword_file;

=== modified file 'include/mysql/thread_pool_priv.h'
--- a/include/mysql/thread_pool_priv.h	revid:marko.makela@oracle.com-20120419090901-f20rhn6fgm8lwfk6
+++ b/include/mysql/thread_pool_priv.h	revid:marko.makela@stripped104835-9138g3x7ebpy1ce3
@@ -1,5 +1,5 @@
 /*
-  Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
+  Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -99,7 +99,9 @@ void thd_cleanup(THD *thd);
 /* Decrement connection counter */
 void dec_connection_count();
 /* Destroy THD object */
-void delete_thd(THD *thd);
+void destroy_thd(THD *thd);
+/* Remove the THD from the set of global threads. */
+void remove_global_thread(THD *thd);
 
 /*
   thread_created is maintained by thread pool when activated since

=== modified file 'mysql-test/collections/default.push'
--- a/mysql-test/collections/default.push	revid:marko.makela@strippedf20rhn6fgm8lwfk6
+++ b/mysql-test/collections/default.push	revid:marko.makela@strippedpy1ce3
@@ -3,4 +3,5 @@ perl mysql-test-run.pl --timer --force -
 perl mysql-test-run.pl --timer --force --parallel=auto --comment=main_embedded --vardir=var-main_emebbed  --suite=main --embedded --experimental=collections/default.experimental --skip-ndb
 perl mysql-test-run.pl --timer --force --parallel=auto --comment=innodb_4k_size --vardir=var-innodb-4k --experimental=collections/default.experimental --skip-ndb --suite=innodb --mysqld=--innodb-page-size=4k
 perl mysql-test-run.pl --timer --force --comment=explain-json --vardir=var-explain-json  --suite=explain_json_validate
-perl mysql-test-run.pl --force --timer --parallel=auto --experimental=collections/default.experimental --comment=partitions --vardir=var-parts      --suite=parts 
+perl mysql-test-run.pl --force --timer --parallel=auto --experimental=collections/default.experimental --comment=partitions --vardir=var-parts      --suite=parts
+perl mysql-test-run.pl --timer --force --parallel=auto --comment=rpl_binlog_n_mix_MTS --vardir=var-mts-rpl-binlog-n_mix --mysqld=--binlog-format=mixed --experimental=collections/default.experimental --skip-ndb  --unit-tests --mysqld=--slave-parallel-workers=4 --mysqld=--slave-transaction-retries=0 --suite=rpl 

=== modified file 'mysql-test/lib/My/CoreDump.pm'
--- a/mysql-test/lib/My/CoreDump.pm	revid:marko.makela@strippedfk6
+++ b/mysql-test/lib/My/CoreDump.pm	revid:marko.makela@stripped
@@ -1,5 +1,5 @@
 # -*- cperl -*-
-# Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -158,7 +158,6 @@ sub cdb_check {
 
 sub _cdb {
   my ($core_name)= @_;
-  print localtime() . " cdb debug A\n";
   print "\nTrying 'cdb' to get a backtrace\n";
   return unless -f $core_name;
   
@@ -184,7 +183,6 @@ sub _cdb {
   # build symbol path (required by cdb if executable was built on 
   # different machine)
   my $tmp_name= $core_name.".cdb_lmv";
-  print localtime() . " cdb debug B\n";
   `cdb -z $core_name -c \"lmv;q\" > $tmp_name 2>&1`;
   print localtime() . " cdb debug C\n";
   if ($? >> 8)
@@ -195,7 +193,7 @@ sub _cdb {
     cdb_check();
     return;
   }
-  
+  print localtime() . " cdb debug E\n";
   open(temp,"< $tmp_name");
   my %dirhash=();
   while(<temp>)
@@ -211,7 +209,7 @@ sub _cdb {
   }
   close(temp);
   unlink($tmp_name);
-  
+  print localtime() . " cdb debug F\n";
   my $image_path= join(";", (keys %dirhash),".");
 
   # For better callstacks, setup _NT_SYMBOL_PATH to include
@@ -242,6 +240,7 @@ sub _cdb {
   my $cdb_output=
     `cdb -c "$cdb_cmd" -z $core_name -i "$image_path" -y "$symbol_path" -t 0 -lines 2>&1`;
   return if $? >> 8;
+  print localtime() . " cdb debug G\n";
   return unless $cdb_output;
   
   # Remove comments (lines starting with *), stack pointer and frame 

=== modified file 'mysql-test/r/archive.result'
--- a/mysql-test/r/archive.result	revid:marko.makela@stripped
+++ b/mysql-test/r/archive.result	revid:marko.makela@oracle.com-20120423104835-9138g3x7ebpy1ce3
@@ -12915,3 +12915,11 @@ OPTIMIZE TABLE t1;
 Table	Op	Msg_type	Msg_text
 test.t1	optimize	status	OK
 DROP TABLE t1;
+#
+# Bug#13907676: HA_ARCHIVE::INFO
+#
+CREATE TABLE t1 (a INT) ENGINE=ARCHIVE;
+CREATE TABLE t2 SELECT * FROM t1;
+SELECT * FROM t2;
+a
+DROP TABLE t1, t2;

=== added file 'mysql-test/r/bug12427262.result'
--- a/mysql-test/r/bug12427262.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/r/bug12427262.result	revid:marko.makela@strippedx7ebpy1ce3
@@ -0,0 +1,56 @@
+#
+# Bug#12427262 : 60961: SHOW TABLES VERY SLOW WHEN NOT IN SYSTEM DISK CACHE. 
+#
+create database show_table_lw_db;
+use show_table_lw_db;
+create table t1 (c1 int);
+create table t2 (c1 int);
+create table t3 (c1 int);
+create table t4 (c1 int);
+create table t5 (c1 int);
+create table t6 (c1 int);
+create table t7 (c1 int);
+create table t8 (c1 int);
+create table t9 (c1 int);
+create table t10 (c1 int);
+select Sum(ALL(COUNT_READ)) from performance_schema.file_summary_by_instance where FILE_NAME 
+like "%show_table_lw_db%" AND FILE_NAME like "%.frm%" AND EVENT_NAME='wait/io/file/sql/FRM'
+into @count_read_before;
+show tables;
+Tables_in_show_table_lw_db
+t1
+t10
+t2
+t3
+t4
+t5
+t6
+t7
+t8
+t9
+select Sum(ALL(COUNT_READ)) from performance_schema.file_summary_by_instance where FILE_NAME 
+like "%show_table_lw_db%" AND FILE_NAME like "%.frm%" AND EVENT_NAME='wait/io/file/sql/FRM'
+into @count_read_after;
+select @count_read_after-@count_read_before;
+@count_read_after-@count_read_before
+0.000000000000000000000000000000
+show full tables;
+Tables_in_show_table_lw_db	Table_type
+t1	BASE TABLE
+t10	BASE TABLE
+t2	BASE TABLE
+t3	BASE TABLE
+t4	BASE TABLE
+t5	BASE TABLE
+t6	BASE TABLE
+t7	BASE TABLE
+t8	BASE TABLE
+t9	BASE TABLE
+select Sum(ALL(COUNT_READ)) from performance_schema.file_summary_by_instance where FILE_NAME 
+like "%show_table_lw_db%" AND FILE_NAME like "%.frm%" AND EVENT_NAME='wait/io/file/sql/FRM'
+into @count_read_after;
+select @count_read_after-@count_read_before;
+@count_read_after-@count_read_before
+10.000000000000000000000000000000
+drop table t1;
+drop database show_table_lw_db;

=== modified file 'mysql-test/r/explain.result'
--- a/mysql-test/r/explain.result	revid:marko.makela@oracle.com-20120419090901-f20rhn6fgm8lwfk6
+++ b/mysql-test/r/explain.result	revid:marko.makela@stripped35-9138g3x7ebpy1ce3
@@ -308,8 +308,9 @@ EXPLAIN SELECT 1 FROM t1
 WHERE 1 > ALL((SELECT 1 FROM t1 JOIN t1 a ON (MATCH(t1.f1) AGAINST (""))
 WHERE t1.f1 GROUP BY t1.f1));
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	PRIMARY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE
-2	SUBQUERY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Select tables optimized away
+1	PRIMARY	t1	system	NULL	NULL	NULL	NULL	1	NULL
+2	SUBQUERY	a	system	NULL	NULL	NULL	NULL	1	NULL
+2	SUBQUERY	t1	fulltext	f1	f1	0	NULL	1	Using where
 PREPARE stmt FROM
 'EXPLAIN SELECT 1 FROM t1
  WHERE 1 > ALL((SELECT 1 FROM t1 RIGHT OUTER JOIN t1 a
@@ -317,12 +318,14 @@ PREPARE stmt FROM
  WHERE t1.f1 GROUP BY t1.f1))';
 EXECUTE stmt;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	PRIMARY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE
-2	SUBQUERY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Select tables optimized away
+1	PRIMARY	t1	system	NULL	NULL	NULL	NULL	1	NULL
+2	SUBQUERY	a	system	NULL	NULL	NULL	NULL	1	NULL
+2	SUBQUERY	t1	fulltext	f1	f1	0	NULL	1	Using where
 EXECUTE stmt;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	PRIMARY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
-2	SUBQUERY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Select tables optimized away
+1	PRIMARY	t1	system	NULL	NULL	NULL	NULL	1	NULL
+2	SUBQUERY	a	system	NULL	NULL	NULL	NULL	1	NULL
+2	SUBQUERY	t1	fulltext	f1	f1	0	NULL	1	Using where
 DEALLOCATE PREPARE stmt;
 PREPARE stmt FROM
 'EXPLAIN SELECT 1 FROM t1
@@ -331,12 +334,14 @@ PREPARE stmt FROM
  WHERE t1.f1 GROUP BY t1.f1))';
 EXECUTE stmt;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	PRIMARY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE
-2	SUBQUERY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Select tables optimized away
+1	PRIMARY	t1	system	NULL	NULL	NULL	NULL	1	NULL
+2	SUBQUERY	a	system	NULL	NULL	NULL	NULL	1	NULL
+2	SUBQUERY	t1	fulltext	f1	f1	0	NULL	1	Using where
 EXECUTE stmt;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	PRIMARY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
-2	SUBQUERY	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Select tables optimized away
+1	PRIMARY	t1	system	NULL	NULL	NULL	NULL	1	NULL
+2	SUBQUERY	a	system	NULL	NULL	NULL	NULL	1	NULL
+2	SUBQUERY	t1	fulltext	f1	f1	0	NULL	1	Using where
 DEALLOCATE PREPARE stmt;
 DROP TABLE t1;
 End of 5.1 tests.

=== modified file 'mysql-test/r/fulltext.result'
--- a/mysql-test/r/fulltext.result	revid:marko.makela@stripped-20120419090901-f20rhn6fgm8lwfk6
+++ b/mysql-test/r/fulltext.result	revid:marko.makela@stripped138g3x7ebpy1ce3
@@ -680,11 +680,12 @@ PREPARE stmt FROM
  ALL((SELECT 1 FROM t1 JOIN t1 a
  ON (MATCH(t1.f1) against (""))
  WHERE t1.f1 GROUP BY t1.f1))';
-# See BUG#12888306. Correct result is one row with value 1.
 EXECUTE stmt;
 1
+1
 EXECUTE stmt;
 1
+1
 DEALLOCATE PREPARE stmt;
 DROP TABLE t1;
 End of 5.1 tests

=== modified file 'mysql-test/r/group_by.result'
--- a/mysql-test/r/group_by.result	revid:marko.makela@strippedfgm8lwfk6
+++ b/mysql-test/r/group_by.result	revid:marko.makela@stripped
@@ -2649,3 +2649,12 @@ MIN(a)	b
 0	a
 
 DROP TABLE t1;
+#
+# Bug #12888306 MISSING ROWS FOR SELECT >ALL (SUBQUERY WITHOUT ROWS)
+#
+CREATE TABLE t1(a INT);
+INSERT INTO t1 VALUES (0);
+SELECT 1 FROM t1 WHERE 1 > ALL(SELECT 1 FROM t1 WHERE a);
+1
+1
+DROP TABLE t1;

=== modified file 'mysql-test/r/type_temporal_fractional.result'
--- a/mysql-test/r/type_temporal_fractional.result	revid:marko.makela@oracle.com-20120419090901-f20rhn6fgm8lwfk6
+++ b/mysql-test/r/type_temporal_fractional.result	revid:marko.makela@oracle.com-20120423104835-9138g3x7ebpy1ce3
@@ -17256,4 +17256,16 @@ SELECT GREATEST(a,10), LEAST(a,10) FROM
 GREATEST(a,10)	LEAST(a,10)
 20010101010101.123456	10.000000
 DROP TABLE t1;
+#
+# Bug#13976233 ASSERTION FAILED: !CHECK_TIME_MMSSFF_RANGE(LTIME), FILE SQL_TIME.CC, LINE 304
+#
+SELECT SECOND(4.99999999991e0);
+SECOND(4.99999999991e0)
+5
+SELECT SECOND(-4.99999999991e0);
+SECOND(-4.99999999991e0)
+5
+SELECT SECOND(TRUNCATE('5',180));
+SECOND(TRUNCATE('5',180))
+5
 # End of 5.6 tests

=== modified file 'mysql-test/suite/binlog/r/binlog_multi_engine.result'
--- a/mysql-test/suite/binlog/r/binlog_multi_engine.result	revid:marko.makela@stripped
+++ b/mysql-test/suite/binlog/r/binlog_multi_engine.result	revid:marko.makela@strippedy1ce3
@@ -19,7 +19,7 @@ COMMIT;
 TRUNCATE t1m;
 TRUNCATE t1b;
 TRUNCATE t1n;
-show binlog events from <binlog_start>;
+include/show_binlog_events.inc
 Log_name	Pos	Event_type	Server_id	End_log_pos	Info
 mysqld-bin.000001	#	Query	#	#	BEGIN
 mysqld-bin.000001	#	Query	#	#	use `test`; INSERT INTO t1b VALUES (1,1), (1,2), (2,1), (2,2)
@@ -52,7 +52,7 @@ ERROR HY000: Cannot execute statement: i
 TRUNCATE t1m;
 TRUNCATE t1b;
 TRUNCATE t1n;
-show binlog events from <binlog_start>;
+include/show_binlog_events.inc
 Log_name	Pos	Event_type	Server_id	End_log_pos	Info
 mysqld-bin.000001	#	Query	#	#	BEGIN
 mysqld-bin.000001	#	Query	#	#	use `test`; INSERT INTO t1b VALUES (1,1), (1,2), (2,1), (2,2)
@@ -81,7 +81,7 @@ UPDATE t1m, t1n SET m = 2, e = 3 WHERE n
 ERROR HY000: Cannot execute statement: impossible to write to binary log since more than one engine is involved and at least one engine is self-logging.
 UPDATE t1n, t1b SET e = 2, b = 3 WHERE f = c;
 ERROR HY000: Cannot execute statement: impossible to write to binary log since more than one engine is involved and at least one engine is self-logging.
-show binlog events from <binlog_start>;
+include/show_binlog_events.inc
 Log_name	Pos	Event_type	Server_id	End_log_pos	Info
 mysqld-bin.000001	#	Query	#	#	BEGIN
 mysqld-bin.000001	#	Table_map	#	#	table_id: # (test.t1m)

=== modified file 'mysql-test/suite/innodb/r/innodb-index-online.result'
--- a/mysql-test/suite/innodb/r/innodb-index-online.result	revid:marko.makela@oracle.com-20120419090901-f20rhn6fgm8lwfk6
+++ b/mysql-test/suite/innodb/r/innodb-index-online.result	revid:marko.makela@stripped
@@ -1,46 +1,42 @@
-SET GLOBAL DEBUG='d,query,debug_sync_exec:i:t:A,ds.trace';
-SET DEBUG='+d,debug_sync_abort_on_timeout';
-SET DEBUG='d,query,debug_sync_exec:i:t:A,ds.trace';
 call mtr.add_suppression("InnoDB: Warning: Small buffer pool size");
 call mtr.add_suppression("Cannot find index .*c2 in InnoDB index translation table");
 call mtr.add_suppression("Find index .*c2 in InnoDB index list but not its MySQL index number");
 call mtr.add_suppression("InnoDB: Error: table 'test/t1'");
 call mtr.add_suppression("MySQL is trying to open a table handle but the .ibd file for");
-SET GLOBAL innodb_file_per_table=on;
-CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 INT, c3 INT) ENGINE=InnoDB;
-INSERT INTO t1 VALUES (1,1,0),(2,2,0),(3,3,0),(4,4,0),(5,5,0);
-SET GLOBAL innodb_monitor_enable=module_ddl;
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+SET @global_innodb_file_per_table_orig = @@global.innodb_file_per_table;
+SET GLOBAL innodb_file_per_table = on;
+CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 INT, c3 TEXT) ENGINE = InnoDB;
+INSERT INTO t1 VALUES (1,1,''), (2,2,''), (3,3,''), (4,4,''), (5,5,'');
+SET GLOBAL innodb_monitor_enable = module_ddl;
+SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
 name	count
 ddl_background_drop_indexes	0
 ddl_background_drop_tables	0
 ddl_online_create_index	0
 ddl_pending_alter_table	0
-SET DEBUG_SYNC='RESET';
-SET DEBUG_SYNC='write_row_noreplace SIGNAL have_handle WAIT_FOR go_ahead';
+SET DEBUG_SYNC = 'RESET';
+SET DEBUG_SYNC = 'write_row_noreplace SIGNAL have_handle WAIT_FOR go_ahead';
 INSERT INTO t1 VALUES(1,2,3);
-SET DEBUG='+d,debug_sync_abort_on_timeout';
-SET DEBUG='d,query,debug_sync_exec:i:t:A,ds.trace';
-SET DEBUG_SYNC='now WAIT_FOR have_handle';
-SET lock_wait_timeout=1;
+SET DEBUG_SYNC = 'now WAIT_FOR have_handle';
+SET lock_wait_timeout = 1;
 ALTER TABLE t1 ADD UNIQUE INDEX(c2);
 ERROR HY000: Lock wait timeout exceeded; try restarting transaction
-SET DEBUG_SYNC='now SIGNAL go_ahead';
+SET DEBUG_SYNC = 'now SIGNAL go_ahead';
 ERROR 23000: Duplicate entry '1' for key 'PRIMARY'
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
 name	count
 ddl_background_drop_indexes	0
 ddl_background_drop_tables	0
 ddl_online_create_index	0
 ddl_pending_alter_table	0
-SET SESSION DEBUG='+d,innodb_OOM_prepare_inplace_alter';
+SET SESSION DEBUG = '+d,innodb_OOM_prepare_inplace_alter';
 ALTER TABLE t1 ADD UNIQUE INDEX(c2);
 ERROR HY000: Out of memory; check if mysqld or some other process uses all available memory; if not, you may have to use 'ulimit' to allow mysqld to use more memory or you can add more swap space
-SET SESSION DEBUG='-d,innodb_OOM_prepare_inplace_alter';
-SET SESSION DEBUG='+d,innodb_OOM_inplace_alter';
+SET SESSION DEBUG = '-d,innodb_OOM_prepare_inplace_alter';
+SET SESSION DEBUG = '+d,innodb_OOM_inplace_alter';
 CREATE UNIQUE INDEX c2 ON t1(c2);
 ERROR HY000: Out of memory; check if mysqld or some other process uses all available memory; if not, you may have to use 'ulimit' to allow mysqld to use more memory or you can add more swap space
-SET SESSION DEBUG='-d,innodb_OOM_inplace_alter';
+SET SESSION DEBUG = '-d,innodb_OOM_inplace_alter';
 CREATE UNIQUE INDEX c2 ON t1(c2);
 DROP INDEX c2 ON t1;
 SHOW CREATE TABLE t1;
@@ -48,25 +44,25 @@ Table	Create Table
 t1	CREATE TABLE `t1` (
   `c1` int(11) NOT NULL,
   `c2` int(11) DEFAULT NULL,
-  `c3` int(11) DEFAULT NULL,
+  `c3` text,
   PRIMARY KEY (`c1`)
 ) ENGINE=InnoDB DEFAULT CHARSET=latin1
 BEGIN;
 INSERT INTO t1 VALUES(7,4,2);
-SET DEBUG_SYNC='row_log_apply_before SIGNAL scanned WAIT_FOR rollback_done';
+SET DEBUG_SYNC = 'row_log_apply_before SIGNAL scanned WAIT_FOR rollback_done';
 ALTER TABLE t1 ADD UNIQUE INDEX(c2);
 ERROR HY000: Lock wait timeout exceeded; try restarting transaction
 COMMIT;
 ALTER TABLE t1 ADD UNIQUE INDEX(c2);
 ERROR 23000: Duplicate entry '4' for key 'c2'
-DELETE FROM t1 WHERE c1=7;
-ALTER TABLE t1 ADD FOREIGN KEY(c2) REFERENCES t1(c2), ALGORITHM=INPLACE;
-ERROR 42000: This version of MySQL doesn't yet support 'ALTER TABLE t1 ADD FOREIGN KEY(c2) REFERENCES t1(c2), ALGORITHM=INPLACE'
-ALTER TABLE t1 ADD UNIQUE INDEX(c2), LOCK=EXCLUSIVE, ALGORITHM=INPLACE;
+DELETE FROM t1 WHERE c1 = 7;
+ALTER TABLE t1 ADD FOREIGN KEY(c2) REFERENCES t1(c2), ALGORITHM = INPLACE;
+ERROR 42000: This version of MySQL doesn't yet support 'ALTER TABLE t1 ADD FOREIGN KEY(c2) REFERENCES t1(c2), ALGORITHM = INPLACE'
+ALTER TABLE t1 ADD UNIQUE INDEX(c2), LOCK = EXCLUSIVE, ALGORITHM = INPLACE;
 DROP INDEX c2 ON t1;
 ALTER TABLE t1 ADD UNIQUE INDEX(c2);
-SET DEBUG_SYNC='now WAIT_FOR scanned';
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+SET DEBUG_SYNC = 'now WAIT_FOR scanned';
+SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
 name	count
 ddl_background_drop_indexes	0
 ddl_background_drop_tables	0
@@ -75,12 +71,12 @@ ddl_pending_alter_table	1
 BEGIN;
 INSERT INTO t1 VALUES(7,4,2);
 ROLLBACK;
-SET DEBUG_SYNC='now SIGNAL rollback_done';
+SET DEBUG_SYNC = 'now SIGNAL rollback_done';
 ERROR 23000: Duplicate entry '4' for key 'c2'
-SET DEBUG_SYNC='row_log_apply_after SIGNAL created WAIT_FOR dml_done';
+SET DEBUG_SYNC = 'row_log_apply_after SIGNAL created WAIT_FOR dml_done';
 ALTER TABLE t1 ADD UNIQUE INDEX(c2);
-SET DEBUG_SYNC='now WAIT_FOR created';
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+SET DEBUG_SYNC = 'now WAIT_FOR created';
+SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
 name	count
 ddl_background_drop_indexes	0
 ddl_background_drop_tables	0
@@ -88,8 +84,8 @@ ddl_online_create_index	0
 ddl_pending_alter_table	1
 INSERT INTO t1 VALUES(6,3,1);
 ERROR 23000: Can't write; duplicate key in table 't1'
-SET DEBUG_SYNC='now SIGNAL dml_done';
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+SET DEBUG_SYNC = 'now SIGNAL dml_done';
+SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
 name	count
 ddl_background_drop_indexes	0
 ddl_background_drop_tables	0
@@ -99,10 +95,13 @@ INSERT INTO t1 VALUES(6,3,1);
 ERROR 23000: Duplicate entry '3' for key 'c2'
 INSERT INTO t1 VALUES(7,4,2);
 ERROR 23000: Duplicate entry '4' for key 'c2'
-SET SESSION innodb_analyze_is_persistent=1;
+SET SESSION innodb_analyze_is_persistent = 1;
 ANALYZE TABLE t1;
 Table	Op	Msg_type	Msg_text
 test.t1	analyze	status	OK
+UPDATE mysql.innodb_index_stats SET stat_value=5
+WHERE database_name='test' AND table_name='t1' AND index_name='PRIMARY'
+AND stat_value=6;
 SELECT * FROM mysql.innodb_index_stats;
 database_name	table_name	index_name	last_update	stat_name	stat_value	sample_size	stat_description
 test	t1	PRIMARY	LAST_UPDATE	n_diff_pfx01	5	1	c1
@@ -112,7 +111,7 @@ test	t1	c2	LAST_UPDATE	n_diff_pfx01	5	1
 test	t1	c2	LAST_UPDATE	n_leaf_pages	1	NULL	Number of leaf pages in the index
 test	t1	c2	LAST_UPDATE	size	1	NULL	Number of pages in the index
 CREATE TABLE t1_c2_stats SELECT * FROM mysql.innodb_index_stats
-WHERE database_name='test' AND table_name='t1' and index_name='c2';
+WHERE database_name = 'test' AND table_name = 't1' and index_name = 'c2';
 DROP INDEX c2 ON t1;
 SELECT * FROM mysql.innodb_index_stats;
 database_name	table_name	index_name	last_update	stat_name	stat_value	sample_size	stat_description
@@ -121,19 +120,19 @@ test	t1	PRIMARY	LAST_UPDATE	n_leaf_pages
 test	t1	PRIMARY	LAST_UPDATE	size	1	NULL	Number of pages in the index
 KILL QUERY @id;
 ERROR 70100: Query execution was interrupted
-SET DEBUG_SYNC='row_log_apply_before SIGNAL c2d_created WAIT_FOR kill_done';
+SET DEBUG_SYNC = 'row_log_apply_before SIGNAL c2d_created WAIT_FOR kill_done';
 CREATE INDEX c2d ON t1(c2);
-SET DEBUG_SYNC='now WAIT_FOR c2d_created';
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+SET DEBUG_SYNC = 'now WAIT_FOR c2d_created';
+SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
 name	count
 ddl_background_drop_indexes	0
 ddl_background_drop_tables	0
 ddl_online_create_index	1
 ddl_pending_alter_table	1
 KILL QUERY @id;
-SET DEBUG_SYNC='now SIGNAL kill_done';
+SET DEBUG_SYNC = 'now SIGNAL kill_done';
 ERROR 70100: Query execution was interrupted
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
 name	count
 ddl_background_drop_indexes	0
 ddl_background_drop_tables	0
@@ -142,22 +141,22 @@ ddl_pending_alter_table	0
 CHECK TABLE t1;
 Table	Op	Msg_type	Msg_text
 test.t1	check	status	OK
-INSERT INTO t1 SELECT 5+c1,c2,c3 FROM t1;
-INSERT INTO t1 SELECT 10+c1,c2,c3 FROM t1;
-INSERT INTO t1 SELECT 20+c1,c2,c3 FROM t1;
-INSERT INTO t1 SELECT 40+c1,c2,c3 FROM t1;
-EXPLAIN SELECT COUNT(*) FROM t1 WHERE c2>3;
+INSERT INTO t1 SELECT  5 + c1, c2,  c3 FROM t1;
+INSERT INTO t1 SELECT 10 + c1, c2, c3 FROM t1;
+INSERT INTO t1 SELECT 20 + c1, c2, c3 FROM t1;
+INSERT INTO t1 SELECT 40 + c1, c2, c3 FROM t1;
+EXPLAIN SELECT COUNT(*) FROM t1 WHERE c2 > 3;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t1	ALL	NULL	NULL	NULL	NULL	5	Using where
 ANALYZE TABLE t1;
 Table	Op	Msg_type	Msg_text
 test.t1	analyze	status	OK
-UPDATE t1_c2_stats SET index_name='c2d';
-UPDATE t1_c2_stats SET stat_value=2 WHERE stat_name='n_diff_pfx01';
+UPDATE t1_c2_stats SET index_name = 'c2d';
+UPDATE t1_c2_stats SET stat_value = 2 WHERE stat_name = 'n_diff_pfx01';
 INSERT INTO t1_c2_stats
-SELECT database_name,table_name,index_name,last_update,'n_diff_pfx02',80,
-sample_size,'c2,c1' FROM t1_c2_stats
-WHERE stat_name='n_diff_pfx01' AND stat_description='c2';
+SELECT database_name, table_name, index_name, last_update, 'n_diff_pfx02', 80,
+sample_size, 'c2,c1' FROM t1_c2_stats
+WHERE stat_name = 'n_diff_pfx01' AND stat_description = 'c2';
 INSERT INTO mysql.innodb_index_stats SELECT * FROM t1_c2_stats;
 DROP TABLE t1_c2_stats;
 CREATE INDEX c2d ON t1(c2);
@@ -165,7 +164,7 @@ SHOW INDEX FROM t1;
 Table	Non_unique	Key_name	Seq_in_index	Column_name	Collation	Cardinality	Sub_part	Packed	Null	Index_type	Comment	Index_comment
 t1	0	PRIMARY	1	c1	A	80	NULL	NULL		BTREE		
 t1	1	c2d	1	c2	A	4	NULL	NULL	YES	BTREE		
-EXPLAIN SELECT COUNT(*) FROM t1 WHERE c2>3;
+EXPLAIN SELECT COUNT(*) FROM t1 WHERE c2 > 3;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 1	SIMPLE	t1	range	c2d	c2d	5	NULL	32	Using where; Using index
 SHOW CREATE TABLE t1;
@@ -173,18 +172,19 @@ Table	Create Table
 t1	CREATE TABLE `t1` (
   `c1` int(11) NOT NULL,
   `c2` int(11) DEFAULT NULL,
-  `c3` int(11) DEFAULT NULL,
+  `c3` text,
   PRIMARY KEY (`c1`),
   KEY `c2d` (`c2`)
 ) ENGINE=InnoDB DEFAULT CHARSET=latin1
-SET DEBUG_SYNC='row_log_apply_before SIGNAL c2e_created WAIT_FOR dml2_done';
-SET lock_wait_timeout=10;
-ALTER TABLE t1 DROP INDEX c2d, ADD INDEX c2e(c2);
-INSERT INTO t1 SELECT 80+c1,c2,c3 FROM t1;
-INSERT INTO t1 SELECT 160+c1,c2,c3 FROM t1;
-UPDATE t1 SET c2=c2+1;
-SET DEBUG_SYNC='now WAIT_FOR c2e_created';
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+SET DEBUG_SYNC = 'row_log_apply_before SIGNAL c2e_created WAIT_FOR dml2_done';
+SET lock_wait_timeout = 10;
+ALTER TABLE t1 CHANGE c2 c22 INT, DROP INDEX c2d, ADD INDEX c2e(c22),
+ALGORITHM=INPLACE;
+INSERT INTO t1 SELECT  80 + c1, c2, c3 FROM t1;
+INSERT INTO t1 SELECT 160 + c1, c2, c3 FROM t1;
+UPDATE t1 SET c2 = c2 + 1;
+SET DEBUG_SYNC = 'now WAIT_FOR c2e_created';
+SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
 name	count
 ddl_background_drop_indexes	0
 ddl_background_drop_tables	0
@@ -193,107 +193,159 @@ ddl_pending_alter_table	1
 BEGIN;
 DELETE FROM t1;
 ROLLBACK;
-UPDATE t1 SET c2=c2+1;
+UPDATE t1 SET c2 = c2 + 1;
 BEGIN;
-UPDATE t1 SET c2=c2+1;
+UPDATE t1 SET c2 = c2 + 1;
 DELETE FROM t1;
 ROLLBACK;
 BEGIN;
 DELETE FROM t1;
 ROLLBACK;
-UPDATE t1 SET c2=c2+1;
+UPDATE t1 SET c2 = c2 + 1;
 BEGIN;
-UPDATE t1 SET c2=c2+1;
+UPDATE t1 SET c2 = c2 + 1;
 DELETE FROM t1;
 ROLLBACK;
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
 name	count
 ddl_background_drop_indexes	0
 ddl_background_drop_tables	0
 ddl_online_create_index	1
 ddl_pending_alter_table	1
-SET DEBUG_SYNC='now SIGNAL dml2_done';
+SELECT sf.name, sf.pos FROM INFORMATION_SCHEMA.INNODB_SYS_INDEXES si
+INNER JOIN INFORMATION_SCHEMA.INNODB_SYS_FIELDS sf
+ON si.index_id = sf.index_id WHERE si.name = 'c2e';
+name	pos
+c2	0
+SET DEBUG_SYNC = 'now SIGNAL dml2_done';
 ERROR HY000: Creating index 'c2e' required more than 'innodb_online_alter_log_max_size' bytes of modification log. Please try again.
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
 name	count
 ddl_background_drop_indexes	1
 ddl_background_drop_tables	0
 ddl_online_create_index	0
 ddl_pending_alter_table	0
-ALTER TABLE t1 COMMENT 'testing if c2e will be dropped';
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+SELECT sf.name, sf.pos FROM INFORMATION_SCHEMA.INNODB_SYS_INDEXES si
+INNER JOIN INFORMATION_SCHEMA.INNODB_SYS_FIELDS sf
+ON si.index_id = sf.index_id WHERE si.name = 'c2e';
+name	pos
+SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
 name	count
-ddl_background_drop_indexes	0
+ddl_background_drop_indexes	1
 ddl_background_drop_tables	0
 ddl_online_create_index	0
 ddl_pending_alter_table	0
-SET GLOBAL innodb_monitor_disable=module_ddl;
-SET DEBUG_SYNC='row_log_apply_before SIGNAL c2f_created WAIT_FOR dml3_done';
-ALTER TABLE t1 ADD INDEX c2f(c2);
-SET DEBUG_SYNC='now WAIT_FOR c2f_created';
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+ALTER TABLE t1 COMMENT 'testing if c2e will be dropped';
+SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
 name	count
 ddl_background_drop_indexes	0
 ddl_background_drop_tables	0
 ddl_online_create_index	0
 ddl_pending_alter_table	0
+SET DEBUG_SYNC = 'row_log_apply_before SIGNAL c2f_created WAIT_FOR dml3_done';
+ALTER TABLE t1 ADD INDEX c2f(c22f), CHANGE c2 c22f INT;
+SET DEBUG_SYNC = 'now WAIT_FOR c2f_created';
+SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+name	count
+ddl_background_drop_indexes	0
+ddl_background_drop_tables	0
+ddl_online_create_index	1
+ddl_pending_alter_table	1
 BEGIN;
-INSERT INTO t1 SELECT 320+c1,c2,c3 FROM t1 WHERE c1>160;
-DELETE FROM t1 WHERE c1>320;
+INSERT INTO t1 SELECT 320 + c1, c2, c3 FROM t1 WHERE c1 > 160;
+DELETE FROM t1 WHERE c1 > 320;
 ROLLBACK;
 BEGIN;
-UPDATE t1 SET c2=c2+1;
+UPDATE t1 SET c2 = c2 + 1;
 DELETE FROM t1;
 ROLLBACK;
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
 name	count
 ddl_background_drop_indexes	0
 ddl_background_drop_tables	0
-ddl_online_create_index	0
-ddl_pending_alter_table	0
-SET DEBUG_SYNC='now SIGNAL dml3_done';
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+ddl_online_create_index	1
+ddl_pending_alter_table	1
+SET DEBUG_SYNC = 'now SIGNAL dml3_done';
+SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
 name	count
 ddl_background_drop_indexes	0
 ddl_background_drop_tables	0
 ddl_online_create_index	0
 ddl_pending_alter_table	0
-SELECT COUNT(c2) FROM t1;
-COUNT(c2)
+SELECT COUNT(c22f) FROM t1;
+COUNT(c22f)
 320
 CHECK TABLE t1;
 Table	Op	Msg_type	Msg_text
 test.t1	check	status	OK
-SET DEBUG_SYNC='row_log_apply_before SIGNAL c2g_created WAIT_FOR dml4_done';
+ALTER TABLE t1 ADD UNIQUE INDEX c3p5(c3(5));
+ERROR 23000: Duplicate entry '' for key 'c3p5'
+UPDATE t1 SET c3=NULL WHERE c3='';
 SET lock_wait_timeout=1;
-ALTER TABLE t1 DROP INDEX c2f, ADD INDEX c2g(c2);
-SET DEBUG_SYNC='now WAIT_FOR c2g_created';
-SET lock_wait_timeout=10;
+SET DEBUG_SYNC = 'row_log_apply_before SIGNAL c3p5_created WAIT_FOR ins_done';
+ALTER TABLE t1 ADD UNIQUE INDEX c3p5(c3(5));
+SET DEBUG_SYNC = 'now WAIT_FOR c3p5_created';
+SELECT sf.name, sf.pos FROM INFORMATION_SCHEMA.INNODB_SYS_INDEXES si
+INNER JOIN INFORMATION_SCHEMA.INNODB_SYS_FIELDS sf
+ON si.index_id = sf.index_id WHERE si.name = 'c3p5';
+name	pos
+c3	0
+SET DEBUG_SYNC = 'ib_after_row_insert SIGNAL ins_done WAIT_FOR ddl_timed_out';
+INSERT INTO t1 VALUES(347,33101,NULL);
+ERROR HY000: Lock wait timeout exceeded; try restarting transaction
+SET DEBUG_SYNC = 'now SIGNAL ddl_timed_out';
+SELECT sf.name, sf.pos FROM INFORMATION_SCHEMA.INNODB_SYS_INDEXES si
+INNER JOIN INFORMATION_SCHEMA.INNODB_SYS_FIELDS sf
+ON si.index_id = sf.index_id WHERE si.name = 'c3p5';
+name	pos
+SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+name	count
+ddl_background_drop_indexes	1
+ddl_background_drop_tables	0
+ddl_online_create_index	0
+ddl_pending_alter_table	0
+SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+name	count
+ddl_background_drop_indexes	1
+ddl_background_drop_tables	0
+ddl_online_create_index	0
+ddl_pending_alter_table	0
+ALTER TABLE t1 COMMENT 'testing if c3p5 will be dropped';
+SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+name	count
+ddl_background_drop_indexes	0
+ddl_background_drop_tables	0
+ddl_online_create_index	0
+ddl_pending_alter_table	0
+SET DEBUG_SYNC = 'row_log_apply_before SIGNAL c2g_created WAIT_FOR dml4_done';
+SET lock_wait_timeout = 1;
+ALTER TABLE t1 DROP INDEX c2f, ADD INDEX c2g(c22f);
+SET DEBUG_SYNC = 'now WAIT_FOR c2g_created';
+SET lock_wait_timeout = 10;
 ALTER TABLE t1 DISCARD TABLESPACE;
-SET DEBUG_SYNC='now SIGNAL dml4_done';
+SET DEBUG_SYNC = 'now SIGNAL dml4_done';
 ERROR HY000: Lock wait timeout exceeded; try restarting transaction
 SHOW CREATE TABLE t1;
 Table	Create Table
 t1	CREATE TABLE `t1` (
   `c1` int(11) NOT NULL,
-  `c2` int(11) DEFAULT NULL,
-  `c3` int(11) DEFAULT NULL,
+  `c22f` int(11) DEFAULT NULL,
+  `c3` text,
   PRIMARY KEY (`c1`),
-  KEY `c2d` (`c2`),
-  KEY `c2f` (`c2`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='testing if c2e will be dropped'
+  KEY `c2d` (`c22f`),
+  KEY `c2f` (`c22f`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='testing if c3p5 will be dropped'
 ALTER TABLE t1 DROP INDEX c2d, DROP INDEX c2f;
 ERROR 42S02: Table 'test.t1' doesn't exist
-ALTER TABLE t1 ADD INDEX c2h(c2), ALGORITHM=INPLACE;
+ALTER TABLE t1 ADD INDEX c2h(c22f), ALGORITHM = INPLACE;
 ERROR 42S02: Table 'test.t1' doesn't exist
-ALTER TABLE t1 ADD INDEX c2h(c2), ALGORITHM=COPY;
+ALTER TABLE t1 ADD INDEX c2h(c22f), ALGORITHM = COPY;
 ERROR 42S02: Table 'test.t1' doesn't exist
-SET GLOBAL innodb_file_per_table=0;
-SET DEBUG_SYNC='RESET';
-SET DEBUG='-d,debug_sync_abort_on_timeout';
-SET DEBUG='';
-SET GLOBAL innodb_monitor_disable=module_ddl;
+SET DEBUG_SYNC = 'RESET';
+SET DEBUG = '';
+SET GLOBAL innodb_monitor_disable = module_ddl;
 DROP TABLE t1;
-SET GLOBAL DEBUG='';
-SET GLOBAL innodb_monitor_enable=default;
-SET GLOBAL innodb_monitor_disable=default;
+SET GLOBAL DEBUG = '';
+SET GLOBAL innodb_file_per_table = @global_innodb_file_per_table_orig;
+SET GLOBAL innodb_monitor_enable  = default;
+SET GLOBAL innodb_monitor_disable = default;

=== modified file 'mysql-test/suite/innodb/t/innodb-index-online.test'
--- a/mysql-test/suite/innodb/t/innodb-index-online.test	revid:marko.makela@oracle.com-20120419090901-f20rhn6fgm8lwfk6
+++ b/mysql-test/suite/innodb/t/innodb-index-online.test	revid:marko.makela@stripped
@@ -1,9 +1,8 @@
 --source include/have_innodb.inc
 --source include/have_debug_sync.inc
 
-SET GLOBAL DEBUG='d,query,debug_sync_exec:i:t:A,ds.trace';
-SET DEBUG='+d,debug_sync_abort_on_timeout';
-SET DEBUG='d,query,debug_sync_exec:i:t:A,ds.trace';
+let $innodb_metrics_select=
+SELECT name, count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
 
 call mtr.add_suppression("InnoDB: Warning: Small buffer pool size");
 # These will be triggered by INSERT INTO t1 VALUES(6,3,1);
@@ -14,50 +13,48 @@ call mtr.add_suppression("InnoDB: Error:
 call mtr.add_suppression("MySQL is trying to open a table handle but the .ibd file for");
 
 # DISCARD TABLESPACE needs file-per-table
-let $per_table=`select @@innodb_file_per_table`;
-SET GLOBAL innodb_file_per_table=on;
+SET @global_innodb_file_per_table_orig = @@global.innodb_file_per_table;
+SET GLOBAL innodb_file_per_table = on;
 
 # Save the initial number of concurrent sessions.
 --source include/count_sessions.inc
 
-CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 INT, c3 INT) ENGINE=InnoDB;
-INSERT INTO t1 VALUES (1,1,0),(2,2,0),(3,3,0),(4,4,0),(5,5,0);
+CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 INT, c3 TEXT) ENGINE = InnoDB;
+INSERT INTO t1 VALUES (1,1,''), (2,2,''), (3,3,''), (4,4,''), (5,5,'');
 
-SET GLOBAL innodb_monitor_enable=module_ddl;
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+SET GLOBAL innodb_monitor_enable = module_ddl;
+eval $innodb_metrics_select;
 
-SET DEBUG_SYNC='RESET';
-SET DEBUG_SYNC='write_row_noreplace SIGNAL have_handle WAIT_FOR go_ahead';
--- send
+SET DEBUG_SYNC = 'RESET';
+SET DEBUG_SYNC = 'write_row_noreplace SIGNAL have_handle WAIT_FOR go_ahead';
+--send
 INSERT INTO t1 VALUES(1,2,3);
 
 connect (con1,localhost,root,,);
 connection con1;
 
-SET DEBUG='+d,debug_sync_abort_on_timeout';
-SET DEBUG='d,query,debug_sync_exec:i:t:A,ds.trace';
 # This should block at the end because of the INSERT in connection default
 # is holding a metadata lock.
-SET DEBUG_SYNC='now WAIT_FOR have_handle';
-SET lock_wait_timeout=1;
+SET DEBUG_SYNC = 'now WAIT_FOR have_handle';
+SET lock_wait_timeout = 1;
 --error ER_LOCK_WAIT_TIMEOUT
 ALTER TABLE t1 ADD UNIQUE INDEX(c2);
-SET DEBUG_SYNC='now SIGNAL go_ahead';
+SET DEBUG_SYNC = 'now SIGNAL go_ahead';
 
 connection default;
 --error ER_DUP_ENTRY
 reap;
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+eval $innodb_metrics_select;
 
 connection con1;
-SET SESSION DEBUG='+d,innodb_OOM_prepare_inplace_alter';
+SET SESSION DEBUG = '+d,innodb_OOM_prepare_inplace_alter';
 --error ER_OUT_OF_RESOURCES
 ALTER TABLE t1 ADD UNIQUE INDEX(c2);
-SET SESSION DEBUG='-d,innodb_OOM_prepare_inplace_alter';
-SET SESSION DEBUG='+d,innodb_OOM_inplace_alter';
+SET SESSION DEBUG = '-d,innodb_OOM_prepare_inplace_alter';
+SET SESSION DEBUG = '+d,innodb_OOM_inplace_alter';
 --error ER_OUT_OF_RESOURCES
 CREATE UNIQUE INDEX c2 ON t1(c2);
-SET SESSION DEBUG='-d,innodb_OOM_inplace_alter';
+SET SESSION DEBUG = '-d,innodb_OOM_inplace_alter';
 CREATE UNIQUE INDEX c2 ON t1(c2);
 DROP INDEX c2 ON t1;
 
@@ -70,7 +67,7 @@ INSERT INTO t1 VALUES(7,4,2);
 connection con1;
 # This DEBUG_SYNC should not kick in yet, because the duplicate key will be
 # detected before we get a chance to apply the online log.
-SET DEBUG_SYNC='row_log_apply_before SIGNAL scanned WAIT_FOR rollback_done';
+SET DEBUG_SYNC = 'row_log_apply_before SIGNAL scanned WAIT_FOR rollback_done';
 # This will be a lock wait timeout on the meta-data lock,
 # because the transaction inserting (7,4,2) is still active.
 --error ER_LOCK_WAIT_TIMEOUT
@@ -81,27 +78,27 @@ connection con1;
 --error ER_DUP_ENTRY
 ALTER TABLE t1 ADD UNIQUE INDEX(c2);
 connection default;
-DELETE FROM t1 WHERE c1=7;
+DELETE FROM t1 WHERE c1 = 7;
 connection con1;
 # ADD FOREIGN KEY is not supported in-place
 --error ER_NOT_SUPPORTED_YET
-ALTER TABLE t1 ADD FOREIGN KEY(c2) REFERENCES t1(c2), ALGORITHM=INPLACE;
+ALTER TABLE t1 ADD FOREIGN KEY(c2) REFERENCES t1(c2), ALGORITHM = INPLACE;
 # The previous DEBUG_SYNC should be ignored, because an exclusive lock
 # has been requested and the online log is not being allocated.
-ALTER TABLE t1 ADD UNIQUE INDEX(c2), LOCK=EXCLUSIVE, ALGORITHM=INPLACE;
+ALTER TABLE t1 ADD UNIQUE INDEX(c2), LOCK = EXCLUSIVE, ALGORITHM = INPLACE;
 DROP INDEX c2 ON t1;
 # Now the previous DEBUG_SYNC should kick in.
 --send
 ALTER TABLE t1 ADD UNIQUE INDEX(c2);
 connection default;
-SET DEBUG_SYNC='now WAIT_FOR scanned';
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+SET DEBUG_SYNC = 'now WAIT_FOR scanned';
+eval $innodb_metrics_select;
 
 # Insert a duplicate entry (4) for the already started UNIQUE INDEX(c2).
 BEGIN;
 INSERT INTO t1 VALUES(7,4,2);
 ROLLBACK;
-SET DEBUG_SYNC='now SIGNAL rollback_done';
+SET DEBUG_SYNC = 'now SIGNAL rollback_done';
 
 connection con1;
 # Because the modification log will be applied in order, there will be
@@ -109,76 +106,82 @@ connection con1;
 --error ER_DUP_ENTRY
 reap;
 # Now, create the index without any concurrent DML, while no duplicate exists.
-SET DEBUG_SYNC='row_log_apply_after SIGNAL created WAIT_FOR dml_done';
+SET DEBUG_SYNC = 'row_log_apply_after SIGNAL created WAIT_FOR dml_done';
 --send
 ALTER TABLE t1 ADD UNIQUE INDEX(c2);
 connection default;
-SET DEBUG_SYNC='now WAIT_FOR created';
+SET DEBUG_SYNC = 'now WAIT_FOR created';
 # At this point, the index has been created inside InnoDB but not yet
 # in the MySQL data dictionary.
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+eval $innodb_metrics_select;
 # A duplicate key error should now be triggered by InnoDB.
 --error ER_DUP_KEY
 INSERT INTO t1 VALUES(6,3,1);
-SET DEBUG_SYNC='now SIGNAL dml_done';
+SET DEBUG_SYNC = 'now SIGNAL dml_done';
 connection con1;
 reap;
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+eval $innodb_metrics_select;
 
 connection default;
 --error ER_DUP_ENTRY
 INSERT INTO t1 VALUES(6,3,1);
 --error ER_DUP_ENTRY
 INSERT INTO t1 VALUES(7,4,2);
-SET SESSION innodb_analyze_is_persistent=1;
+SET SESSION innodb_analyze_is_persistent = 1;
 ANALYZE TABLE t1;
+# Purge may or may not have cleaned up the DELETE FROM t1 WHERE c1 = 7;
+UPDATE mysql.innodb_index_stats SET stat_value=5
+WHERE database_name='test' AND table_name='t1' AND index_name='PRIMARY'
+AND stat_value=6;
 --replace_column 4 LAST_UPDATE
 SELECT * FROM mysql.innodb_index_stats;
 CREATE TABLE t1_c2_stats SELECT * FROM mysql.innodb_index_stats
-WHERE database_name='test' AND table_name='t1' and index_name='c2';
+WHERE database_name = 'test' AND table_name = 't1' and index_name = 'c2';
 DROP INDEX c2 ON t1;
 --replace_column 4 LAST_UPDATE
 SELECT * FROM mysql.innodb_index_stats;
 
 connection con1;
-LET $ID=`SELECT @id:=CONNECTION_ID()`;
+let $ID= `SELECT @id := CONNECTION_ID()`;
 --error ER_QUERY_INTERRUPTED
 KILL QUERY @id;
 
-SET DEBUG_SYNC='row_log_apply_before SIGNAL c2d_created WAIT_FOR kill_done';
+SET DEBUG_SYNC = 'row_log_apply_before SIGNAL c2d_created WAIT_FOR kill_done';
 --send
 CREATE INDEX c2d ON t1(c2);
 
 connection default;
-SET DEBUG_SYNC='now WAIT_FOR c2d_created';
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
-let $ignore=`SELECT @id:=$ID`;
+SET DEBUG_SYNC = 'now WAIT_FOR c2d_created';
+eval $innodb_metrics_select;
+let $ignore= `SELECT @id := $ID`;
 KILL QUERY @id;
-SET DEBUG_SYNC='now SIGNAL kill_done';
+SET DEBUG_SYNC = 'now SIGNAL kill_done';
 
 connection con1;
 --error ER_QUERY_INTERRUPTED
 reap;
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+eval $innodb_metrics_select;
 
 connection default;
 CHECK TABLE t1;
-INSERT INTO t1 SELECT 5+c1,c2,c3 FROM t1;
-INSERT INTO t1 SELECT 10+c1,c2,c3 FROM t1;
-INSERT INTO t1 SELECT 20+c1,c2,c3 FROM t1;
-INSERT INTO t1 SELECT 40+c1,c2,c3 FROM t1;
-EXPLAIN SELECT COUNT(*) FROM t1 WHERE c2>3;
+INSERT INTO t1 SELECT  5 + c1, c2,  c3 FROM t1;
+INSERT INTO t1 SELECT 10 + c1, c2, c3 FROM t1;
+INSERT INTO t1 SELECT 20 + c1, c2, c3 FROM t1;
+INSERT INTO t1 SELECT 40 + c1, c2, c3 FROM t1;
+# Purge may or may not have cleaned up the DELETE FROM t1 WHERE c1 = 7;
+--replace_result 6 5
+EXPLAIN SELECT COUNT(*) FROM t1 WHERE c2 > 3;
 ANALYZE TABLE t1;
 
 connection con1;
 # Forge some statistics for c2d, and see that they will be used
-UPDATE t1_c2_stats SET index_name='c2d';
+UPDATE t1_c2_stats SET index_name = 'c2d';
 # Fake the statistics. The cardinality should be 5,80.
-UPDATE t1_c2_stats SET stat_value=2 WHERE stat_name='n_diff_pfx01';
+UPDATE t1_c2_stats SET stat_value = 2 WHERE stat_name = 'n_diff_pfx01';
 INSERT INTO t1_c2_stats
-SELECT database_name,table_name,index_name,last_update,'n_diff_pfx02',80,
-sample_size,'c2,c1' FROM t1_c2_stats
-WHERE stat_name='n_diff_pfx01' AND stat_description='c2';
+SELECT database_name, table_name, index_name, last_update, 'n_diff_pfx02', 80,
+sample_size, 'c2,c1' FROM t1_c2_stats
+WHERE stat_name = 'n_diff_pfx01' AND stat_description = 'c2';
 INSERT INTO mysql.innodb_index_stats SELECT * FROM t1_c2_stats;
 DROP TABLE t1_c2_stats;
 
@@ -186,105 +189,161 @@ CREATE INDEX c2d ON t1(c2);
 # This should reflect the cardinality that was faked above.
 # It is not an exact match because of the rec_per_key translation and tweaks.
 SHOW INDEX FROM t1;
-EXPLAIN SELECT COUNT(*) FROM t1 WHERE c2>3;
+EXPLAIN SELECT COUNT(*) FROM t1 WHERE c2 > 3;
 
 SHOW CREATE TABLE t1;
 
 # Exceed the configured innodb_online_alter_log_max_size.
 # The actual limit is a multiple of innodb_sort_buf_size,
 # because that is the size of the in-memory log buffers.
-SET DEBUG_SYNC='row_log_apply_before SIGNAL c2e_created WAIT_FOR dml2_done';
+SET DEBUG_SYNC = 'row_log_apply_before SIGNAL c2e_created WAIT_FOR dml2_done';
 # Ensure that the ALTER TABLE will be executed even with some concurrent DML.
-SET lock_wait_timeout=10;
+SET lock_wait_timeout = 10;
 --send
-ALTER TABLE t1 DROP INDEX c2d, ADD INDEX c2e(c2);
+ALTER TABLE t1 CHANGE c2 c22 INT, DROP INDEX c2d, ADD INDEX c2e(c22),
+ALGORITHM=INPLACE;
 
 # Generate some log (delete-mark, delete-unmark, insert etc.)
 # while the index creation is blocked. Some of this may run
 # in parallel with the clustered index scan.
 connection default;
-INSERT INTO t1 SELECT 80+c1,c2,c3 FROM t1;
-INSERT INTO t1 SELECT 160+c1,c2,c3 FROM t1;
-UPDATE t1 SET c2=c2+1;
-SET DEBUG_SYNC='now WAIT_FOR c2e_created';
+INSERT INTO t1 SELECT  80 + c1, c2, c3 FROM t1;
+INSERT INTO t1 SELECT 160 + c1, c2, c3 FROM t1;
+UPDATE t1 SET c2 = c2 + 1;
+SET DEBUG_SYNC = 'now WAIT_FOR c2e_created';
 # At this point, the clustered index scan must have completed,
 # but the modification log keeps accumulating due to the DEBUG_SYNC.
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
-let $c=2;
+eval $innodb_metrics_select;
+let $c= 2;
 while ($c)
 {
   BEGIN;
   DELETE FROM t1;
   ROLLBACK;
-  UPDATE t1 SET c2=c2+1;
+  UPDATE t1 SET c2 = c2 + 1;
   BEGIN;
-  UPDATE t1 SET c2=c2+1;
+  UPDATE t1 SET c2 = c2 + 1;
   DELETE FROM t1;
   ROLLBACK;
   dec $c;
 }
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+# Incomplete index c2e should exist until the DDL thread notices the overflow.
+# (The output below strips TEMP_INDEX_PREFIX from the name.)
+eval $innodb_metrics_select;
+SELECT sf.name, sf.pos FROM INFORMATION_SCHEMA.INNODB_SYS_INDEXES si
+INNER JOIN INFORMATION_SCHEMA.INNODB_SYS_FIELDS sf
+ON si.index_id = sf.index_id WHERE si.name = 'c2e';
+
 # Release con1.
-SET DEBUG_SYNC='now SIGNAL dml2_done';
+SET DEBUG_SYNC = 'now SIGNAL dml2_done';
 
 connection con1;
 # If the following fails with the wrong error, it probably means that
 # you should rerun with a larger mtr --debug-sync-timeout.
 --error ER_INNODB_ONLINE_LOG_TOO_BIG
 reap;
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+# The index c2e should have been dropped from the data dictionary
+# when the above error was noticed. It should still exist in the
+# cache with index->online_status=ONLINE_INDEX_ABORTED_DROPPED.
+eval $innodb_metrics_select;
+SELECT sf.name, sf.pos FROM INFORMATION_SCHEMA.INNODB_SYS_INDEXES si
+INNER JOIN INFORMATION_SCHEMA.INNODB_SYS_FIELDS sf
+ON si.index_id = sf.index_id WHERE si.name = 'c2e';
+
+# ddl_background_drop_indexes=1 here, because the incomplete index c2e still
+# exists in the InnoDB data dictionary cache.
+eval $innodb_metrics_select;
 
 connection default;
 
 ALTER TABLE t1 COMMENT 'testing if c2e will be dropped';
 
 # Check that the 'zombie' index c2e was dropped.
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
-SET GLOBAL innodb_monitor_disable=module_ddl;
+eval $innodb_metrics_select;
 
 connection con1;
 # Accumulate and apply some modification log.
-SET DEBUG_SYNC='row_log_apply_before SIGNAL c2f_created WAIT_FOR dml3_done';
+SET DEBUG_SYNC = 'row_log_apply_before SIGNAL c2f_created WAIT_FOR dml3_done';
 --send
-ALTER TABLE t1 ADD INDEX c2f(c2);
+ALTER TABLE t1 ADD INDEX c2f(c22f), CHANGE c2 c22f INT;
 
 connection default;
-SET DEBUG_SYNC='now WAIT_FOR c2f_created';
+SET DEBUG_SYNC = 'now WAIT_FOR c2f_created';
 # Generate some log (delete-mark, delete-unmark, insert etc.)
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+eval $innodb_metrics_select;
 BEGIN;
-INSERT INTO t1 SELECT 320+c1,c2,c3 FROM t1 WHERE c1>160;
-DELETE FROM t1 WHERE c1>320;
+INSERT INTO t1 SELECT 320 + c1, c2, c3 FROM t1 WHERE c1 > 160;
+DELETE FROM t1 WHERE c1 > 320;
 ROLLBACK;
 BEGIN;
-UPDATE t1 SET c2=c2+1;
+UPDATE t1 SET c2 = c2 + 1;
 DELETE FROM t1;
 ROLLBACK;
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
+eval $innodb_metrics_select;
 # Release con1.
-SET DEBUG_SYNC='now SIGNAL dml3_done';
+SET DEBUG_SYNC = 'now SIGNAL dml3_done';
 
 connection con1;
 reap;
-SELECT name,count FROM INFORMATION_SCHEMA.INNODB_METRICS WHERE subsystem='ddl';
-SELECT COUNT(c2) FROM t1;
+eval $innodb_metrics_select;
+SELECT COUNT(c22f) FROM t1;
 CHECK TABLE t1;
 
+# Create a column prefix index.
+--error ER_DUP_ENTRY
+ALTER TABLE t1 ADD UNIQUE INDEX c3p5(c3(5));
+UPDATE t1 SET c3=NULL WHERE c3='';
+SET lock_wait_timeout=1;
+SET DEBUG_SYNC = 'row_log_apply_before SIGNAL c3p5_created WAIT_FOR ins_done';
+--send
+ALTER TABLE t1 ADD UNIQUE INDEX c3p5(c3(5));
+
 connection default;
-SET DEBUG_SYNC='row_log_apply_before SIGNAL c2g_created WAIT_FOR dml4_done';
+SET DEBUG_SYNC = 'now WAIT_FOR c3p5_created';
+
+# Check that the index was created.
+SELECT sf.name, sf.pos FROM INFORMATION_SCHEMA.INNODB_SYS_INDEXES si
+INNER JOIN INFORMATION_SCHEMA.INNODB_SYS_FIELDS sf
+ON si.index_id = sf.index_id WHERE si.name = 'c3p5';
+
+SET DEBUG_SYNC = 'ib_after_row_insert SIGNAL ins_done WAIT_FOR ddl_timed_out';
+--send
+INSERT INTO t1 VALUES(347,33101,NULL);
+
+connection con1;
+--error ER_LOCK_WAIT_TIMEOUT
+reap;
+SET DEBUG_SYNC = 'now SIGNAL ddl_timed_out';
+
+# InnoDB should have cleaned up the index c3p5 from the data dictionary,
+# but not yet from the dictionary cache.
+SELECT sf.name, sf.pos FROM INFORMATION_SCHEMA.INNODB_SYS_INDEXES si
+INNER JOIN INFORMATION_SCHEMA.INNODB_SYS_FIELDS sf
+ON si.index_id = sf.index_id WHERE si.name = 'c3p5';
+eval $innodb_metrics_select;
+
+connection default;
+reap;
+# Index c3p5 should still exist in the data dictionary cache.
+eval $innodb_metrics_select;
+
+ALTER TABLE t1 COMMENT 'testing if c3p5 will be dropped';
+eval $innodb_metrics_select;
+
+SET DEBUG_SYNC = 'row_log_apply_before SIGNAL c2g_created WAIT_FOR dml4_done';
 # The lock upgrade at the end of the ALTER will conflict with the DISCARD.
-SET lock_wait_timeout=1;
+SET lock_wait_timeout = 1;
 --send
-ALTER TABLE t1 DROP INDEX c2f, ADD INDEX c2g(c2);
+ALTER TABLE t1 DROP INDEX c2f, ADD INDEX c2g(c22f);
 
 connection con1;
-SET DEBUG_SYNC='now WAIT_FOR c2g_created';
+SET DEBUG_SYNC = 'now WAIT_FOR c2g_created';
 
 connect (con2,localhost,root,,);
 connection con2;
 
 # This will conflict with the ALTER in connection default, above.
-SET lock_wait_timeout=10;
+SET lock_wait_timeout = 10;
 --send
 ALTER TABLE t1 DISCARD TABLESPACE;
 
@@ -295,7 +354,7 @@ let $wait_condition=
         info = 'ALTER TABLE t1 DISCARD TABLESPACE';
 --source include/wait_condition.inc
 
-SET DEBUG_SYNC='now SIGNAL dml4_done';
+SET DEBUG_SYNC = 'now SIGNAL dml4_done';
 disconnect con1;
 connection con2;
 reap;
@@ -310,15 +369,13 @@ SHOW CREATE TABLE t1;
 --error ER_NO_SUCH_TABLE
 ALTER TABLE t1 DROP INDEX c2d, DROP INDEX c2f;
 --error ER_NO_SUCH_TABLE
-ALTER TABLE t1 ADD INDEX c2h(c2), ALGORITHM=INPLACE;
+ALTER TABLE t1 ADD INDEX c2h(c22f), ALGORITHM = INPLACE;
 --error ER_NO_SUCH_TABLE
-ALTER TABLE t1 ADD INDEX c2h(c2), ALGORITHM=COPY;
+ALTER TABLE t1 ADD INDEX c2h(c22f), ALGORITHM = COPY;
 
-eval SET GLOBAL innodb_file_per_table=$per_table;
-SET DEBUG_SYNC='RESET';
-SET DEBUG='-d,debug_sync_abort_on_timeout';
-SET DEBUG='';
-SET GLOBAL innodb_monitor_disable=module_ddl;
+SET DEBUG_SYNC = 'RESET';
+SET DEBUG = '';
+SET GLOBAL innodb_monitor_disable = module_ddl;
 
 DROP TABLE t1;
 
@@ -326,8 +383,9 @@ DROP TABLE t1;
 # gone so execution of other tests won't be affected by their presence.
 --source include/wait_until_count_sessions.inc
 
-SET GLOBAL DEBUG='';
--- disable_warnings
-SET GLOBAL innodb_monitor_enable=default;
-SET GLOBAL innodb_monitor_disable=default;
--- enable_warnings
+SET GLOBAL DEBUG = '';
+SET GLOBAL innodb_file_per_table = @global_innodb_file_per_table_orig;
+--disable_warnings
+SET GLOBAL innodb_monitor_enable  = default;
+SET GLOBAL innodb_monitor_disable = default;
+--enable_warnings

=== added file 'mysql-test/suite/innodb_fts/r/innodb_fts_opt.result'
--- a/mysql-test/suite/innodb_fts/r/innodb_fts_opt.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/innodb_fts/r/innodb_fts_opt.result	revid:marko.makela@strippedy1ce3
@@ -0,0 +1,825 @@
+CREATE TABLE wp(
+FTS_DOC_ID BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
+title VARCHAR(255) NOT NULL DEFAULT '',
+text MEDIUMTEXT NOT NULL,
+dummy INTEGER,
+PRIMARY KEY (FTS_DOC_ID),
+UNIQUE KEY FTS_DOC_ID_INDEX (FTS_DOC_ID),
+FULLTEXT KEY idx (title,text) 
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+INSERT INTO wp (title, text) VALUES
+('MySQL Tutorial','DBMS stands for MySQL DataBase ...'),
+('How To Use MySQL Well','After you went through a ...'),
+('Optimizing MySQL','In this tutorial we will show ...'),
+('1001 MySQL Tricks','1. Never run mysqld as root. 2. ...'),
+('MySQL vs. YourSQL','In the following database to database comparison ...'),
+('MySQL Security','When configured properly, MySQL ...');
+CREATE TABLE t1 (i INTEGER);
+INSERT INTO t1 SELECT FTS_DOC_ID FROM wp;
+SELECT FTS_DOC_ID, title, MATCH(title, text) AGAINST ('database') AS score1,
+MATCH(title, text) AGAINST ('mysql') AS score2 
+FROM wp;
+FTS_DOC_ID	title	score1	score2
+1	MySQL Tutorial	0.22764469683170319	0.000000003771856604828372
+2	How To Use MySQL Well	0	0.000000001885928302414186
+3	Optimizing MySQL	0	0.000000001885928302414186
+4	1001 MySQL Tricks	0	0.000000001885928302414186
+5	MySQL vs. YourSQL	0.45528939366340637	0.000000001885928302414186
+6	MySQL Security	0	0.000000003771856604828372
+No sorting for this query
+FLUSH STATUS;
+SELECT title, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database')
+ORDER BY score DESC;
+title	score
+MySQL vs. YourSQL	0.45528939366340637
+MySQL Tutorial	0.22764469683170319
+SHOW SESSION STATUS LIKE 'Sort%';
+Variable_name	Value
+Sort_merge_passes	0
+Sort_range	0
+Sort_rows	0
+Sort_scan	0
+No sorting for this query even if MATCH is part of an expression
+FLUSH STATUS;
+SELECT title, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database') > 0.1
+ORDER BY score DESC;
+title	score
+MySQL vs. YourSQL	0.45528939366340637
+MySQL Tutorial	0.22764469683170319
+SHOW SESSION STATUS LIKE 'Sort%';
+Variable_name	Value
+Sort_merge_passes	0
+Sort_range	0
+Sort_rows	0
+Sort_scan	0
+No sorting even if there are several MATCH expressions as long as the
+right one is used in ORDER BY
+FLUSH STATUS;
+SELECT title, MATCH(title, text) AGAINST ('database') AS score1,
+MATCH(title, text) AGAINST ('mysql') AS score2 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database')
+ORDER BY score1 DESC;
+title	score1	score2
+MySQL vs. YourSQL	0.45528939366340637	0.000000001885928302414186
+MySQL Tutorial	0.22764469683170319	0.000000003771856604828372
+SHOW SESSION STATUS LIKE 'Sort%';
+Variable_name	Value
+Sort_merge_passes	0
+Sort_range	0
+Sort_rows	0
+Sort_scan	0
+Sorting since it is not a single table query
+FLUSH STATUS;
+SELECT title, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp, t1 
+WHERE MATCH(title, text) AGAINST ('database') AND FTS_DOC_ID = t1.i
+ORDER BY score DESC;
+title	score
+MySQL vs. YourSQL	0.45528939366340637
+MySQL Tutorial	0.22764469683170319
+SHOW SESSION STATUS LIKE 'Sort_rows%';
+Variable_name	Value
+Sort_rows	2
+Sorting since there is no WHERE clause
+FLUSH STATUS;
+SELECT title, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+ORDER BY score DESC;
+title	score
+MySQL vs. YourSQL	0.45528939366340637
+MySQL Tutorial	0.22764469683170319
+How To Use MySQL Well	0
+Optimizing MySQL	0
+1001 MySQL Tricks	0
+MySQL Security	0
+SHOW SESSION STATUS LIKE 'Sort_rows%';
+Variable_name	Value
+Sort_rows	6
+Sorting since ordering on multiple columns
+FLUSH STATUS;
+SELECT title, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database')
+ORDER BY score DESC, FTS_DOC_ID;
+title	score
+MySQL vs. YourSQL	0.45528939366340637
+MySQL Tutorial	0.22764469683170319
+SHOW SESSION STATUS LIKE 'Sort_rows%';
+Variable_name	Value
+Sort_rows	2
+Sorting since ordering is not descending
+FLUSH STATUS;
+SELECT title, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database')
+ORDER BY score ASC;
+title	score
+MySQL Tutorial	0.22764469683170319
+MySQL vs. YourSQL	0.45528939366340637
+SHOW SESSION STATUS LIKE 'Sort_rows%';
+Variable_name	Value
+Sort_rows	2
+Sorting because one is ordering on a different MATCH expression
+FLUSH STATUS;
+SELECT title, MATCH(title, text) AGAINST ('mysql') AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database')
+ORDER BY score DESC;
+title	score
+MySQL Tutorial	0.000000003771856604828372
+MySQL vs. YourSQL	0.000000001885928302414186
+SHOW SESSION STATUS LIKE 'Sort_rows%';
+Variable_name	Value
+Sort_rows	2
+No sorting for this query
+FLUSH STATUS;
+SELECT title, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+ORDER BY score DESC LIMIT 2;
+title	score
+MySQL vs. YourSQL	0.45528939366340637
+MySQL Tutorial	0.22764469683170319
+SHOW SESSION STATUS LIKE 'Sort%';
+Variable_name	Value
+Sort_merge_passes	0
+Sort_range	0
+Sort_rows	0
+Sort_scan	0
+Revert to table scan and sorting for this query since not
+enough matching rows to satisfy LIMIT clause
+FLUSH STATUS;
+SELECT title, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+ORDER BY score DESC LIMIT 3;
+title	score
+MySQL vs. YourSQL	0.45528939366340637
+MySQL Tutorial	0.22764469683170319
+How To Use MySQL Well	0
+SHOW SESSION STATUS LIKE 'Handler_read%';
+Variable_name	Value
+Handler_read_first	1
+Handler_read_key	4
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	3
+Handler_read_rnd_next	7
+SHOW SESSION STATUS LIKE 'Sort_rows%';
+Variable_name	Value
+Sort_rows	3
+Sorting since no LIMIT clause
+FLUSH STATUS;
+SELECT title, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+ORDER BY score DESC;
+title	score
+MySQL vs. YourSQL	0.45528939366340637
+MySQL Tutorial	0.22764469683170319
+How To Use MySQL Well	0
+Optimizing MySQL	0
+1001 MySQL Tricks	0
+MySQL Security	0
+SHOW SESSION STATUS LIKE 'Sort_rows%';
+Variable_name	Value
+Sort_rows	6
+Sorting since there is a WHERE clause
+FLUSH STATUS;
+SELECT title, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp
+WHERE dummy IS NULL
+ORDER BY score DESC LIMIT 2;
+title	score
+MySQL vs. YourSQL	0.45528939366340637
+MySQL Tutorial	0.22764469683170319
+SHOW SESSION STATUS LIKE 'Sort_rows%';
+Variable_name	Value
+Sort_rows	2
+Sorting since ordering is not on a simple MATCH expressions
+FLUSH STATUS;
+SELECT title, (MATCH(title, text) AGAINST ('database')) * 100 AS score 
+FROM wp 
+ORDER BY score DESC LIMIT 2;
+title	score
+MySQL vs. YourSQL	45.52893936634064
+MySQL Tutorial	22.76446968317032
+SHOW SESSION STATUS LIKE 'Sort_rows%';
+Variable_name	Value
+Sort_rows	2
+No ordinary handler accesses when only accessing FTS_DOC_ID and MATCH
+FLUSH STATUS;
+SELECT FTS_DOC_ID docid, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database');
+docid	score
+5	0.45528939366340637
+1	0.22764469683170319
+SHOW SESSION STATUS LIKE 'Handler_read%';
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	0
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	0
+Still no handler accesses when adding FTS_DOC_ID to WHERE clause
+FLUSH STATUS;
+SELECT FTS_DOC_ID docid, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database') AND FTS_DOC_ID > 2;
+docid	score
+5	0.45528939366340637
+SHOW SESSION STATUS LIKE 'Handler_read%';
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	0
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	0
+Still no handler accesses when ordering by MATCH expression
+FLUSH STATUS;
+SELECT FTS_DOC_ID docid, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database')
+ORDER BY score;
+docid	score
+1	0.22764469683170319
+5	0.45528939366340637
+SHOW SESSION STATUS LIKE 'Handler_read%';
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	2
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	2
+Handler_read_rnd_next	0
+Optimization is disabled when ordering on FTS_DOC_ID
+FLUSH STATUS;
+SELECT FTS_DOC_ID docid, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database')
+ORDER BY 1 DESC;
+docid	score
+5	0.45528939366340637
+1	0.22764469683170319
+SHOW SESSION STATUS LIKE 'Handler_read%';
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	2
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	2
+Handler_read_rnd_next	0
+Optimization also work with several MATCH expressions
+FLUSH STATUS;
+SELECT FTS_DOC_ID docid, MATCH(title, text) AGAINST ('database') AS score1,
+MATCH(title, text) AGAINST ('mysql') AS score2 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database');
+docid	score1	score2
+5	0.45528939366340637	0.000000001885928302414186
+1	0.22764469683170319	0.000000003771856604828372
+SHOW SESSION STATUS LIKE 'Handler_read%';
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	0
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	0
+Optimization does not apply if sorting on a different MATCH expressions
+from the one used to access the 
+FLUSH STATUS;
+SELECT FTS_DOC_ID docid, MATCH(title, text) AGAINST ('database') AS score1,
+MATCH(title, text) AGAINST ('mysql') AS score2 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database')
+ORDER BY score2 DESC;
+docid	score1	score2
+1	0.22764469683170319	0.000000003771856604828372
+5	0.45528939366340637	0.000000001885928302414186
+SHOW SESSION STATUS LIKE 'Handler_read%';
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	2
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	2
+Handler_read_rnd_next	0
+FLUSH STATUS;
+Optimization does not apply for GROUP BY
+SELECT FTS_DOC_ID, MATCH(title, text) AGAINST ('database') AS score
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database')
+GROUP BY score;
+FTS_DOC_ID	score
+1	0.22764469683170319
+5	0.45528939366340637
+SHOW SESSION STATUS LIKE 'Handler_read%';
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	0
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	2
+Handler_read_rnd_next	3
+No sorting and no table access with LIMIT clause and only information
+from FTS result
+FLUSH STATUS;
+SELECT FTS_DOC_ID docid, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+ORDER BY score DESC LIMIT 2;
+docid	score
+5	0.45528939366340637
+1	0.22764469683170319
+SHOW STATUS LIKE 'Handler_read%';
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	0
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	0
+SHOW SESSION STATUS LIKE 'Sort%';
+Variable_name	Value
+Sort_merge_passes	0
+Sort_range	0
+Sort_rows	0
+Sort_scan	0
+If count optimization applies, EXPLAIN shows 
+"Select tables optimized away."
+EXPLAIN SELECT COUNT(*) 
+FROM wp 
+WHERE MATCH(title,text) AGAINST ('database' IN NATURAL LANGUAGE MODE);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Select tables optimized away
+FLUSH STATUS;
+SELECT COUNT(*) 
+FROM wp 
+WHERE MATCH(title,text) AGAINST ('database' IN NATURAL LANGUAGE MODE);
+COUNT(*)
+2
+Verify that there was no table access
+SHOW STATUS LIKE 'Handler_read%';
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	0
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	0
+Optimization applies also to COUNT(expr) as long as expr is not nullable
+EXPLAIN SELECT COUNT(title) 
+FROM wp 
+WHERE MATCH(title,text) AGAINST ('database' IN NATURAL LANGUAGE MODE);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Select tables optimized away
+SELECT COUNT(title) 
+FROM wp 
+WHERE MATCH(title,text) AGAINST ('database' IN NATURAL LANGUAGE MODE);
+COUNT(title)
+2
+Optimization does not apply if not a single table query.
+EXPLAIN SELECT count(*)
+FROM wp, t1 
+WHERE MATCH(title, text) AGAINST ('database');
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	wp	fulltext	idx	idx	0	NULL	1	Using where
+1	SIMPLE	t1	ALL	NULL	NULL	NULL	NULL	6	NULL
+SELECT count(*)
+FROM wp, t1 
+WHERE MATCH(title, text) AGAINST ('database');
+count(*)
+12
+Optimization does not apply if MATCH is part of an expression
+EXPLAIN SELECT COUNT(title) 
+FROM wp 
+WHERE MATCH(title,text) AGAINST ('database' IN NATURAL LANGUAGE MODE) > 0;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	wp	fulltext	idx	idx	0	NULL	1	Using where
+SELECT COUNT(title) 
+FROM wp 
+WHERE MATCH(title,text) AGAINST ('database' IN NATURAL LANGUAGE MODE) > 0;
+COUNT(title)
+2
+Optimization does not apply if MATCH is part of an expression
+EXPLAIN SELECT COUNT(title) 
+FROM wp 
+WHERE MATCH(title,text) AGAINST ('database' IN NATURAL LANGUAGE MODE) > 0;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	wp	fulltext	idx	idx	0	NULL	1	Using where
+SELECT COUNT(title) 
+FROM wp 
+WHERE MATCH(title,text) AGAINST ('database' IN NATURAL LANGUAGE MODE) > 0;
+COUNT(title)
+2
+Optimization does not apply if COUNT expression is nullable
+EXPLAIN SELECT COUNT(dummy) 
+FROM wp 
+WHERE MATCH(title,text) AGAINST ('database' IN NATURAL LANGUAGE MODE);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	wp	fulltext	idx	idx	0	NULL	1	Using where
+SELECT COUNT(dummy) 
+FROM wp 
+WHERE MATCH(title,text) AGAINST ('database' IN NATURAL LANGUAGE MODE);
+COUNT(dummy)
+0
+FLUSH STATUS;
+SELECT title, 
+MATCH(title, text) AGAINST ('database' WITH QUERY EXPANSION) AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database' WITH QUERY EXPANSION)
+ORDER BY score DESC;
+title	score
+MySQL vs. YourSQL	2.2718474864959717
+MySQL Tutorial	1.6663280725479126
+Optimizing MySQL	0.22764469683170319
+MySQL Security	0.000000003771856604828372
+How To Use MySQL Well	0.000000001885928302414186
+1001 MySQL Tricks	0.000000001885928302414186
+SHOW SESSION STATUS LIKE 'Sort%';
+Variable_name	Value
+Sort_merge_passes	0
+Sort_range	0
+Sort_rows	0
+Sort_scan	0
+FLUSH STATUS;
+SELECT title,
+MATCH(title, text) AGAINST ('database' WITH QUERY EXPANSION) AS score 
+FROM wp 
+ORDER BY score DESC LIMIT 2;
+title	score
+MySQL vs. YourSQL	2.2718474864959717
+MySQL Tutorial	1.6663280725479126
+SHOW SESSION STATUS LIKE 'Sort%';
+Variable_name	Value
+Sort_merge_passes	0
+Sort_range	0
+Sort_rows	0
+Sort_scan	0
+FLUSH STATUS;
+SELECT FTS_DOC_ID docid, 
+MATCH(title, text) AGAINST ('database' WITH QUERY EXPANSION) AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database');
+docid	score
+5	2.2718474864959717
+1	1.6663280725479126
+SHOW SESSION STATUS LIKE 'Handler_read%';
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	0
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	0
+FLUSH STATUS;
+SELECT FTS_DOC_ID docid,
+MATCH(title, text) AGAINST ('database' WITH QUERY EXPANSION) AS score 
+FROM wp 
+ORDER BY score DESC LIMIT 2;
+docid	score
+5	2.2718474864959717
+1	1.6663280725479126
+SHOW STATUS LIKE 'Handler_read%';
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	0
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	0
+SHOW SESSION STATUS LIKE 'Sort%';
+Variable_name	Value
+Sort_merge_passes	0
+Sort_range	0
+Sort_rows	0
+Sort_scan	0
+EXPLAIN SELECT COUNT(*) 
+FROM wp 
+WHERE MATCH(title,text) AGAINST ('database' WITH QUERY EXPANSION);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Select tables optimized away
+FLUSH STATUS;
+SELECT COUNT(*) 
+FROM wp 
+WHERE MATCH(title,text) AGAINST ('database' WITH QUERY EXPANSION);
+COUNT(*)
+6
+SHOW STATUS LIKE 'Handler_read%';
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	0
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	0
+FLUSH STATUS;
+SELECT title, 
+MATCH(title, text) AGAINST ('+MySQL -database' IN BOOLEAN MODE) AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('+MySQL -database' IN BOOLEAN MODE)
+ORDER BY score DESC;
+title	score
+MySQL Security	0.000000003771856604828372
+How To Use MySQL Well	0.000000001885928302414186
+Optimizing MySQL	0.000000001885928302414186
+1001 MySQL Tricks	0.000000001885928302414186
+SHOW SESSION STATUS LIKE 'Sort%';
+Variable_name	Value
+Sort_merge_passes	0
+Sort_range	0
+Sort_rows	0
+Sort_scan	0
+FLUSH STATUS;
+SELECT title,
+MATCH(title, text) AGAINST ('+MySQL -database' IN BOOLEAN MODE) AS score 
+FROM wp 
+ORDER BY score DESC LIMIT 2;
+title	score
+MySQL Security	0.000000003771856604828372
+How To Use MySQL Well	0.000000001885928302414186
+SHOW SESSION STATUS LIKE 'Sort%';
+Variable_name	Value
+Sort_merge_passes	0
+Sort_range	0
+Sort_rows	0
+Sort_scan	0
+FLUSH STATUS;
+SELECT FTS_DOC_ID docid, 
+MATCH(title, text) AGAINST ('+MySQL -database' IN BOOLEAN MODE) AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('+MySQL -database');
+docid	score
+SHOW SESSION STATUS LIKE 'Handler_read%';
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	0
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	0
+FLUSH STATUS;
+SELECT FTS_DOC_ID docid,
+MATCH(title, text) AGAINST ('+MySQL -database' IN BOOLEAN MODE) AS score 
+FROM wp 
+ORDER BY score DESC LIMIT 2;
+docid	score
+6	0.000000003771856604828372
+2	0.000000001885928302414186
+SHOW STATUS LIKE 'Handler_read%';
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	0
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	0
+SHOW SESSION STATUS LIKE 'Sort%';
+Variable_name	Value
+Sort_merge_passes	0
+Sort_range	0
+Sort_rows	0
+Sort_scan	0
+EXPLAIN SELECT COUNT(*) 
+FROM wp 
+WHERE MATCH(title,text) AGAINST ('+MySQL -database' IN BOOLEAN MODE);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Select tables optimized away
+FLUSH STATUS;
+SELECT COUNT(*) 
+FROM wp 
+WHERE MATCH(title,text) AGAINST ('+MySQL -database' IN BOOLEAN MODE);
+COUNT(*)
+4
+SHOW STATUS LIKE 'Handler_read%';
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	0
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	0
+FLUSH STATUS;
+SELECT title, 
+MATCH(title, text) AGAINST ('"MySQL database"@10' IN BOOLEAN MODE) AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('"MySQL database"@10' IN BOOLEAN MODE)
+ORDER BY score DESC;
+title	score
+MySQL Tutorial	0.22764469683170319
+SHOW SESSION STATUS LIKE 'Sort%';
+Variable_name	Value
+Sort_merge_passes	0
+Sort_range	0
+Sort_rows	0
+Sort_scan	0
+FLUSH STATUS;
+SELECT title,
+MATCH(title, text) AGAINST ('"MySQL database"@10' IN BOOLEAN MODE) AS score 
+FROM wp 
+ORDER BY score DESC LIMIT 1;
+title	score
+MySQL Tutorial	0.22764469683170319
+SHOW SESSION STATUS LIKE 'Sort%';
+Variable_name	Value
+Sort_merge_passes	0
+Sort_range	0
+Sort_rows	0
+Sort_scan	0
+FLUSH STATUS;
+SELECT FTS_DOC_ID docid, 
+MATCH(title, text) AGAINST ('"MySQL database"@10' IN BOOLEAN MODE) AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('"MySQL database"@10');
+docid	score
+1	0.22764469683170319
+SHOW SESSION STATUS LIKE 'Handler_read%';
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	0
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	0
+FLUSH STATUS;
+SELECT FTS_DOC_ID docid,
+MATCH(title, text) AGAINST ('"MySQL database"@10' IN BOOLEAN MODE) AS score 
+FROM wp 
+ORDER BY score DESC LIMIT 1;
+docid	score
+1	0.22764469683170319
+SHOW STATUS LIKE 'Handler_read%';
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	0
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	0
+SHOW SESSION STATUS LIKE 'Sort%';
+Variable_name	Value
+Sort_merge_passes	0
+Sort_range	0
+Sort_rows	0
+Sort_scan	0
+EXPLAIN SELECT COUNT(*) 
+FROM wp 
+WHERE MATCH(title,text) AGAINST ('"MySQL database"@10' IN BOOLEAN MODE);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Select tables optimized away
+FLUSH STATUS;
+SELECT COUNT(*) 
+FROM wp 
+WHERE MATCH(title,text) AGAINST ('"MySQL database"@10' IN BOOLEAN MODE);
+COUNT(*)
+1
+SHOW STATUS LIKE 'Handler_read%';
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	0
+Handler_read_last	0
+Handler_read_next	0
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	0
+SELECT title, 
+MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database' WITH QUERY EXPANSION)
+ORDER BY score DESC;
+title	score
+MySQL vs. YourSQL	0.45528939366340637
+MySQL Tutorial	0.22764469683170319
+How To Use MySQL Well	0
+Optimizing MySQL	0
+1001 MySQL Tricks	0
+MySQL Security	0
+SELECT title, 
+MATCH(title, text) AGAINST ('+MySQL -database' IN BOOLEAN MODE) AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('MySQL database' WITH QUERY EXPANSION)
+ORDER BY score DESC;
+title	score
+MySQL Security	0.000000003771856604828372
+How To Use MySQL Well	0.000000001885928302414186
+Optimizing MySQL	0.000000001885928302414186
+1001 MySQL Tricks	0.000000001885928302414186
+MySQL Tutorial	0
+MySQL vs. YourSQL	0
+SELECT title, 
+MATCH(title, text) AGAINST ('+MySQL -database' IN BOOLEAN MODE) AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('"MySQL database"@10' IN BOOLEAN MODE)
+ORDER BY score DESC;
+title	score
+MySQL Tutorial	0
+ALTER TABLE wp ENGINE=myisam;
+FLUSH STATUS;
+SELECT title, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database')
+ORDER BY score DESC;
+title	score
+MySQL vs. YourSQL	0.9562782645225525
+MySQL Tutorial	0.5756555199623108
+SHOW SESSION STATUS LIKE 'Sort%';
+Variable_name	Value
+Sort_merge_passes	0
+Sort_range	1
+Sort_rows	2
+Sort_scan	0
+FLUSH STATUS;
+SELECT title, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+ORDER BY score DESC LIMIT 2;
+title	score
+MySQL vs. YourSQL	0.9562782645225525
+MySQL Tutorial	0.5756555199623108
+SHOW SESSION STATUS LIKE 'Sort%';
+Variable_name	Value
+Sort_merge_passes	0
+Sort_range	1
+Sort_rows	2
+Sort_scan	0
+FLUSH STATUS;
+SELECT FTS_DOC_ID docid, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database');
+docid	score
+5	0.9562782645225525
+1	0.5756555199623108
+SHOW SESSION STATUS LIKE 'Handler_read%';
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	0
+Handler_read_last	0
+Handler_read_next	3
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	0
+FLUSH STATUS;
+SELECT FTS_DOC_ID docid, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+ORDER BY score DESC LIMIT 2;
+docid	score
+5	0.9562782645225525
+1	0.5756555199623108
+SHOW STATUS LIKE 'Handler_read%';
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	0
+Handler_read_last	0
+Handler_read_next	3
+Handler_read_prev	0
+Handler_read_rnd	2
+Handler_read_rnd_next	0
+SHOW SESSION STATUS LIKE 'Sort%';
+Variable_name	Value
+Sort_merge_passes	0
+Sort_range	1
+Sort_rows	2
+Sort_scan	0
+EXPLAIN SELECT COUNT(*) 
+FROM wp 
+WHERE MATCH(title,text) AGAINST ('database' IN NATURAL LANGUAGE MODE);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	wp	fulltext	idx	idx	0	NULL	1	Using where
+FLUSH STATUS;
+SELECT COUNT(*) 
+FROM wp 
+WHERE MATCH(title,text) AGAINST ('database' IN NATURAL LANGUAGE MODE);
+COUNT(*)
+2
+SHOW STATUS LIKE 'Handler_read%';
+Variable_name	Value
+Handler_read_first	0
+Handler_read_key	0
+Handler_read_last	0
+Handler_read_next	3
+Handler_read_prev	0
+Handler_read_rnd	0
+Handler_read_rnd_next	0
+DROP TABLE wp, t1;

=== added file 'mysql-test/suite/innodb_fts/t/innodb_fts_opt.test'
--- a/mysql-test/suite/innodb_fts/t/innodb_fts_opt.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/innodb_fts/t/innodb_fts_opt.test	revid:marko.makela@stripped3x7ebpy1ce3
@@ -0,0 +1,534 @@
+#  
+# Tests for optimizations for InnoDB fulltext search (WL#6043)
+#
+
+CREATE TABLE wp(
+  FTS_DOC_ID BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
+  title VARCHAR(255) NOT NULL DEFAULT '',
+  text MEDIUMTEXT NOT NULL,
+  dummy INTEGER,
+  PRIMARY KEY (FTS_DOC_ID),
+  UNIQUE KEY FTS_DOC_ID_INDEX (FTS_DOC_ID),
+  FULLTEXT KEY idx (title,text) 
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;  
+
+INSERT INTO wp (title, text) VALUES
+  ('MySQL Tutorial','DBMS stands for MySQL DataBase ...'),
+  ('How To Use MySQL Well','After you went through a ...'),
+  ('Optimizing MySQL','In this tutorial we will show ...'),
+  ('1001 MySQL Tricks','1. Never run mysqld as root. 2. ...'),
+  ('MySQL vs. YourSQL','In the following database to database comparison ...'),
+  ('MySQL Security','When configured properly, MySQL ...');      
+
+CREATE TABLE t1 (i INTEGER);
+INSERT INTO t1 SELECT FTS_DOC_ID FROM wp;
+
+#
+#  Show results of MATCH expressions for reference
+#
+SELECT FTS_DOC_ID, title, MATCH(title, text) AGAINST ('database') AS score1,
+       MATCH(title, text) AGAINST ('mysql') AS score2 
+FROM wp;
+
+#
+# Test that filesort is not used if ordering on same match expression
+# as where clause
+#
+--echo No sorting for this query
+FLUSH STATUS;
+
+SELECT title, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database')
+ORDER BY score DESC;
+
+SHOW SESSION STATUS LIKE 'Sort%';
+
+--echo No sorting for this query even if MATCH is part of an expression
+FLUSH STATUS;
+
+SELECT title, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database') > 0.1
+ORDER BY score DESC;
+
+SHOW SESSION STATUS LIKE 'Sort%';
+
+--echo No sorting even if there are several MATCH expressions as long as the
+--echo right one is used in ORDER BY
+FLUSH STATUS;
+
+SELECT title, MATCH(title, text) AGAINST ('database') AS score1,
+       MATCH(title, text) AGAINST ('mysql') AS score2 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database')
+ORDER BY score1 DESC;
+
+SHOW SESSION STATUS LIKE 'Sort%';
+
+--echo Sorting since it is not a single table query
+FLUSH STATUS;
+
+SELECT title, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp, t1 
+WHERE MATCH(title, text) AGAINST ('database') AND FTS_DOC_ID = t1.i
+ORDER BY score DESC;
+
+SHOW SESSION STATUS LIKE 'Sort_rows%';
+
+--echo Sorting since there is no WHERE clause
+FLUSH STATUS;
+
+SELECT title, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+ORDER BY score DESC;
+
+SHOW SESSION STATUS LIKE 'Sort_rows%';
+
+--echo Sorting since ordering on multiple columns
+FLUSH STATUS;
+
+SELECT title, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database')
+ORDER BY score DESC, FTS_DOC_ID;
+  
+SHOW SESSION STATUS LIKE 'Sort_rows%';
+
+--echo Sorting since ordering is not descending
+FLUSH STATUS;
+
+SELECT title, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database')
+ORDER BY score ASC;
+  
+SHOW SESSION STATUS LIKE 'Sort_rows%';
+
+--echo Sorting because one is ordering on a different MATCH expression
+FLUSH STATUS;
+
+SELECT title, MATCH(title, text) AGAINST ('mysql') AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database')
+ORDER BY score DESC;
+  
+SHOW SESSION STATUS LIKE 'Sort_rows%';
+
+#
+#  Tests for ORDER BY/LIMIT optimzation
+#
+--echo No sorting for this query
+FLUSH STATUS;
+
+SELECT title, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+ORDER BY score DESC LIMIT 2;
+
+SHOW SESSION STATUS LIKE 'Sort%';
+
+--echo Revert to table scan and sorting for this query since not
+--echo enough matching rows to satisfy LIMIT clause
+FLUSH STATUS;
+
+SELECT title, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+ORDER BY score DESC LIMIT 3;
+
+SHOW SESSION STATUS LIKE 'Handler_read%';
+SHOW SESSION STATUS LIKE 'Sort_rows%';
+
+--echo Sorting since no LIMIT clause
+FLUSH STATUS;
+
+SELECT title, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+ORDER BY score DESC;
+
+SHOW SESSION STATUS LIKE 'Sort_rows%';
+
+--echo Sorting since there is a WHERE clause
+FLUSH STATUS;
+
+SELECT title, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp
+WHERE dummy IS NULL
+ORDER BY score DESC LIMIT 2;
+
+SHOW SESSION STATUS LIKE 'Sort_rows%';
+
+--echo Sorting since ordering is not on a simple MATCH expressions
+FLUSH STATUS;
+
+SELECT title, (MATCH(title, text) AGAINST ('database')) * 100 AS score 
+FROM wp 
+ORDER BY score DESC LIMIT 2;
+
+SHOW SESSION STATUS LIKE 'Sort_rows%';
+
+#
+#  Test that there is no row accesses if all necessary information is
+#  available in FTS result
+#
+--echo No ordinary handler accesses when only accessing FTS_DOC_ID and MATCH
+FLUSH STATUS;
+
+SELECT FTS_DOC_ID docid, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database');
+
+SHOW SESSION STATUS LIKE 'Handler_read%';
+
+--echo Still no handler accesses when adding FTS_DOC_ID to WHERE clause
+FLUSH STATUS;
+
+SELECT FTS_DOC_ID docid, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database') AND FTS_DOC_ID > 2;
+
+SHOW SESSION STATUS LIKE 'Handler_read%';
+
+--echo Still no handler accesses when ordering by MATCH expression
+FLUSH STATUS;
+
+SELECT FTS_DOC_ID docid, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database')
+ORDER BY score;
+
+SHOW SESSION STATUS LIKE 'Handler_read%';
+
+--echo Optimization is disabled when ordering on FTS_DOC_ID
+FLUSH STATUS;
+
+SELECT FTS_DOC_ID docid, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database')
+ORDER BY 1 DESC;
+
+SHOW SESSION STATUS LIKE 'Handler_read%';
+
+--echo Optimization also work with several MATCH expressions
+FLUSH STATUS;
+
+SELECT FTS_DOC_ID docid, MATCH(title, text) AGAINST ('database') AS score1,
+       MATCH(title, text) AGAINST ('mysql') AS score2 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database');
+
+SHOW SESSION STATUS LIKE 'Handler_read%';
+
+--echo Optimization does not apply if sorting on a different MATCH expressions
+--echo from the one used to access the 
+FLUSH STATUS;
+
+SELECT FTS_DOC_ID docid, MATCH(title, text) AGAINST ('database') AS score1,
+       MATCH(title, text) AGAINST ('mysql') AS score2 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database')
+ORDER BY score2 DESC;
+
+SHOW SESSION STATUS LIKE 'Handler_read%';
+
+FLUSH STATUS;
+
+--echo Optimization does not apply for GROUP BY
+SELECT FTS_DOC_ID, MATCH(title, text) AGAINST ('database') AS score
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database')
+GROUP BY score;
+
+SHOW SESSION STATUS LIKE 'Handler_read%';
+
+#
+#  Putting all three optimizations together
+#
+--echo No sorting and no table access with LIMIT clause and only information
+--echo from FTS result
+FLUSH STATUS;
+
+SELECT FTS_DOC_ID docid, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+ORDER BY score DESC LIMIT 2;
+
+SHOW STATUS LIKE 'Handler_read%';
+SHOW SESSION STATUS LIKE 'Sort%';
+
+#
+# Count optimization
+#
+let $query = 
+SELECT COUNT(*) 
+FROM wp 
+WHERE MATCH(title,text) AGAINST ('database' IN NATURAL LANGUAGE MODE);
+
+--echo If count optimization applies, EXPLAIN shows 
+--echo "Select tables optimized away."
+eval EXPLAIN $query;
+FLUSH STATUS;
+eval $query;
+--echo Verify that there was no table access
+SHOW STATUS LIKE 'Handler_read%';
+
+let $query = 
+SELECT COUNT(title) 
+FROM wp 
+WHERE MATCH(title,text) AGAINST ('database' IN NATURAL LANGUAGE MODE);
+
+--echo Optimization applies also to COUNT(expr) as long as expr is not nullable
+eval EXPLAIN $query;
+eval $query;
+
+let $query = 
+SELECT count(*)
+FROM wp, t1 
+WHERE MATCH(title, text) AGAINST ('database');
+
+--echo Optimization does not apply if not a single table query.
+eval EXPLAIN $query;
+eval $query;
+
+let $query = 
+SELECT COUNT(title) 
+FROM wp 
+WHERE MATCH(title,text) AGAINST ('database' IN NATURAL LANGUAGE MODE) > 0;
+
+--echo Optimization does not apply if MATCH is part of an expression
+eval EXPLAIN $query;
+eval $query;
+
+let $query = 
+SELECT COUNT(title) 
+FROM wp 
+WHERE MATCH(title,text) AGAINST ('database' IN NATURAL LANGUAGE MODE) > 0;
+
+--echo Optimization does not apply if MATCH is part of an expression
+eval EXPLAIN $query;
+eval $query;
+
+let $query = 
+SELECT COUNT(dummy) 
+FROM wp 
+WHERE MATCH(title,text) AGAINST ('database' IN NATURAL LANGUAGE MODE);
+
+--echo Optimization does not apply if COUNT expression is nullable
+eval EXPLAIN $query;
+eval $query;
+
+#
+#  Verify that the queries optimized for InnoDB works with QUERY EXPANSION
+#
+
+# Query will also avoid sorting when query expansion is used
+FLUSH STATUS;
+SELECT title, 
+       MATCH(title, text) AGAINST ('database' WITH QUERY EXPANSION) AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database' WITH QUERY EXPANSION)
+ORDER BY score DESC;
+SHOW SESSION STATUS LIKE 'Sort%';
+
+# Check ORDER BY/LIMIT query with no WHERE clause
+FLUSH STATUS;
+SELECT title,
+       MATCH(title, text) AGAINST ('database' WITH QUERY EXPANSION) AS score 
+FROM wp 
+ORDER BY score DESC LIMIT 2;
+SHOW SESSION STATUS LIKE 'Sort%';
+
+# Check query where FTS result is "covering"
+FLUSH STATUS;
+SELECT FTS_DOC_ID docid, 
+       MATCH(title, text) AGAINST ('database' WITH QUERY EXPANSION) AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database');
+SHOW SESSION STATUS LIKE 'Handler_read%';
+
+# Check the combination of all three
+FLUSH STATUS;
+SELECT FTS_DOC_ID docid,
+       MATCH(title, text) AGAINST ('database' WITH QUERY EXPANSION) AS score 
+FROM wp 
+ORDER BY score DESC LIMIT 2;
+SHOW STATUS LIKE 'Handler_read%';
+SHOW SESSION STATUS LIKE 'Sort%';
+
+# Check the count optimization
+let $query = 
+SELECT COUNT(*) 
+FROM wp 
+WHERE MATCH(title,text) AGAINST ('database' WITH QUERY EXPANSION);
+eval EXPLAIN $query;
+FLUSH STATUS;
+eval $query;
+SHOW STATUS LIKE 'Handler_read%';
+
+#
+#  Verify that the queries optimized for InnoDB works with BOOLEAN MODE
+#
+
+# Query will also avoid sorting when Boolean mode is used
+FLUSH STATUS;
+SELECT title, 
+       MATCH(title, text) AGAINST ('+MySQL -database' IN BOOLEAN MODE) AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('+MySQL -database' IN BOOLEAN MODE)
+ORDER BY score DESC;
+SHOW SESSION STATUS LIKE 'Sort%';
+
+# Check ORDER BY/LIMIT query with no WHERE clause
+FLUSH STATUS;
+SELECT title,
+       MATCH(title, text) AGAINST ('+MySQL -database' IN BOOLEAN MODE) AS score 
+FROM wp 
+ORDER BY score DESC LIMIT 2;
+SHOW SESSION STATUS LIKE 'Sort%';
+
+# Check query where FTS result is "covering"
+FLUSH STATUS;
+SELECT FTS_DOC_ID docid, 
+       MATCH(title, text) AGAINST ('+MySQL -database' IN BOOLEAN MODE) AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('+MySQL -database');
+SHOW SESSION STATUS LIKE 'Handler_read%';
+
+# Check the combination of all three
+FLUSH STATUS;
+SELECT FTS_DOC_ID docid,
+       MATCH(title, text) AGAINST ('+MySQL -database' IN BOOLEAN MODE) AS score 
+FROM wp 
+ORDER BY score DESC LIMIT 2;
+SHOW STATUS LIKE 'Handler_read%';
+SHOW SESSION STATUS LIKE 'Sort%';
+
+# Check the count optimization
+let $query = 
+SELECT COUNT(*) 
+FROM wp 
+WHERE MATCH(title,text) AGAINST ('+MySQL -database' IN BOOLEAN MODE);
+eval EXPLAIN $query;
+FLUSH STATUS;
+eval $query;
+SHOW STATUS LIKE 'Handler_read%';
+
+
+#
+#  Verify that the queries optimized for InnoDB works with 
+#  BOOLEAN proximity search
+#
+
+# Query will also avoid sorting when Boolean mode is used
+FLUSH STATUS;
+SELECT title, 
+       MATCH(title, text) AGAINST ('"MySQL database"@10' IN BOOLEAN MODE) AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('"MySQL database"@10' IN BOOLEAN MODE)
+ORDER BY score DESC;
+SHOW SESSION STATUS LIKE 'Sort%';
+
+# Check ORDER BY/LIMIT query with no WHERE clause
+FLUSH STATUS;
+SELECT title,
+       MATCH(title, text) AGAINST ('"MySQL database"@10' IN BOOLEAN MODE) AS score 
+FROM wp 
+ORDER BY score DESC LIMIT 1;
+SHOW SESSION STATUS LIKE 'Sort%';
+
+# Check query where FTS result is "covering"
+FLUSH STATUS;
+SELECT FTS_DOC_ID docid, 
+       MATCH(title, text) AGAINST ('"MySQL database"@10' IN BOOLEAN MODE) AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('"MySQL database"@10');
+SHOW SESSION STATUS LIKE 'Handler_read%';
+
+# Check the combination of all three
+FLUSH STATUS;
+SELECT FTS_DOC_ID docid,
+       MATCH(title, text) AGAINST ('"MySQL database"@10' IN BOOLEAN MODE) AS score 
+FROM wp 
+ORDER BY score DESC LIMIT 1;
+SHOW STATUS LIKE 'Handler_read%';
+SHOW SESSION STATUS LIKE 'Sort%';
+
+# Check the count optimization
+let $query = 
+SELECT COUNT(*) 
+FROM wp 
+WHERE MATCH(title,text) AGAINST ('"MySQL database"@10' IN BOOLEAN MODE);
+eval EXPLAIN $query;
+FLUSH STATUS;
+eval $query;
+SHOW STATUS LIKE 'Handler_read%';
+
+#
+# Check that nothing goes wrong when combining different modes
+#
+SELECT title, 
+       MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database' WITH QUERY EXPANSION)
+ORDER BY score DESC;
+
+SELECT title, 
+       MATCH(title, text) AGAINST ('+MySQL -database' IN BOOLEAN MODE) AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('MySQL database' WITH QUERY EXPANSION)
+ORDER BY score DESC;
+
+SELECT title, 
+       MATCH(title, text) AGAINST ('+MySQL -database' IN BOOLEAN MODE) AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('"MySQL database"@10' IN BOOLEAN MODE)
+ORDER BY score DESC;
+
+
+#
+#  Verify that the queries optimized for InnoDB still works with MyISAM
+#
+ALTER TABLE wp ENGINE=myisam;
+
+# Check avoid sorting query
+FLUSH STATUS;
+SELECT title, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database')
+ORDER BY score DESC;
+SHOW SESSION STATUS LIKE 'Sort%';
+
+# Check ORDER BY/LIMIT query with no WHERE clause
+FLUSH STATUS;
+SELECT title, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+ORDER BY score DESC LIMIT 2;
+SHOW SESSION STATUS LIKE 'Sort%';
+
+# Check query where FTS result is "covering"
+FLUSH STATUS;
+SELECT FTS_DOC_ID docid, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+WHERE MATCH(title, text) AGAINST ('database');
+SHOW SESSION STATUS LIKE 'Handler_read%';
+
+# Check the combination of all three
+FLUSH STATUS;
+SELECT FTS_DOC_ID docid, MATCH(title, text) AGAINST ('database') AS score 
+FROM wp 
+ORDER BY score DESC LIMIT 2;
+SHOW STATUS LIKE 'Handler_read%';
+SHOW SESSION STATUS LIKE 'Sort%';
+
+# Check the count optimization
+let $query = 
+SELECT COUNT(*) 
+FROM wp 
+WHERE MATCH(title,text) AGAINST ('database' IN NATURAL LANGUAGE MODE);
+eval EXPLAIN $query;
+FLUSH STATUS;
+eval $query;
+SHOW STATUS LIKE 'Handler_read%';
+
+
+DROP TABLE wp, t1;
+
+

=== added file 'mysql-test/suite/rpl/r/rpl_parallel_show_binlog_events_purge_logs.result'
--- a/mysql-test/suite/rpl/r/rpl_parallel_show_binlog_events_purge_logs.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/rpl/r/rpl_parallel_show_binlog_events_purge_logs.result	revid:marko.makela@stripped1ce3
@@ -0,0 +1,16 @@
+include/master-slave.inc
+Warnings:
+Note	####	Sending passwords in plain text without SSL/TLS is extremely insecure.
+Note	####	Storing MySQL user name or password information in the master.info repository is not secure and is therefore not recommended. Please see the MySQL Manual for more about this issue and possible alternatives.
+[connection master]
+[connection slave]
+SET DEBUG_SYNC= 'after_show_binlog_events SIGNAL on_show_binlog_events WAIT_FOR end';
+SHOW BINLOG EVENTS;
+[connection slave1]
+SET DEBUG_SYNC= 'now WAIT_FOR on_show_binlog_events';
+FLUSH LOGS;
+SET DEBUG_SYNC= 'now SIGNAL end';
+SET DEBUG_SYNC= 'RESET';
+[connection slave]
+SET DEBUG_SYNC= 'RESET';
+include/rpl_end.inc

=== modified file 'mysql-test/suite/rpl/t/rpl_parallel_change_master.test'
--- a/mysql-test/suite/rpl/t/rpl_parallel_change_master.test	revid:marko.makela@strippedn6fgm8lwfk6
+++ b/mysql-test/suite/rpl/t/rpl_parallel_change_master.test	revid:marko.makela@stripped835-9138g3x7ebpy1ce3
@@ -68,11 +68,19 @@ INSERT INTO d1.t1 VALUES (13); # to caus
 INSERT INTO d1.t1 VALUES (6);
 INSERT INTO d2.t1 VALUES (7);
 INSERT INTO d1.t1 VALUES (13);
-INSERT INTO d2.t1 VALUES (8);
+INSERT INTO d2.t1 VALUES (8);  # this worker will race over one inserting (13)
 INSERT INTO d2.t1 VALUES (9);
 
+--connection slave1
+# make sure workers doing d2.t1 raced the one that occupied  with d1.t1
+--let $count= 1
+--let $table= d2.t1
+--let $wait_condition= select count(*) = 1 from $table where a = 8
+--source include/wait_condition.inc
+
 --connection slave
-COMMIT; # worker executing (13) errors out
+# make worker executing (13) to error out
+COMMIT; 
 
 --let $slave_sql_errno= 1062
 --source include/wait_for_slave_sql_error.inc

=== added file 'mysql-test/suite/rpl/t/rpl_parallel_show_binlog_events_purge_logs.test'
--- a/mysql-test/suite/rpl/t/rpl_parallel_show_binlog_events_purge_logs.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/rpl/t/rpl_parallel_show_binlog_events_purge_logs.test	revid:marko.makela@strippede3
@@ -0,0 +1,35 @@
+# BUG#13979418: SHOW BINLOG EVENTS MAY CRASH THE SERVER
+#
+# The function mysql_show_binlog_events has a local stack variable
+# 'LOG_INFO linfo;', which is assigned to thd->current_linfo, however
+# this variable goes out of scope and is destroyed before clean
+# thd->current_linfo.
+#
+# This test case runs SHOW BINLOG EVENTS and FLUSH LOGS to make sure
+# that with the fix local variable linfo is valid along all
+# mysql_show_binlog_events function scope.
+#
+--source include/have_debug_sync.inc
+--source include/master-slave.inc
+
+--echo [connection slave]
+--connection slave
+SET DEBUG_SYNC= 'after_show_binlog_events SIGNAL on_show_binlog_events WAIT_FOR end';
+--send SHOW BINLOG EVENTS
+
+--connection slave1
+--echo [connection slave1]
+SET DEBUG_SYNC= 'now WAIT_FOR on_show_binlog_events';
+FLUSH LOGS;
+SET DEBUG_SYNC= 'now SIGNAL end';
+SET DEBUG_SYNC= 'RESET';
+
+--echo [connection slave]
+--connection slave
+--disable_result_log
+--reap
+--enable_result_log
+SET DEBUG_SYNC= 'RESET';
+
+--connection master
+--source include/rpl_end.inc

=== modified file 'mysql-test/t/archive.test'
--- a/mysql-test/t/archive.test	revid:marko.makela@stripped419090901-f20rhn6fgm8lwfk6
+++ b/mysql-test/t/archive.test	revid:marko.makela@strippedpy1ce3
@@ -1803,3 +1803,11 @@ CHECKSUM TABLE t1 EXTENDED;
 FLUSH TABLE t1;
 OPTIMIZE TABLE t1;
 DROP TABLE t1;
+
+--echo #
+--echo # Bug#13907676: HA_ARCHIVE::INFO
+--echo #
+CREATE TABLE t1 (a INT) ENGINE=ARCHIVE;
+CREATE TABLE t2 SELECT * FROM t1;
+SELECT * FROM t2;
+DROP TABLE t1, t2;

=== added file 'mysql-test/t/bug12427262.test'
--- a/mysql-test/t/bug12427262.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/bug12427262.test	revid:marko.makela@oracle.com-20120423104835-9138g3x7ebpy1ce3
@@ -0,0 +1,51 @@
+--echo #
+--echo # Bug#12427262 : 60961: SHOW TABLES VERY SLOW WHEN NOT IN SYSTEM DISK CACHE. 
+--echo #
+
+--source include/not_embedded.inc
+--source include/have_perfschema.inc
+
+--disable_warnings
+create database show_table_lw_db;
+use show_table_lw_db;
+create table t1 (c1 int);
+create table t2 (c1 int);
+create table t3 (c1 int);
+create table t4 (c1 int);
+create table t5 (c1 int);
+create table t6 (c1 int);
+create table t7 (c1 int);
+create table t8 (c1 int);
+create table t9 (c1 int);
+create table t10 (c1 int);
+--enable_warnings
+
+# Query PS to know initial read count for frm file.
+select Sum(ALL(COUNT_READ)) from performance_schema.file_summary_by_instance where FILE_NAME 
+like "%show_table_lw_db%" AND FILE_NAME like "%.frm%" AND EVENT_NAME='wait/io/file/sql/FRM'
+into @count_read_before;
+
+show tables;
+
+# Query PS to know read count for frm file after above query. It should
+# not be changed as FRM file will not be opened for above query.
+select Sum(ALL(COUNT_READ)) from performance_schema.file_summary_by_instance where FILE_NAME 
+like "%show_table_lw_db%" AND FILE_NAME like "%.frm%" AND EVENT_NAME='wait/io/file/sql/FRM'
+into @count_read_after;
+
+select @count_read_after-@count_read_before;
+
+show full tables;
+
+# Query PS to know read count for frm file after above query. COUNT_READ
+# will be incremented by 1 as FRM file will be opened for above query.
+select Sum(ALL(COUNT_READ)) from performance_schema.file_summary_by_instance where FILE_NAME 
+like "%show_table_lw_db%" AND FILE_NAME like "%.frm%" AND EVENT_NAME='wait/io/file/sql/FRM'
+into @count_read_after;
+
+select @count_read_after-@count_read_before;
+
+--disable_warnings
+drop table t1;
+drop database show_table_lw_db;
+--enable_warnings

=== modified file 'mysql-test/t/fulltext.test'
--- a/mysql-test/t/fulltext.test	revid:marko.makela@stripped901-f20rhn6fgm8lwfk6
+++ b/mysql-test/t/fulltext.test	revid:marko.makela@stripped3
@@ -623,7 +623,6 @@ PREPARE stmt FROM
  ON (MATCH(t1.f1) against (""))
  WHERE t1.f1 GROUP BY t1.f1))';
 
---echo # See BUG#12888306. Correct result is one row with value 1.
 EXECUTE stmt;
 EXECUTE stmt;
 

=== modified file 'mysql-test/t/group_by.test'
--- a/mysql-test/t/group_by.test	revid:marko.makela@strippedgm8lwfk6
+++ b/mysql-test/t/group_by.test	revid:marko.makela@stripped
@@ -1995,3 +1995,12 @@ let $query= SELECT MIN(a), b FROM t1 WHE
 
 --echo
 DROP TABLE t1;
+
+--echo #
+--echo # Bug #12888306 MISSING ROWS FOR SELECT >ALL (SUBQUERY WITHOUT ROWS)
+--echo #
+
+CREATE TABLE t1(a INT);
+INSERT INTO t1 VALUES (0);
+SELECT 1 FROM t1 WHERE 1 > ALL(SELECT 1 FROM t1 WHERE a);
+DROP TABLE t1;

=== modified file 'mysql-test/t/type_temporal_fractional.test'
--- a/mysql-test/t/type_temporal_fractional.test	revid:marko.makela@stripped0419090901-f20rhn6fgm8lwfk6
+++ b/mysql-test/t/type_temporal_fractional.test	revid:marko.makela@stripped3104835-9138g3x7ebpy1ce3
@@ -7665,4 +7665,12 @@ INSERT INTO t1 VALUES ('2001-01-01 01:01
 SELECT GREATEST(a,10), LEAST(a,10) FROM t1;
 DROP TABLE t1;
 
+--echo #
+--echo # Bug#13976233 ASSERTION FAILED: !CHECK_TIME_MMSSFF_RANGE(LTIME), FILE SQL_TIME.CC, LINE 304
+--echo #
+SELECT SECOND(4.99999999991e0);
+SELECT SECOND(-4.99999999991e0);
+SELECT SECOND(TRUNCATE('5',180));
+
+
 --echo # End of 5.6 tests

=== modified file 'sql/binlog.cc'
--- a/sql/binlog.cc	revid:marko.makela@strippedfgm8lwfk6
+++ b/sql/binlog.cc	revid:marko.makela@strippedom-20120423104835-9138g3x7ebpy1ce3
@@ -1826,6 +1826,8 @@ bool show_binlog_events(THD *thd, MYSQL_
 
     mysql_mutex_unlock(log_lock);
   }
+  // Check that linfo is still on the function scope.
+  DEBUG_SYNC(thd, "after_show_binlog_events");
 
   ret= FALSE;
 

=== modified file 'sql/ha_ndbcluster_binlog.cc'
--- a/sql/ha_ndbcluster_binlog.cc	revid:marko.makela@strippedm8lwfk6
+++ b/sql/ha_ndbcluster_binlog.cc	revid:marko.makela@stripped
@@ -6449,7 +6449,7 @@ ndb_binlog_thread_func(void *arg)
   pthread_detach_this_thread();
   thd->real_id= pthread_self();
   mysql_mutex_lock(&LOCK_thread_count);
-  threads.push_front(thd);
+  add_global_thread(thd);
   mysql_mutex_unlock(&LOCK_thread_count);
   thd->lex->start_transaction_opt= 0;
 

=== modified file 'sql/handler.h'
--- a/sql/handler.h	revid:marko.makela@strippedfgm8lwfk6
+++ b/sql/handler.h	revid:marko.makela@strippedom-20120423104835-9138g3x7ebpy1ce3
@@ -189,6 +189,11 @@ enum enum_alter_inplace_result {
 */
 #define HA_READ_BEFORE_WRITE_REMOVAL  (LL(1) << 38)
 
+/*
+  Engine supports extended fulltext API
+ */
+#define HA_CAN_FULLTEXT_EXT              (LL(1) << 39)
+
 /* bits in index_flags(index_number) for what you can do with index */
 #define HA_READ_NEXT            1       /* TODO really use this flag */
 #define HA_READ_PREV            2       /* supports ::index_prev */

=== modified file 'sql/item.cc'
--- a/sql/item.cc	revid:marko.makela@strippedhn6fgm8lwfk6
+++ b/sql/item.cc	revid:marko.makela@oracle.com-20120423104835-9138g3x7ebpy1ce3
@@ -961,7 +961,7 @@ bool Item::check_cols(uint c)
 const NameString null_name_string(NULL, 0);
 
 
-void NameString::copy(const char *str, uint length, const CHARSET_INFO *cs)
+void NameString::copy(const char *str, size_t length, const CHARSET_INFO *cs)
 {
   if (!length)
   {
@@ -997,7 +997,7 @@ void NameString::copy(const char *str, u
 }
 
 
-void ItemNameString::copy(const char *str_arg, uint length_arg,
+void ItemNameString::copy(const char *str_arg, size_t length_arg,
                           const CHARSET_INFO *cs_arg,
                           bool is_autogenerated_arg)
 {
@@ -1005,7 +1005,7 @@ void ItemNameString::copy(const char *st
   copy(str_arg, length_arg, cs_arg);
   if (length_arg > length() && !is_autogenerated())
   {
-    ErrConvString tmp(str_arg, length_arg, cs_arg);
+    ErrConvString tmp(str_arg, static_cast<uint>(length_arg), cs_arg);
     if (length() == 0)
       push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
                           ER_NAME_BECOMES_EMPTY, ER(ER_NAME_BECOMES_EMPTY),
@@ -6787,6 +6787,25 @@ Item* Item::cache_const_expr_transformer
 }
 
 
+bool Item_field::item_field_by_name_analyzer(uchar **arg)
+{
+  const char *name= reinterpret_cast<char*>(*arg);
+  
+  if (strcmp(field_name, name) == 0)
+    return true;
+  else
+    return false;
+}
+
+
+Item* Item_field::item_field_by_name_transformer(uchar *arg)
+{
+  Item *item= reinterpret_cast<Item*>(arg);
+  item->item_name= item_name;
+  return item;
+}
+
+
 bool Item_field::send(Protocol *protocol, String *buffer)
 {
   return protocol->store(result_field);

=== modified file 'sql/item.h'
--- a/sql/item.h	revid:marko.makela@stripped0120419090901-f20rhn6fgm8lwfk6
+++ b/sql/item.h	revid:marko.makela@stripped
@@ -156,7 +156,7 @@ public:
 class NameString: public SimpleCString
 {
 private:
-  void set_or_copy(const char *str, uint length, bool is_null_terminated)
+  void set_or_copy(const char *str, size_t length, bool is_null_terminated)
   {
     if (is_null_terminated)
       set(str, length);
@@ -170,10 +170,10 @@ public:
     It will involve hidden strlen() call, which can affect
     performance negatively. Use NameString(str, len) instead.
   */
-  NameString(const char *str, uint length):
+  NameString(const char *str, size_t length):
     SimpleCString(str, length) {}
   NameString(const LEX_STRING str): SimpleCString(str) {}
-  NameString(const char *str, uint length, bool is_null_terminated):
+  NameString(const char *str, size_t length, bool is_null_terminated):
     SimpleCString()
   {
     set_or_copy(str, length, is_null_terminated);
@@ -181,30 +181,30 @@ public:
   NameString(const LEX_STRING str, bool is_null_terminated):
     SimpleCString()
   {
-    set_or_copy(str.str, (uint) str.length, is_null_terminated);
+    set_or_copy(str.str, str.length, is_null_terminated);
   }
   /**
     Allocate space using sql_strmake() or sql_strmake_with_convert().
   */
-  void copy(const char *str, uint length, const CHARSET_INFO *cs);
+  void copy(const char *str, size_t length, const CHARSET_INFO *cs);
   /**
     Variants for copy(), for various argument combinations.
   */
-  void copy(const char *str, uint length)
+  void copy(const char *str, size_t length)
   {
     copy(str, length, system_charset_info);
   }
   void copy(const char *str)
   {
-    copy(str, (uint) (str ? strlen(str) : 0), system_charset_info);
+    copy(str, (str ? strlen(str) : 0), system_charset_info);
   }
   void copy(const LEX_STRING lex)
   {
-    copy(lex.str, (uint) lex.length);
+    copy(lex.str, lex.length);
   }
   void copy(const LEX_STRING *lex)
   {
-    copy(lex->str, (uint) lex->length);
+    copy(lex->str, lex->length);
   }
   void copy(const NameString str)
   {
@@ -273,7 +273,7 @@ public:
     Copy name together with autogenerated flag.
     Produce a warning if name was cut.
   */
-  void copy(const char *str_arg, uint length_arg, const CHARSET_INFO *cs_arg,
+  void copy(const char *str_arg, size_t length_arg, const CHARSET_INFO *cs_arg,
            bool is_autogenerated_arg);
 };
 
@@ -1385,6 +1385,28 @@ public:
 
   virtual bool cache_const_expr_analyzer(uchar **arg);
   virtual Item* cache_const_expr_transformer(uchar *arg);
+
+  /**
+     Analyzer for finding Item_field by name
+     
+     @param arg  Field name to search for
+     
+     @return TRUE Go deeper in item tree.  (Found Item or not an Item_field)
+     @return FALSE Don't go deeper in item tree. (Item_field with other name)
+  */
+  virtual bool item_field_by_name_analyzer(uchar **arg) { return true; };
+
+  /**
+     Simple transformer that returns the argument if this is an Item_field.
+     The new item will inherit it's name to maintain aliases.
+
+     @param arg Item to replace Item_field
+
+     @return argument if this is an Item_field
+     @return this otherwise.
+  */
+  virtual Item* item_field_by_name_transformer(uchar *arg) { return this; };
+  
   /*
     Check if a partition function is allowed
     SYNOPSIS
@@ -2174,6 +2196,8 @@ public:
   Item *safe_charset_converter(const CHARSET_INFO *tocs);
   int fix_outer_field(THD *thd, Field **field, Item **reference);
   virtual Item *update_value_transformer(uchar *select_arg);
+  virtual bool item_field_by_name_analyzer(uchar **arg);
+  virtual Item* item_field_by_name_transformer(uchar *arg);
   virtual void print(String *str, enum_query_type query_type);
   bool is_outer_field() const
   {
@@ -2797,7 +2821,7 @@ public:
   bool eq(const Item *item, bool binary_cmp) const;
   Item *clone_item() 
   {
-    return new Item_string((NameString) item_name, str_value.ptr(), 
+    return new Item_string(static_cast<NameString>(item_name), str_value.ptr(), 
     			   str_value.length(), collation.collation);
   }
   Item *safe_charset_converter(const CHARSET_INFO *tocs);
@@ -2899,7 +2923,7 @@ class Item_return_date_time :public Item
   enum_field_types date_time_field_type;
 public:
   Item_return_date_time(const char *name_arg, enum_field_types field_type_arg)
-    :Item_partition_func_safe_string(NameString((char *) name_arg, (uint) strlen(name_arg)),
+    :Item_partition_func_safe_string(NameString(name_arg, strlen(name_arg)),
                                      0, &my_charset_bin),
      date_time_field_type(field_type_arg)
   { decimals= 0; }
@@ -2911,7 +2935,7 @@ class Item_blob :public Item_partition_f
 {
 public:
   Item_blob(const char *name, uint length) :
-    Item_partition_func_safe_string(NameString((char *) name, (uint) strlen(name)),
+    Item_partition_func_safe_string(NameString(name, strlen(name)),
                                     length, &my_charset_bin)
   { }
   enum Type type() const { return TYPE_HOLDER; }
@@ -2930,7 +2954,7 @@ class Item_empty_string :public Item_par
 public:
   Item_empty_string(const char *header, uint length,
                     const CHARSET_INFO *cs= NULL) :
-    Item_partition_func_safe_string(NameString((char *)header, (uint) strlen(header)),
+    Item_partition_func_safe_string(NameString(header, strlen(header)),
                                     0, cs ? cs : &my_charset_utf8_general_ci)
     {
       max_length= length * collation.collation->mbmaxlen;
@@ -2945,7 +2969,7 @@ class Item_return_int :public Item_int
 public:
   Item_return_int(const char *name_arg, uint length,
 		  enum_field_types field_type_arg, longlong value= 0)
-    :Item_int(NameString(name_arg, name_arg ? (uint) strlen(name_arg) : 0),
+    :Item_int(NameString(name_arg, name_arg ? strlen(name_arg) : 0),
               value, length), int_field_type(field_type_arg)
   {
     unsigned_flag=1;

=== modified file 'sql/item_func.cc'
--- a/sql/item_func.cc	revid:marko.makela@oracle.com-20120419090901-f20rhn6fgm8lwfk6
+++ b/sql/item_func.cc	revid:marko.makela@stripped7ebpy1ce3
@@ -5760,7 +5760,7 @@ void Item_func_get_system_var::fix_lengt
 
 void Item_func_get_system_var::print(String *str, enum_query_type query_type)
 {
-  str->append(item_name.ptr(), item_name.length());
+  str->append(item_name);
 }
 
 
@@ -6323,9 +6323,11 @@ err:
 
 bool Item_func_match::eq(const Item *item, bool binary_cmp) const
 {
+  /* We ignore FT_SORTED flag when checking for equality since result is
+     equvialent regardless of sorting */
   if (item->type() != FUNC_ITEM ||
       ((Item_func*)item)->functype() != FT_FUNC ||
-      flags != ((Item_func_match*)item)->flags)
+      (flags | FT_SORTED) != (((Item_func_match*)item)->flags | FT_SORTED))
     return 0;
 
   Item_func_match *ifm=(Item_func_match*) item;

=== modified file 'sql/item_func.h'
--- a/sql/item_func.h	revid:marko.makela@strippedlwfk6
+++ b/sql/item_func.h	revid:marko.makela@stripped-20120423104835-9138g3x7ebpy1ce3
@@ -1855,8 +1855,91 @@ public:
 
   bool fix_index();
   void init_search(bool no_order);
+
+  /**
+     Get number of matching rows from FT handler.
+
+     @note Requires that FT handler supports the extended API
+
+     @return Number of matching rows in result 
+   */
+  ulonglong get_count()
+  {
+    DBUG_ASSERT(ft_handler);
+    DBUG_ASSERT(table->file->ha_table_flags() & HA_CAN_FULLTEXT_EXT);
+
+    return ((FT_INFO_EXT *)ft_handler)->could_you->
+      count_matches((FT_INFO_EXT *)ft_handler);
+  }
+
+  /**
+     Check whether FT result is ordered on rank
+
+     @return true if result is ordered
+     @return false otherwise
+   */
+  bool ordered_result()
+  {
+    if (flags & FT_SORTED)
+      return true;
+
+    if ((table->file->ha_table_flags() & HA_CAN_FULLTEXT_EXT) == 0)
+      return false;
+
+    DBUG_ASSERT(ft_handler);
+    return ((FT_INFO_EXT *)ft_handler)->could_you->get_flags() & 
+      FTS_ORDERED_RESULT;
+  }
+
+  /**
+     Check whether FT result contains the document ID
+
+     @return true if document ID is available
+     @return false otherwise
+   */
+  bool docid_in_result()
+  {
+    DBUG_ASSERT(ft_handler);
+
+    if ((table->file->ha_table_flags() & HA_CAN_FULLTEXT_EXT) == 0)
+      return false;
+
+    return ((FT_INFO_EXT *)ft_handler)->could_you->get_flags() & 
+      FTS_DOCID_IN_RESULT;
+  }
 };
 
+/**
+   Item_func class used to fetch document ID from FTS result.  This
+   class is used to replace Item_field objects in order to fetch
+   document ID from FTS result instead of table.
+ */
+class Item_func_docid : public Item_int_func
+{
+  FT_INFO_EXT *ft_handler;
+public:
+  Item_func_docid(FT_INFO_EXT *handler) : ft_handler(handler) 
+  { 
+    max_length= 21;
+    maybe_null= false; 
+    unsigned_flag= true;
+  } 
+
+  const char *func_name() const { return "docid"; }
+
+  void update_used_tables()
+  {
+    Item_int_func::update_used_tables();
+    used_tables_cache|= RAND_TABLE_BIT;
+    const_item_cache= false;
+  }
+
+  longlong val_int() 
+  { 
+    DBUG_ASSERT(ft_handler);
+    return ft_handler->could_you->get_docid(ft_handler);
+  }
+};
 
 class Item_func_bit_xor : public Item_func_bit
 {

=== modified file 'sql/mysqld.cc'
--- a/sql/mysqld.cc	revid:marko.makela@stripped
+++ b/sql/mysqld.cc	revid:marko.makela@stripped5-9138g3x7ebpy1ce3
@@ -2366,18 +2366,12 @@ void dec_connection_count()
 }
 
 
-/*
-  Delete the THD object and decrease number of threads
-
-  SYNOPSIS
-    delete_thd()
-    thd    Thread handler
-*/
-
-void delete_thd(THD *thd)
+/**
+  Delete the THD object.
+ */
+void destroy_thd(THD *thd)
 {
-  mysql_mutex_assert_owner(&LOCK_thread_count);
-  remove_global_thread(thd);
+  mysql_mutex_assert_not_owner(&LOCK_thread_count);
   delete thd;
 }
 

=== modified file 'sql/mysqld.h'
--- a/sql/mysqld.h	revid:marko.makela@stripped
+++ b/sql/mysqld.h	revid:marko.makela@stripped-9138g3x7ebpy1ce3
@@ -68,7 +68,7 @@ void kill_mysql(void);
 void close_connection(THD *thd, uint sql_errno= 0);
 void handle_connection_in_main_thread(THD *thd);
 void create_thread_to_handle_connection(THD *thd);
-void delete_thd(THD *thd);
+void destroy_thd(THD *thd);
 bool one_thread_per_connection_end(THD *thd, bool block_pthread);
 void kill_blocked_pthreads();
 void refresh_status(THD *thd);

=== modified file 'sql/opt_sum.cc'
--- a/sql/opt_sum.cc	revid:marko.makela@stripped8lwfk6
+++ b/sql/opt_sum.cc	revid:marko.makela@stripped-20120423104835-9138g3x7ebpy1ce3
@@ -239,7 +239,7 @@ int opt_sum_query(THD *thd,
 {
   List_iterator_fast<Item> it(all_fields);
   int const_result= 1;
-  bool recalc_const_item= 0;
+  bool recalc_const_item= false;
   ulonglong count= 1;
   bool is_exact_count= TRUE, maybe_exact_count= TRUE;
   table_map removed_tables= 0, outer_tables= 0, used_tables= 0;
@@ -348,11 +348,37 @@ int opt_sum_query(THD *thd,
             }
             is_exact_count= 1;                  // count is now exact
           }
-          ((Item_sum_count*) item)->make_const((longlong) count);
-          recalc_const_item= 1;
+        }
+        /* For result count of full-text search: If
+           1. it is a single table query,
+           2. the WHERE condition is a single MATCH expresssion,
+           3. the table engine can provide the row count from FTS result, and
+           4. the expr in COUNT(expr) can not be NULL,
+           we do the full-text search now, and replace with the actual count.
+
+           Note: Item_func_match::init_search() will be called again
+                 later in the optimization phase by init_fts_funcs(),
+                 but search will still only be done once.
+        */
+        else if (tables->next_leaf == NULL &&                             // 1 
+                 conds && conds->type() == Item::FUNC_ITEM && 
+                 ((Item_func*)conds)->functype() == Item_func::FT_FUNC && // 2
+                 (tables->table->file->ha_table_flags() &
+                  HA_CAN_FULLTEXT_EXT) &&                                 // 3
+                 !((Item_sum_count*) item)->get_arg(0)->maybe_null)       // 4
+        {
+          Item_func_match* fts_item= static_cast<Item_func_match*>(conds); 
+          fts_item->init_search(true);
+          count= fts_item->get_count();
         }
         else
           const_result= 0;
+
+        if (const_result == 1) {
+          ((Item_sum_count*) item)->make_const((longlong) count);
+          recalc_const_item= true;
+        }
+          
         break;
       case Item_sum::MIN_FUNC:
       case Item_sum::MAX_FUNC:
@@ -412,16 +438,22 @@ int opt_sum_query(THD *thd,
 	  }
           removed_tables|= table->map;
         }
-        else if (!expr->const_item() || !is_exact_count)
+        else if (!expr->const_item() || conds || !is_exact_count)
         {
           /*
-            The optimization is not applicable in both cases:
-            (a) 'expr' is a non-constant expression. Then we can't
-            replace 'expr' by a constant.
-            (b) 'expr' is a costant. According to ANSI, MIN/MAX must return
-            NULL if the query does not return any rows. Thus, if we are not
-            able to determine if the query returns any rows, we can't apply
-            the optimization and replace MIN/MAX with a constant.
+            We get here if the aggregate function is not based on a field.
+            Example: "SELECT MAX(1) FROM table ..."
+
+            This constant optimization is not applicable if
+            1. the expression is not constant, or
+            2. it is unknown if the query returns any rows. MIN/MAX must return
+               NULL if the query doesn't return any rows. We can't determine
+               this if:
+               - the query has a condition, because, in contrast to the
+                 "MAX(field)" case above, the condition will not be evaluated
+                 against an index for this case, or
+               - the storage engine does not provide exact count, which means
+                 that it doesn't know whether there are any rows.
           */
           const_result= 0;
           break;
@@ -437,6 +469,8 @@ int opt_sum_query(THD *thd,
         if (!count && !outer_tables)
         {
           item_sum->aggregator_clear();
+          // Mark the aggregated value as based on no rows
+          item->no_rows_in_result();
         }
         else
           item_sum->reset_and_add();

=== modified file 'sql/rpl_mi.cc'
--- a/sql/rpl_mi.cc	revid:marko.makela@stripped
+++ b/sql/rpl_mi.cc	revid:marko.makela@stripped835-9138g3x7ebpy1ce3
@@ -317,7 +317,7 @@ int Master_info::init_info()
   DBUG_RETURN(0);
 
 err:
-  // todo: handler->end_info(uidx, nidx);
+  handler->end_info(uidx, nidx);
   inited= 0;
   sql_print_error("Error reading master configuration.");
   DBUG_RETURN(1);

=== modified file 'sql/scheduler.cc'
--- a/sql/scheduler.cc	revid:marko.makela@stripped
+++ b/sql/scheduler.cc	revid:marko.makela@stripped-9138g3x7ebpy1ce3
@@ -1,4 +1,4 @@
-/* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
+/* Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -35,10 +35,11 @@ static bool no_threads_end(THD *thd, boo
   thd_cleanup(thd);
   dec_connection_count();
 
-  // THD is an incomplete type here, so use delete_thd() to delete it.
+  // THD is an incomplete type here, so use destroy_thd() to delete it.
   mysql_mutex_lock(&LOCK_thread_count);
-  delete_thd(thd);
+  remove_global_thread(thd);
   mysql_mutex_unlock(&LOCK_thread_count);
+  destroy_thd(thd);
 
   return 1;                                     // Abort handle_one_connection
 }

=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc	revid:marko.makela@stripped
+++ b/sql/sql_class.cc	revid:marko.makela@stripped4835-9138g3x7ebpy1ce3
@@ -1402,14 +1402,19 @@ void THD::cleanup(void)
 
 THD::~THD()
 {
+  mysql_mutex_assert_not_owner(&LOCK_thread_count);
   THD_CHECK_SENTRY(this);
   DBUG_ENTER("~THD()");
   DBUG_PRINT("info", ("THD dtor, this %p", this));
+
   /* Ensure that no one is using THD */
   mysql_mutex_lock(&LOCK_thd_data);
   mysys_var=0;					// Safety (shouldn't be needed)
   mysql_mutex_unlock(&LOCK_thd_data);
+
+  mysql_mutex_lock(&LOCK_status);
   add_to_status(&global_status_var, &status_var);
+  mysql_mutex_unlock(&LOCK_status);
 
   /* Close connection */
 #ifndef EMBEDDED_LIBRARY

=== modified file 'sql/sql_optimizer.cc'
--- a/sql/sql_optimizer.cc	revid:marko.makela@oracle.com-20120419090901-f20rhn6fgm8lwfk6
+++ b/sql/sql_optimizer.cc	revid:marko.makela@stripped9138g3x7ebpy1ce3
@@ -276,6 +276,8 @@ JOIN::optimize()
   }
 #endif
 
+  optimize_fts_limit_query();
+
   /* 
      Try to optimize count(*), min() and max() to const fields if
      there is implicit grouping (aggregate functions but no
@@ -732,7 +734,10 @@ JOIN::optimize()
 
   /* Perform FULLTEXT search before all regular searches */
   if (!(select_options & SELECT_DESCRIBE))
+  {
     init_ftfuncs(thd, select_lex, test(order));
+    optimize_fts_query();
+  }
 
   /* Create all structures needed for materialized subquery execution. */
   if (setup_subquery_materialization())
@@ -6875,6 +6880,31 @@ bool JOIN::cache_const_exprs()
 }
 
 
+void JOIN::replace_item_field(const char* field_name, Item* new_item)
+{
+  if (conds)
+  {
+    conds= conds->compile(&Item::item_field_by_name_analyzer, 
+                          (uchar **)&field_name,
+                          &Item::item_field_by_name_transformer,
+                          (uchar *)new_item);
+    conds->update_used_tables();
+  }
+
+  List_iterator<Item> it(fields_list);
+  Item *item;
+  while ((item= it++))
+  {
+    item= item->compile(&Item::item_field_by_name_analyzer,
+                        (uchar **)&field_name,
+                        &Item::item_field_by_name_transformer,
+                        (uchar *)new_item);
+    it.replace(item);
+    item->update_used_tables();
+  }
+}
+
+
 /**
   Extract a condition that can be checked after reading given table
   
@@ -8784,6 +8814,159 @@ static void optimize_keyuse(JOIN *join,
 }
 
 
+void JOIN::optimize_fts_query()
+{
+  if (tables > 1)
+    return;    // We only optimize single table FTS queries
+
+  JOIN_TAB * const tab= &(join_tab[0]);
+  if (tab->type != JT_FT)
+    return;    // Access is not using FTS result
+
+  if ((tab->table->file->ha_table_flags() & HA_CAN_FULLTEXT_EXT) == 0)
+    return;    // Optimizations requires extended FTS support by table engine
+
+  Item_func_match* fts_result= static_cast<Item_func_match*>(tab->keyuse->val);
+
+  /* If we are ordering on the rank of the same result as is used for access,
+     and the table engine deliver result ordered by rank, we can drop ordering.
+   */
+  if (order != NULL 
+      && order->next == NULL &&             
+      order->direction == ORDER::ORDER_DESC && 
+      fts_result->eq(*(order->item), true))
+  {
+    Item_func_match* fts_item= 
+      static_cast<Item_func_match*>(*(order->item)); 
+
+    /* If we applied the LIMIT optimization @see optimize_fts_limit_query,
+       check that the number of matching rows is sufficient.
+       Otherwise, revert this optimization and use table scan instead.
+    */
+    if (min_ft_matches != HA_POS_ERROR && 
+        min_ft_matches > fts_item->get_count())
+    {
+      // revert to table scan, do things make_join_readinfo would have done
+      tab->type= JT_ALL;
+      tab->read_first_record= join_init_read_record;
+      tab->use_quick= QS_NONE;
+      tab->ref.key= -1;
+
+      // Reset join condition
+      tab->select->cond= NULL;
+      conds= NULL;
+
+      thd->set_status_no_index_used();
+      // make_join_readinfo only calls inc_status_select_scan()
+      // when this is not SELECT_DESCRIBE
+      DBUG_ASSERT((select_options & SELECT_DESCRIBE) == 0);
+      thd->inc_status_select_scan();
+
+      return;
+    }
+    else if (fts_item->ordered_result())
+      order= NULL;
+  }
+  
+  /* Check whether the FTS result is covering.  If only document id
+     and rank is needed, there is no need to access table rows.
+  */
+  List_iterator<Item> it(all_fields);
+  Item *item;
+  // This optimization does not work with filesort nor GROUP BY
+  bool covering= (!order && !group);
+  bool docid_found= false;
+  while (covering && (item= it++))
+  {
+    switch (item->type()) {
+    case Item::FIELD_ITEM:
+    {
+      Item_field *item_field= static_cast<Item_field*>(item);
+      if (strcmp(item_field->field_name, FTS_DOC_ID_COL_NAME) == 0)
+      {
+        docid_found= true;
+        covering= fts_result->docid_in_result();
+      }
+      else
+        covering= false;
+      break;
+    }
+    case Item::FUNC_ITEM:
+      if (static_cast<Item_func*>(item)->functype() == Item_func::FT_FUNC)
+      {
+        Item_func_match* fts_item= static_cast<Item_func_match*>(item); 
+        if (fts_item->eq(fts_result, true))
+          break;
+      }
+      // Fall-through when not an equivalent MATCH expression
+    default:
+      covering= false;
+    }
+  }
+
+  if (covering) 
+  {
+    if (docid_found)
+    {
+      replace_item_field(FTS_DOC_ID_COL_NAME, 
+                         new Item_func_docid(reinterpret_cast<FT_INFO_EXT*>
+                                             (fts_result->ft_handler)));
+    }
+    
+    // Tell storage engine that row access is not necessary
+    fts_result->table->set_keyread(true);
+    fts_result->table->covering_keys.set_bit(fts_result->key);
+  }
+}
+
+
+  /**
+     Optimize FTS queries with ORDER BY/LIMIT, but no WHERE clause.
+
+     If MATCH expression is not in WHERE clause, but in ORDER BY,
+     JT_FT access will not apply. However, if we are ordering on rank and
+     there is a limit, normally, only the top ranking rows are needed
+     returned, and one would benefit from the optimizations associated
+     with JT_FT acess (@see optimize_fts_query).  To get JT_FT access we
+     will add the MATCH expression to the WHERE clause.
+
+     @note This optimization will only be applied to single table
+           queries with no existing WHERE clause.
+     @note This transformation is not correct if number of matches 
+           is less than the number of rows requested by limit.
+           If this turns out to be the case, the transformation will
+           be reverted @see optimize_fts_query()
+   */
+void 
+JOIN::optimize_fts_limit_query()
+{
+  /* 
+     Only do this optimization if
+     1. It is a single table query
+     2. There is no WHERE condition
+     3. There is a single ORDER BY element
+     4. Ordering is descending
+     5. There is a LIMIT clause
+     6. Ordering is on a MATCH expression
+   */
+  if (tables == 1 &&                                // 1
+      conds == NULL &&                              // 2
+      order && order->next == NULL &&     // 3
+      order->direction == ORDER::ORDER_DESC && // 4
+      m_select_limit != HA_POS_ERROR)               // 5
+  {
+    DBUG_ASSERT(order->item);
+    Item* item= *order->item;
+    DBUG_ASSERT(item);
+
+    if (item->type() == Item::FUNC_ITEM &&
+        static_cast<Item_func*>(item)->functype() == Item_func::FT_FUNC)  // 6
+    {
+      conds= item;
+      min_ft_matches= m_select_limit;
+    }
+  }
+}
 
 /**
   @} (end of group Query_Optimizer)

=== modified file 'sql/sql_optimizer.h'
--- a/sql/sql_optimizer.h	revid:marko.makela@stripped-f20rhn6fgm8lwfk6
+++ b/sql/sql_optimizer.h	revid:marko.makela@stripped
@@ -105,6 +105,13 @@ public:
       - on each fetch iteration we add num_rows to fetch to fetch_limit
   */
   ha_rows  fetch_limit;
+
+  /**
+     Minimum number of matches that is needed to use JT_FT access.
+     @see optimize_fts_limit_query
+  */
+  ha_rows  min_ft_matches;
+
   /* Finally picked QEP. This is result of join optimization */
   POSITION *best_positions;
 
@@ -295,6 +302,8 @@ public:
     operator       ORDER *()       { return order; }
     operator const ORDER *() const { return order; }
 
+    ORDER* operator->() const { return order; }
+ 
     void clean() { order= NULL; src= ESC_none; flags= ESP_none; }
 
     void set_flag(Explain_sort_property flag)
@@ -418,6 +427,7 @@ public:
     send_records= 0;
     found_records= 0;
     fetch_limit= HA_POS_ERROR;
+    min_ft_matches= HA_POS_ERROR;
     examined_rows= 0;
     exec_tmp_table1= 0;
     exec_tmp_table2= 0;
@@ -621,6 +631,39 @@ private:
   */
   void optimize_distinct();
 
+  /** 
+      Optimize FTS queries where JT_FT access has been selected.
+
+      The following optimization is may be applied:
+      1. Skip filesort if FTS result is ordered
+      2. Skip accessing table rows if FTS result contains necessary information
+      Also verifize that LIMIT optimization was sound.
+
+      @note Optimizations are restricted to single table queries, and the table
+            engine needs to support the extended FTS API.
+   */
+  void optimize_fts_query();
+
+
+  /**
+     Optimize FTS queries with ORDER BY/LIMIT, but no WHERE clause.
+   */
+  void optimize_fts_limit_query();
+
+  /**
+     Replace all Item_field objects with the given field name with the
+     given item in all parts of the query.
+
+     @todo So far this function only handles SELECT list and WHERE clause,
+           For more general use, ON clause, ORDER BY list, GROUP BY list and
+	   HAVING clause also needs to be handled.
+
+     @param field_name Name of the field to search for
+     @param new_item Replacement item
+  */
+  void replace_item_field(const char* field_name, Item* new_item);
+
+
   /**
     TRUE if the query contains an aggregate function but has no GROUP
     BY clause. 

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	revid:marko.makela@strippedn6fgm8lwfk6
+++ b/sql/sql_parse.cc	revid:marko.makela@oracle.com-20120423104835-9138g3x7ebpy1ce3
@@ -801,16 +801,22 @@ end:
   net_end(&thd->net);
   thd->cleanup();
 
+  if (thd_added)
+  {
+    mysql_mutex_lock(&LOCK_thread_count);
+    remove_global_thread(thd);
+    mysql_mutex_unlock(&LOCK_thread_count);
+  }
   /*
-    Here we delete the thd while holding the LOCK_thread_count.
+    We need to delete the thd before signalling that bootstrap is done.
     The reason is that we have to call ha_close_connection(thd)
     before shutting down InnoDB (this is done by THD::~THD())
   */
+  delete thd;
+
   mysql_mutex_lock(&LOCK_thread_count);
-  if (thd_added)
-    remove_global_thread(thd);
   in_bootstrap= FALSE;
-  delete thd;
+  mysql_cond_broadcast(&COND_thread_count);
   mysql_mutex_unlock(&LOCK_thread_count);
 
 #ifndef EMBEDDED_LIBRARY

=== modified file 'sql/sql_show.cc'
--- a/sql/sql_show.cc	revid:marko.makela@strippedgm8lwfk6
+++ b/sql/sql_show.cc	revid:marko.makela@oracle.com-20120423104835-9138g3x7ebpy1ce3
@@ -2647,7 +2647,6 @@ void calc_sum_of_all_status(STATUS_VAR *
 {
   DBUG_ENTER("calc_sum_of_all_status");
 
-  /* Ensure that thread id not killed during loop */
   mysql_mutex_lock(&LOCK_thread_count);
 
   Thread_iterator it= global_thread_list_begin();
@@ -3491,39 +3490,44 @@ end:
 
 static int fill_schema_table_names(THD *thd, TABLE *table,
                                    LEX_STRING *db_name, LEX_STRING *table_name,
-                                   bool with_i_schema)
+                                   bool with_i_schema,
+                                   bool need_table_type)
 {
-  if (with_i_schema)
-  {
-    table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW"),
-                           system_charset_info);
-  }
-  else
+  /* Avoid opening FRM files if table type is not needed. */
+  if (need_table_type)
   {
-    enum legacy_db_type not_used;
-    char path[FN_REFLEN + 1];
-    (void) build_table_filename(path, sizeof(path) - 1, db_name->str, 
-                                table_name->str, reg_ext, 0);
-    switch (dd_frm_type(thd, path, &not_used)) {
-    case FRMTYPE_ERROR:
-      table->field[3]->store(STRING_WITH_LEN("ERROR"),
-                             system_charset_info);
-      break;
-    case FRMTYPE_TABLE:
-      table->field[3]->store(STRING_WITH_LEN("BASE TABLE"),
-                             system_charset_info);
-      break;
-    case FRMTYPE_VIEW:
-      table->field[3]->store(STRING_WITH_LEN("VIEW"),
+    if (with_i_schema)
+    {
+      table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW"),
                              system_charset_info);
-      break;
-    default:
-      DBUG_ASSERT(0);
     }
-    if (thd->is_error() && thd->get_stmt_da()->sql_errno() == ER_NO_SUCH_TABLE)
+    else
     {
-      thd->clear_error();
-      return 0;
+      enum legacy_db_type not_used;
+      char path[FN_REFLEN + 1];
+      (void) build_table_filename(path, sizeof(path) - 1, db_name->str, 
+                                  table_name->str, reg_ext, 0);
+      switch (dd_frm_type(thd, path, &not_used)) {
+      case FRMTYPE_ERROR:
+        table->field[3]->store(STRING_WITH_LEN("ERROR"),
+                               system_charset_info);
+        break;
+      case FRMTYPE_TABLE:
+        table->field[3]->store(STRING_WITH_LEN("BASE TABLE"),
+                               system_charset_info);
+        break;
+      case FRMTYPE_VIEW:
+        table->field[3]->store(STRING_WITH_LEN("VIEW"),
+                               system_charset_info);
+        break;
+      default:
+        DBUG_ASSERT(0);
+      }
+    if (thd->is_error() && thd->get_stmt_da()->sql_errno() == ER_NO_SUCH_TABLE)
+      {
+        thd->clear_error();
+        return 0;
+      }
     }
   }
   if (schema_table_store_record(thd, table))
@@ -4062,7 +4066,8 @@ int get_all_tables(THD *thd, TABLE_LIST
           if (schema_table_idx == SCH_TABLE_NAMES)
           {
             if (fill_schema_table_names(thd, tables->table, db_name,
-                                        table_name, with_i_schema))
+                                        table_name, with_i_schema,
+                                        lex->verbose))
               continue;
           }
           else
@@ -6725,7 +6730,7 @@ TABLE *create_schema_table(THD *thd, TAB
     case MYSQL_TYPE_DOUBLE:
     {
       const NameString field_name(fields_info->field_name,
-                                  (uint) strlen(fields_info->field_name));
+                                  strlen(fields_info->field_name));
       if ((item= new Item_float(field_name, 0.0, NOT_FIXED_DEC, 
                                 fields_info->field_length)) == NULL)
         DBUG_RETURN(NULL);

=== modified file 'sql/sql_show.h'
--- a/sql/sql_show.h	revid:marko.makela@stripped
+++ b/sql/sql_show.h	revid:marko.makela@stripped-9138g3x7ebpy1ce3
@@ -168,7 +168,7 @@ void append_identifier(THD *thd, String
 		       uint length);
 inline void append_identifier(THD *thd, String *packet, SimpleCString str)
 {
-  append_identifier(thd, packet, str.ptr(), str.length());
+  append_identifier(thd, packet, str.ptr(), static_cast<uint>(str.length()));
 }
 void mysqld_list_fields(THD *thd,TABLE_LIST *table, const char *wild);
 bool mysqld_show_create(THD *thd, TABLE_LIST *table_list);

=== modified file 'sql/sql_string.h'
--- a/sql/sql_string.h	revid:marko.makela@strippedm8lwfk6
+++ b/sql/sql_string.h	revid:marko.makela@oracle.com-20120423104835-9138g3x7ebpy1ce3
@@ -38,12 +38,12 @@ class SimpleCString
 {
 private:
   const char *m_str;
-  uint m_length;
+  size_t m_length;
 protected:
   /**
     Initialize from a C string whose length is already known.
   */
-  void set(const char *str_arg, uint length_arg)
+  void set(const char *str_arg, size_t length_arg)
   {
     // NULL is allowed only with length==0
     DBUG_ASSERT(str_arg || length_arg == 0);
@@ -57,13 +57,13 @@ public:
   {
     set(NULL, 0);
   }
-  SimpleCString(const char *str_arg, uint length_arg)
+  SimpleCString(const char *str_arg, size_t length_arg)
   {
     set(str_arg, length_arg);
   }
   SimpleCString(const LEX_STRING arg)
   {
-    set(arg.str, (uint) arg.length);
+    set(arg.str, arg.length);
   }
   void reset()
   {
@@ -74,7 +74,7 @@ public:
   */
   void set(const char *str)
   {
-    set(str, str ? (uint) strlen(str) : 0);
+    set(str, str ? strlen(str) : 0);
   }
   /**
     Return string buffer.
@@ -87,7 +87,7 @@ public:
   /**
     Return name length.
   */
-  uint length() const { return m_length; }
+  size_t length() const { return m_length; }
   /**
     Compare to another SimpleCString.
   */
@@ -377,7 +377,7 @@ public:
   }
   bool append(SimpleCString str)
   {
-    return append(str.ptr(), str.length());
+    return append(str.ptr(), static_cast<uint>(str.length()));
   }
   bool append(const char *s, uint32 arg_length);
   bool append(const char *s, uint32 arg_length, const CHARSET_INFO *cs);

=== modified file 'storage/archive/ha_archive.cc'
--- a/storage/archive/ha_archive.cc	revid:marko.makela@oracle.com-20120419090901-f20rhn6fgm8lwfk6
+++ b/storage/archive/ha_archive.cc	revid:marko.makela@stripped104835-9138g3x7ebpy1ce3
@@ -1657,19 +1657,13 @@ int ha_archive::info(uint flag)
 {
   DBUG_ENTER("ha_archive::info");
 
-  /* 
-    If dirty, we lock, and then reset/flush the data.
-    I found that just calling azflush() doesn't always work.
-  */
   mysql_mutex_lock(&share->mutex);
-  if (share->dirty == TRUE)
+  if (share->dirty)
   {
-    if (share->dirty == TRUE)
-    {
-      DBUG_PRINT("ha_archive", ("archive flushing out rows for scan"));
-      azflush(&(share->archive_write), Z_SYNC_FLUSH);
-      share->dirty= FALSE;
-    }
+    DBUG_PRINT("ha_archive", ("archive flushing out rows for scan"));
+    DBUG_ASSERT(share->archive_write_open);
+    azflush(&(share->archive_write), Z_SYNC_FLUSH);
+    share->dirty= FALSE;
   }
 
   /* 
@@ -1709,6 +1703,7 @@ int ha_archive::info(uint flag)
 
   if (flag & HA_STATUS_AUTO)
   {
+    /* TODO: Use the shared writer instead during the lock above. */
     init_archive_reader();
     mysql_mutex_lock(&share->mutex);
     azflush(&archive, Z_SYNC_FLUSH);
@@ -1782,7 +1777,10 @@ int ha_archive::end_bulk_insert()
 {
   DBUG_ENTER("ha_archive::end_bulk_insert");
   bulk_insert= FALSE;
-  share->dirty= TRUE;
+  mysql_mutex_lock(&share->mutex);
+  if (share->archive_write_open)
+    share->dirty= true;
+  mysql_mutex_unlock(&share->mutex);
   DBUG_RETURN(0);
 }
 

=== modified file 'storage/innobase/btr/btr0cur.cc'
--- a/storage/innobase/btr/btr0cur.cc	revid:marko.makela@stripped
+++ b/storage/innobase/btr/btr0cur.cc	revid:marko.makela@oracle.com-20120423104835-9138g3x7ebpy1ce3
@@ -470,12 +470,7 @@ btr_cur_search_to_nth_level(
 	estimate = latch_mode & BTR_ESTIMATE;
 
 	/* Turn the flags unrelated to the latch mode off. */
-	latch_mode &= ~(BTR_INSERT
-			| BTR_DELETE_MARK
-			| BTR_DELETE
-			| BTR_ESTIMATE
-			| BTR_IGNORE_SEC_UNIQUE
-			| BTR_ALREADY_S_LATCHED);
+	latch_mode = BTR_LATCH_MODE_WITHOUT_FLAGS(latch_mode);
 
 	ut_ad(!s_latch_by_caller || latch_mode == BTR_SEARCH_LEAF);
 

=== modified file 'storage/innobase/handler/ha_innodb.cc'
--- a/storage/innobase/handler/ha_innodb.cc	revid:marko.makela@stripped
+++ b/storage/innobase/handler/ha_innodb.cc	revid:marko.makela@oracle.com-20120423104835-9138g3x7ebpy1ce3
@@ -243,6 +243,11 @@ const struct _ft_vft ft_vft_result = {NU
 				      innobase_fts_retrieve_ranking,
 				      NULL};
 
+const struct _ft_vft_ext ft_vft_ext_result = {innobase_fts_get_version,
+					      innobase_fts_flags,
+					      innobase_fts_retrieve_docid,
+					      innobase_fts_count_matches};
+
 #ifdef HAVE_PSI_INTERFACE
 /* Keys to register pthread mutexes/cond in the current file with
 performance schema */
@@ -2082,7 +2087,8 @@ ha_innobase::ha_innobase(
 		  HA_PRIMARY_KEY_IN_READ_INDEX |
 		  HA_BINLOG_ROW_CAPABLE |
 		  HA_CAN_GEOMETRY | HA_PARTIAL_COLUMN_READ |
-		  HA_TABLE_SCAN_ON_INDEX | HA_CAN_FULLTEXT),
+		  HA_TABLE_SCAN_ON_INDEX | HA_CAN_FULLTEXT |
+		  HA_CAN_FULLTEXT_EXT),
 	start_of_scan(0),
 	num_write_row(0)
 {}
@@ -6269,6 +6275,7 @@ no_commit:
 	innobase_srv_conc_enter_innodb(prebuilt->trx);
 
 	error = row_insert_for_mysql((byte*) record, prebuilt);
+	DEBUG_SYNC(user_thd, "ib_after_row_insert");
 
 	/* Handle duplicate key errors */
 	if (auto_inc_used) {
@@ -7726,6 +7733,7 @@ ha_innobase::ft_init_ext(
 						   MYF(0));
 
 		fts_hdl->please = (struct _ft_vft*)(&ft_vft_result);
+		fts_hdl->could_you = (struct _ft_vft_ext*)(&ft_vft_ext_result);
 		fts_hdl->ft_prebuilt = prebuilt;
 		fts_hdl->ft_result = result;
 	}
@@ -7778,6 +7786,13 @@ next_record:
 		dict_index_t*	index;
 		dtuple_t*	tuple = prebuilt->search_tuple;
 
+		/* If we only need information from result we can return
+		   without fetching the table row */
+		if (ft_prebuilt->read_just_key) {
+			table->status= 0;
+			return (0);
+		}
+
 		index = dict_table_get_index_on_name(
 			prebuilt->table, FTS_DOC_ID_INDEX_NAME);
 
@@ -14025,6 +14040,12 @@ innobase_fts_retrieve_ranking(
 
 	ft_prebuilt = ((NEW_FT_INFO*) fts_hdl)->ft_prebuilt;
 
+	if (ft_prebuilt->read_just_key) {
+		fts_ranking_t*  ranking = 
+			rbt_value(fts_ranking_t, result->current);
+		return (ranking->rank);
+	}
+
 	/* Retrieve the ranking value for doc_id with value of
 	prebuilt->fts_doc_id */
 	return(fts_retrieve_ranking(result, ft_prebuilt->fts_doc_id));
@@ -14079,6 +14100,69 @@ innobase_fts_find_ranking(
 	return fts_retrieve_ranking(result, ft_prebuilt->fts_doc_id);
 }
 
+/***********************************************************************
+@return version of the extended FTS API */
+uint
+innobase_fts_get_version()
+{
+	/* Currently this doesn't make much sense as returning
+	HA_CAN_FULLTEXT_EXT automatically mean this version is supported.
+	This supposed to ease future extensions.  */
+	return 2;
+}
+
+/***********************************************************************
+@return Which part of the extended FTS API is supported */
+ulonglong
+innobase_fts_flags()
+{
+	return (FTS_ORDERED_RESULT | FTS_DOCID_IN_RESULT);
+}
+
+
+/***********************************************************************
+Find and Retrieve the FTS doc_id for the current result row
+@return the document ID */
+ulonglong
+innobase_fts_retrieve_docid(
+/*============================*/
+		FT_INFO_EXT * fts_hdl)	/*!< in: FTS handler */
+{
+	row_prebuilt_t* ft_prebuilt;
+	fts_result_t*	result;
+
+	ft_prebuilt = ((NEW_FT_INFO *)fts_hdl)->ft_prebuilt;
+	result = ((NEW_FT_INFO *)fts_hdl)->ft_result;
+
+	if (ft_prebuilt->read_just_key) {
+		fts_ranking_t* ranking = 
+			rbt_value(fts_ranking_t, result->current);
+		return (ranking->doc_id);
+	}
+
+	return(ft_prebuilt->fts_doc_id);
+}
+
+/***********************************************************************
+Find and retrieve the size of the current result
+@return number of matching rows */
+ulonglong
+innobase_fts_count_matches(
+/*============================*/
+		FT_INFO_EXT * fts_hdl)	/*!< in: FTS handler */
+{
+	row_prebuilt_t* ft_prebuilt;
+
+	ft_prebuilt = ((NEW_FT_INFO *)fts_hdl)->ft_prebuilt;
+
+	if (ft_prebuilt->result->rankings_by_id != NULL) {
+		return rbt_size(ft_prebuilt->result->rankings_by_id);
+	} else {
+		return(0);
+	}
+}
+
+
 /* These variables are never read by InnoDB or changed. They are a kind of
 dummies that are needed by the MySQL infrastructure to call
 buffer_pool_dump_now(), buffer_pool_load_now() and buffer_pool_load_abort()

=== modified file 'storage/innobase/handler/ha_innodb.h'
--- a/storage/innobase/handler/ha_innodb.h	revid:marko.makela@stripped6
+++ b/storage/innobase/handler/ha_innodb.h	revid:marko.makela@stripped
@@ -445,6 +445,7 @@ extern const struct _ft_vft ft_vft_resul
 typedef struct new_ft_info
 {
 	struct _ft_vft		*please;
+	struct _ft_vft_ext	*could_you;
 	row_prebuilt_t*		ft_prebuilt;
 	fts_result_t*		ft_result;
 } NEW_FT_INFO;
@@ -567,6 +568,32 @@ innobase_fts_check_doc_id_index_in_def(
 	const KEY*	key_info)	/*!< in: Key definitions */
 	__attribute__((nonnull, warn_unused_result));
 
+/***********************************************************************
+@return version of the extended FTS API */
+uint
+innobase_fts_get_version();
+
+/***********************************************************************
+@return Which part of the extended FTS API is supported */
+ulonglong
+innobase_fts_flags();
+
+/***********************************************************************
+Find and Retrieve the FTS doc_id for the current result row
+@return the document ID */
+ulonglong
+innobase_fts_retrieve_docid(
+/*============================*/
+	FT_INFO_EXT*	fts_hdl);	/*!< in: FTS handler */
+
+/***********************************************************************
+Find and retrieve the size of the current result
+@return number of matching rows */
+ulonglong
+innobase_fts_count_matches(
+/*============================*/
+	FT_INFO_EXT*	fts_hdl);	/*!< in: FTS handler */
+
 /** "GEN_CLUST_INDEX" is the name reserved for InnoDB default
 system clustered index when there is no primary key. */
 extern const char innobase_index_reserve_name[];

=== modified file 'storage/innobase/include/btr0btr.h'
--- a/storage/innobase/include/btr0btr.h	revid:marko.makela@stripped0rhn6fgm8lwfk6
+++ b/storage/innobase/include/btr0btr.h	revid:marko.makela@strippedce3
@@ -100,6 +100,14 @@ buffer when the record is not in the buf
 on the index tree */
 #define BTR_ALREADY_S_LATCHED	16384
 
+#define BTR_LATCH_MODE_WITHOUT_FLAGS(latch_mode)	\
+	((latch_mode) & ~(BTR_INSERT			\
+			  | BTR_DELETE_MARK		\
+			  | BTR_DELETE			\
+			  | BTR_ESTIMATE		\
+			  | BTR_IGNORE_SEC_UNIQUE	\
+			  | BTR_ALREADY_S_LATCHED))
+
 /**************************************************************//**
 Report that an index page is corrupted. */
 UNIV_INTERN

=== modified file 'storage/innobase/include/btr0pcur.ic'
--- a/storage/innobase/include/btr0pcur.ic	revid:marko.makela@stripped20rhn6fgm8lwfk6
+++ b/storage/innobase/include/btr0pcur.ic	revid:marko.makela@strippedpy1ce3
@@ -429,7 +429,7 @@ btr_pcur_open_low(
 
 	btr_pcur_init(cursor);
 
-	cursor->latch_mode = latch_mode;
+	cursor->latch_mode = BTR_LATCH_MODE_WITHOUT_FLAGS(latch_mode);
 	cursor->search_mode = mode;
 
 	/* Search with the tree cursor */

=== modified file 'storage/innobase/include/read0read.h'
--- a/storage/innobase/include/read0read.h	revid:marko.makela@stripped8lwfk6
+++ b/storage/innobase/include/read0read.h	revid:marko.makela@stripped
@@ -57,12 +57,14 @@ read_view_purge_open(
 	mem_heap_t*	heap);		/*!< in: memory heap from which
 					allocated */
 /*********************************************************************//**
-Remove read view from the trx_sys->view_list. */
-UNIV_INTERN
+Remove a read view from the trx_sys->view_list. */
+UNIV_INLINE
 void
 read_view_remove(
 /*=============*/
-	read_view_t*	view);	/*!< in: read view */
+	read_view_t*	view,		/*!< in: read view, can be 0 */
+	bool		own_mutex);	/*!< in: true if caller owns the
+					trx_sys_t::mutex */
 /*********************************************************************//**
 Closes a consistent read view for MySQL. This function is called at an SQL
 statement end if the trx isolation level is <= TRX_ISO_READ_COMMITTED. */

=== modified file 'storage/innobase/include/read0read.ic'
--- a/storage/innobase/include/read0read.ic	revid:marko.makela@stripped-f20rhn6fgm8lwfk6
+++ b/storage/innobase/include/read0read.ic	revid:marko.makela@stripped7ebpy1ce3
@@ -23,6 +23,59 @@ Cursor read
 Created 2/16/1997 Heikki Tuuri
 *******************************************************/
 
+#include "trx0sys.h"
+
+#ifdef UNIV_DEBUG
+/*********************************************************************//**
+Validates a read view object. */
+static
+bool
+read_view_validate(
+/*===============*/
+	const read_view_t*	view)	/*!< in: view to validate */
+{
+	ut_ad(mutex_own(&trx_sys->mutex));
+
+	/* Check that the view->trx_ids array is in descending order. */
+	for (ulint i = 1; i < view->n_trx_ids; ++i) {
+
+		ut_a(view->trx_ids[i] < view->trx_ids[i - 1]);
+	}
+
+	return(true);
+}
+
+/** Functor to validate the view list. */
+struct	ViewCheck {
+
+	ViewCheck() : m_prev_view(0) { }
+
+	void	operator()(const read_view_t* view)
+	{
+		ut_a(m_prev_view == NULL
+		     || m_prev_view->low_limit_no >= view->low_limit_no);
+
+		m_prev_view = view;
+	}
+
+	const read_view_t*	m_prev_view;
+};
+
+/*********************************************************************//**
+Validates a read view list. */
+static
+bool
+read_view_list_validate(void)
+/*=========================*/
+{
+	ut_ad(mutex_own(&trx_sys->mutex));
+
+	ut_list_map(trx_sys->view_list, &read_view_t::view_list, ViewCheck());
+
+	return(true);
+}
+#endif /* UNIV_DEBUG */
+
 /*********************************************************************//**
 Checks if a read view sees the specified transaction.
 @return	true if sees */
@@ -65,3 +118,31 @@ read_view_sees_trx_id(
 
 	return(true);
 }
+
+/*********************************************************************//**
+Remove a read view from the trx_sys->view_list. */
+UNIV_INLINE
+void
+read_view_remove(
+/*=============*/
+	read_view_t*	view,		/*!< in: read view, can be 0 */
+	bool		own_mutex)	/*!< in: true if caller owns the
+					trx_sys_t::mutex */
+{
+	if (view != 0) {
+		if (!own_mutex) {
+			mutex_enter(&trx_sys->mutex);
+		}
+
+		ut_ad(read_view_validate(view));
+
+		UT_LIST_REMOVE(view_list, trx_sys->view_list, view);
+
+		ut_ad(read_view_list_validate());
+
+		if (!own_mutex) {
+			mutex_exit(&trx_sys->mutex);
+		}
+	}
+}
+

=== modified file 'storage/innobase/read/read0read.cc'
--- a/storage/innobase/read/read0read.cc	revid:marko.makela@stripped
+++ b/storage/innobase/read/read0read.cc	revid:marko.makela@oracle.com-20120423104835-9138g3x7ebpy1ce3
@@ -174,59 +174,6 @@ The order does not matter. No new transa
 transaction can commit or rollback (or free views).
 */
 
-#ifdef UNIV_DEBUG
-/*********************************************************************//**
-Validates a read view object. */
-static
-ibool
-read_view_validate(
-/*===============*/
-	const read_view_t*	view)	/*!< in: view to validate */
-{
-	ulint	i;
-
-	ut_ad(mutex_own(&trx_sys->mutex));
-
-	/* Check that the view->trx_ids array is in descending order. */
-	for (i = 1; i < view->n_trx_ids; ++i) {
-
-		ut_a(view->trx_ids[i] < view->trx_ids[i - 1]);
-	}
-
-	return(TRUE);
-}
-
-/** Functor to validate the view list. */
-struct	Check {
-
-	Check() : m_prev_view(0) { }
-
-	void	operator()(const read_view_t* view)
-	{
-		ut_a(m_prev_view == NULL
-		     || m_prev_view->low_limit_no >= view->low_limit_no);
-
-		m_prev_view = view;
-	}
-
-	const read_view_t*	m_prev_view;
-};
-
-/*********************************************************************//**
-Validates a read view list. */
-static
-ibool
-read_view_list_validate(void)
-/*=========================*/
-{
-	ut_ad(mutex_own(&trx_sys->mutex));
-
-	ut_list_map(trx_sys->view_list, &read_view_t::view_list, Check());
-
-	return(TRUE);
-}
-#endif
-
 /*********************************************************************//**
 Creates a read view object.
 @return	own: read view struct */
@@ -530,25 +477,6 @@ read_view_purge_open(
 }
 
 /*********************************************************************//**
-Remove a read view from the trx_sys->view_list. */
-UNIV_INTERN
-void
-read_view_remove(
-/*=============*/
-	read_view_t*	view)	/*!< in: read view */
-{
-	mutex_enter(&trx_sys->mutex);
-
-	ut_ad(read_view_validate(view));
-
-	UT_LIST_REMOVE(view_list, trx_sys->view_list, view);
-
-	ut_ad(read_view_list_validate());
-
-	mutex_exit(&trx_sys->mutex);
-}
-
-/*********************************************************************//**
 Closes a consistent read view for MySQL. This function is called at an SQL
 statement end if the trx isolation level is <= TRX_ISO_READ_COMMITTED. */
 UNIV_INTERN
@@ -559,7 +487,7 @@ read_view_close_for_mysql(
 {
 	ut_a(trx->global_read_view);
 
-	read_view_remove(trx->global_read_view);
+	read_view_remove(trx->global_read_view, false);
 
 	mem_heap_empty(trx->global_read_view_heap);
 
@@ -692,7 +620,7 @@ read_cursor_view_close_for_mysql(
 	belong to this transaction */
 	trx->n_mysql_tables_in_use += curview->n_mysql_tables_in_use;
 
-	read_view_remove(curview->read_view);
+	read_view_remove(curview->read_view, false);
 
 	trx->read_view = trx->global_read_view;
 

=== modified file 'storage/innobase/row/row0log.cc'
--- a/storage/innobase/row/row0log.cc	revid:marko.makela@strippedlwfk6
+++ b/storage/innobase/row/row0log.cc	revid:marko.makela@stripped
@@ -455,11 +455,14 @@ update_the_rec:
 					goto insert_the_rec;
 				}
 
-				/* Duplicate key found. Complain if
-				the record was not delete-marked or we
-				are trying to insert a non-matching
-				delete-marked record. */
-				if (!deleted || entry->info_bits) {
+				/* Duplicate key found. This is OK if
+				any of the key columns are NULL.
+				Complain if the record was not
+				delete-marked or we are trying to
+				insert a non-matching delete-marked
+				record. */
+				if ((!deleted || entry->info_bits)
+				    && !dtuple_contains_null(entry)) {
 					row_merge_dup_report(
 						dup, entry->fields);
 					goto func_exit;
@@ -523,7 +526,8 @@ update_the_rec:
 
 			if (update->n_fields > 0
 			    && cursor.low_match
-			    < dict_index_get_n_fields(index)) {
+			    < dict_index_get_n_fields(index)
+			    && !dtuple_contains_null(entry)) {
 				/* Duplicate key error */
 				ut_ad(dict_index_is_unique(index));
 				row_merge_dup_report(dup, entry->fields);

=== modified file 'storage/innobase/row/row0merge.cc'
--- a/storage/innobase/row/row0merge.cc	revid:marko.makela@stripped
+++ b/storage/innobase/row/row0merge.cc	revid:marko.makela@oracle.com-20120423104835-9138g3x7ebpy1ce3
@@ -2490,7 +2490,7 @@ row_merge_drop_index_dict(
 	if (error != DB_SUCCESS) {
 		/* Even though we ensure that DDL transactions are WAIT
 		and DEADLOCK free, we could encounter other errors e.g.,
-		DB_TOO_MANY_TRANSACTIONS. */
+		DB_TOO_MANY_CONCURRENT_TRXS. */
 		trx->error_state = DB_SUCCESS;
 
 		ut_print_timestamp(stderr);
@@ -2563,7 +2563,7 @@ row_merge_drop_indexes_dict(
 	if (error != DB_SUCCESS) {
 		/* Even though we ensure that DDL transactions are WAIT
 		and DEADLOCK free, we could encounter other errors e.g.,
-		DB_TOO_MANY_TRANSACTIONS. */
+		DB_TOO_MANY_CONCURRENT_TRXS. */
 		trx->error_state = DB_SUCCESS;
 
 		ut_print_timestamp(stderr);
@@ -2758,7 +2758,7 @@ row_merge_drop_temp_indexes(void)
 	if (error != DB_SUCCESS) {
 		/* Even though we ensure that DDL transactions are WAIT
 		and DEADLOCK free, we could encounter other errors e.g.,
-		DB_TOO_MANY_TRANSACTIONS. */
+		DB_TOO_MANY_CONCURRENT_TRXS. */
 		trx->error_state = DB_SUCCESS;
 
 		ut_print_timestamp(stderr);
@@ -2890,7 +2890,7 @@ row_merge_rename_index_to_add(
 	if (err != DB_SUCCESS) {
 		/* Even though we ensure that DDL transactions are WAIT
 		and DEADLOCK free, we could encounter other errors e.g.,
-		DB_TOO_MANY_TRANSACTIONS. */
+		DB_TOO_MANY_CONCURRENT_TRXS. */
 		trx->error_state = DB_SUCCESS;
 
 		ut_print_timestamp(stderr);
@@ -2945,7 +2945,7 @@ row_merge_rename_index_to_drop(
 	if (err != DB_SUCCESS) {
 		/* Even though we ensure that DDL transactions are WAIT
 		and DEADLOCK free, we could encounter other errors e.g.,
-		DB_TOO_MANY_TRANSACTIONS. */
+		DB_TOO_MANY_CONCURRENT_TRXS. */
 		trx->error_state = DB_SUCCESS;
 
 		ut_print_timestamp(stderr);

=== modified file 'storage/innobase/trx/trx0trx.cc'
--- a/storage/innobase/trx/trx0trx.cc	revid:marko.makela@oracle.com-20120419090901-f20rhn6fgm8lwfk6
+++ b/storage/innobase/trx/trx0trx.cc	revid:marko.makela@stripped23104835-9138g3x7ebpy1ce3
@@ -205,6 +205,7 @@ trx_free(
 	ut_a(trx->magic_n == TRX_MAGIC_N);
 	ut_ad(!trx->in_ro_trx_list);
 	ut_ad(!trx->in_rw_trx_list);
+	ut_ad(!trx->in_mysql_trx_list);
 
 	mutex_free(&trx->undo_mutex);
 
@@ -1060,6 +1061,8 @@ trx_commit(
 
 		trx->state = TRX_STATE_NOT_STARTED;
 
+		read_view_remove(trx->global_read_view, false);
+
 		MONITOR_INC(MONITOR_TRX_NL_RO_COMMIT);
 	} else {
 		lock_trx_release_locks(trx);
@@ -1091,13 +1094,16 @@ trx_commit(
 
 		trx->state = TRX_STATE_NOT_STARTED;
 
+		/* We already own the trx_sys_t::mutex, by doing it here we
+		avoid a potential context switch later. */
+		read_view_remove(trx->global_read_view, true);
+
 		ut_ad(trx_sys_validate_trx_list());
 
 		mutex_exit(&trx_sys->mutex);
 	}
 
 	if (trx->global_read_view != NULL) {
-		read_view_remove(trx->global_read_view);
 
 		mem_heap_empty(trx->global_read_view_heap);
 

=== modified file 'strings/decimal.c'
--- a/strings/decimal.c	revid:marko.makela@stripped
+++ b/strings/decimal.c	revid:marko.makela@stripped835-9138g3x7ebpy1ce3
@@ -1167,6 +1167,17 @@ int double2lldiv_t(double nr, lldiv_t *l
   lld->quot= (longlong) (nr > 0 ? floor(nr) : ceil(nr));
   /* Multiply reminder to 10^9 and store into "rem" */
   lld->rem= (longlong) rint((nr - (double) lld->quot) * 1000000000);
+  /*
+    Sometimes the expression "(double) 0.999999999xxx * (double) 10e9"
+    gives 1,000,000,000 instead of 999,999,999 due to lack of double precision.
+    The callers do not expect lld->rem to be greater than 999,999,999.
+    Let's catch this corner case and put the "nanounit" (e.g. nanosecond)
+    value in ldd->rem back into the valid range.
+  */
+  if (lld->rem > 999999999LL)
+    lld->rem= 999999999LL;
+  else if (lld->rem < -999999999LL)
+    lld->rem= -999999999LL;
   return E_DEC_OK;
 }
 

No bundle (reason: useless for push emails).
Thread
bzr push into mysql-trunk-wl5545 branch (marko.makela:3900 to 3903) WL#5545marko.makela23 Apr