List:Commits« Previous MessageNext Message »
From:jonas oreland Date:June 17 2011 12:41pm
Subject:bzr commit into mysql-5.1-telco-7.0-spj-scan-vs-scan branch (jonas:3508)
View as plain text  
#At file:///home/jonas/src/70-spj-svs/ based on revid:jonas@stripped

 3508 jonas oreland	2011-06-17 [merge]
      ndb - merge 70 into 70-spj

    added:
      mysql-test/suite/ndb_rpl/ndb_master-slave_2ch_end.inc
      sql/ha_ndb_index_stat.cc
      sql/ha_ndb_index_stat.h
      storage/ndb/src/ndbapi/NdbIndexStatFrmData.cpp
      storage/ndb/tools/ndb_dump_frm_data.cpp
    modified:
      libmysqld/Makefile.am
      mysql-test/suite/ndb/r/ndb_basic.result
      mysql-test/suite/ndb/r/ndb_index_ordered.result
      mysql-test/suite/ndb/t/ndb_basic.test
      mysql-test/suite/ndb/t/ndb_index_ordered.test
      mysql-test/suite/ndb_rpl/ndb_master-slave_2ch.inc
      mysql-test/suite/ndb_rpl/r/ndb_rpl_circular_2ch.result
      mysql-test/suite/ndb_rpl/r/ndb_rpl_conflict_max.result
      mysql-test/suite/ndb_rpl/r/ndb_rpl_conflict_max_delete_win.result
      mysql-test/suite/ndb_rpl/r/ndb_rpl_conflict_old.result
      mysql-test/suite/ndb_rpl/r/ndb_rpl_rep_error.result
      mysql-test/suite/ndb_rpl/t/ndb_conflict_info.inc
      mysql-test/suite/ndb_rpl/t/ndb_conflict_info_init.inc
      mysql-test/suite/ndb_rpl/t/ndb_rpl_circular_2ch.cnf
      mysql-test/suite/ndb_rpl/t/ndb_rpl_circular_2ch.test
      mysql-test/suite/ndb_rpl/t/ndb_rpl_rep_error.test
      sql/Makefile.am
      sql/ha_ndbcluster.cc
      sql/ha_ndbcluster.h
      sql/ha_ndbcluster_binlog.cc
      sql/ha_ndbcluster_binlog.h
      storage/ndb/CMakeLists.txt
      storage/ndb/include/kernel/ndb_limits.h
      storage/ndb/include/ndb_constants.h
      storage/ndb/src/kernel/blocks/trix/Trix.cpp
      storage/ndb/src/ndbapi/CMakeLists.txt
      storage/ndb/src/ndbapi/Makefile.am
      storage/ndb/src/ndbapi/NdbIndexStatImpl.cpp
      storage/ndb/src/ndbapi/NdbIndexStatImpl.hpp
      storage/ndb/tools/CMakeLists.txt
      storage/ndb/tools/Makefile.am
      storage/ndb/tools/ndb_index_stat.cpp
=== modified file 'libmysqld/Makefile.am'
--- a/libmysqld/Makefile.am	2011-03-30 10:54:30 +0000
+++ b/libmysqld/Makefile.am	2011-06-17 12:41:11 +0000
@@ -49,6 +49,7 @@ noinst_HEADERS =	embedded_priv.h emb_qca
 sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \
 	     ha_ndbcluster.cc ha_ndbcluster_cond.cc ha_ndbcluster_push.cc \
 	ha_ndbcluster_connection.cc ha_ndbinfo.cc \
+	ha_ndb_index_stat.cc \
 	ha_ndbcluster_binlog.cc ha_partition.cc \
 	handler.cc sql_handler.cc \
 	hostname.cc init.cc password.c \
@@ -124,6 +125,9 @@ ha_ndbcluster_binlog.o: ha_ndbcluster_bi
 ha_ndbcluster_connection.o: ha_ndbcluster_connection.cc
 		$(CXXCOMPILE) @ndbcluster_includes@ $(LM_CFLAGS) -c $<
 
+ha_ndb_index_stat.o: ha_ndb_index_stat.cc
+		$(CXXCOMPILE) @ndbcluster_includes@ $(LM_CFLAGS) -c $<
+
 ha_ndbinfo.o: ha_ndbinfo.cc
 		$(CXXCOMPILE) @ndbcluster_includes@ $(LM_CFLAGS) -c $<
 

=== modified file 'mysql-test/suite/ndb/r/ndb_basic.result'
--- a/mysql-test/suite/ndb/r/ndb_basic.result	2011-04-29 09:23:56 +0000
+++ b/mysql-test/suite/ndb/r/ndb_basic.result	2011-06-17 12:41:11 +0000
@@ -66,9 +66,12 @@ Ndb_cluster_node_id	#
 Ndb_config_from_host	#
 Ndb_config_from_port	#
 Ndb_conflict_fn_max	#
+Ndb_conflict_fn_max_del_win	#
 Ndb_conflict_fn_old	#
 Ndb_connect_count	#
 Ndb_execute_count	#
+Ndb_index_stat_cache_clean	#
+Ndb_index_stat_cache_query	#
 Ndb_number_of_data_nodes	#
 Ndb_number_of_ready_data_nodes	#
 Ndb_pruned_scan_count	#
@@ -93,6 +96,7 @@ ndb_extra_logging	#
 ndb_force_send	#
 ndb_index_stat_cache_entries	#
 ndb_index_stat_enable	#
+ndb_index_stat_option	#
 ndb_index_stat_update_freq	#
 ndb_join_pushdown	#
 ndb_log_apply_status	#
@@ -904,8 +908,8 @@ drop table t10,t20;
 #
 # bug #39872 - explain causes segv
 # (ndb_index_stat_enable=1 must be set to trigger bug)
+# index stats v4: do not set, the v2 setting was local
 #
-set ndb_index_stat_enable=1;
 CREATE TABLE `t1` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 PRIMARY KEY (`id`)
@@ -919,8 +923,8 @@ KEY `obj_id` (`obj_id`)
 # here we used to segv
 explain SELECT t1.id FROM t1 INNER JOIN t2 ON t1.id = t2.id WHERE t2.obj_id=1;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t2	ref	id,obj_id	obj_id	5	const	1	Parent of 2 pushed join@1; Using where with pushed condition
-1	SIMPLE	t1	eq_ref	PRIMARY	PRIMARY	4	test.t2.id	1	Child of 't2' in pushed join@1
+1	SIMPLE	t2	ref	id,obj_id	obj_id	5	const	#	Parent of 2 pushed join@1; Using where with pushed condition
+1	SIMPLE	t1	eq_ref	PRIMARY	PRIMARY	4	test.t2.id	#	Child of 't2' in pushed join@1
 drop table t1, t2;
 #
 # Bug #47054 Cluster only deletes first matched row in delete with left join

=== modified file 'mysql-test/suite/ndb/r/ndb_index_ordered.result'
--- a/mysql-test/suite/ndb/r/ndb_index_ordered.result	2010-10-20 17:39:53 +0000
+++ b/mysql-test/suite/ndb/r/ndb_index_ordered.result	2011-06-15 10:37:56 +0000
@@ -659,143 +659,6 @@ select count(*) from t1 where c<'bbb';
 count(*)
 1
 drop table t1;
-set autocommit=1;
-show session variables like 'ndb_index_stat_%';
-Variable_name	Value
-ndb_index_stat_cache_entries	32
-ndb_index_stat_enable	OFF
-ndb_index_stat_update_freq	20
-set ndb_index_stat_enable = off;
-show session variables like 'ndb_index_stat_%';
-Variable_name	Value
-ndb_index_stat_cache_entries	32
-ndb_index_stat_enable	OFF
-ndb_index_stat_update_freq	20
-create table t1 (a int, b int, c varchar(10) not null,
-primary key using hash (a), index(b,c)) engine=ndb;
-insert into t1 values
-(1,10,'aaa'),(2,10,'bbb'),(3,10,'ccc'),
-(4,20,'aaa'),(5,20,'bbb'),(6,20,'ccc'),
-(7,30,'aaa'),(8,30,'bbb'),(9,30,'ccc');
-select count(*) from t1 where b < 10;
-count(*)
-0
-select count(*) from t1 where b >= 10 and c >= 'bbb';
-count(*)
-6
-select count(*) from t1 where b > 10;
-count(*)
-6
-select count(*) from t1 where b <= 20 and c < 'ccc';
-count(*)
-4
-select count(*) from t1 where b = 20 and c = 'ccc';
-count(*)
-1
-select count(*) from t1 where b > 20;
-count(*)
-3
-select count(*) from t1 where b = 30 and c > 'aaa';
-count(*)
-2
-select count(*) from t1 where b <= 20;
-count(*)
-6
-select count(*) from t1 where b >= 20 and c > 'aaa';
-count(*)
-4
-drop table t1;
-set ndb_index_stat_enable = on;
-set ndb_index_stat_cache_entries = 0;
-show session variables like 'ndb_index_stat_%';
-Variable_name	Value
-ndb_index_stat_cache_entries	0
-ndb_index_stat_enable	ON
-ndb_index_stat_update_freq	20
-create table t1 (a int, b int, c varchar(10) not null,
-primary key using hash (a), index(b,c)) engine=ndb;
-insert into t1 values
-(1,10,'aaa'),(2,10,'bbb'),(3,10,'ccc'),
-(4,20,'aaa'),(5,20,'bbb'),(6,20,'ccc'),
-(7,30,'aaa'),(8,30,'bbb'),(9,30,'ccc');
-select count(*) from t1 where b < 10;
-count(*)
-0
-select count(*) from t1 where b >= 10 and c >= 'bbb';
-count(*)
-6
-select count(*) from t1 where b > 10;
-count(*)
-6
-select count(*) from t1 where b <= 20 and c < 'ccc';
-count(*)
-4
-select count(*) from t1 where b = 20 and c = 'ccc';
-count(*)
-1
-select count(*) from t1 where b > 20;
-count(*)
-3
-select count(*) from t1 where b = 30 and c > 'aaa';
-count(*)
-2
-select count(*) from t1 where b <= 20;
-count(*)
-6
-select count(*) from t1 where b >= 20 and c > 'aaa';
-count(*)
-4
-drop table t1;
-set ndb_index_stat_enable = on;
-set ndb_index_stat_cache_entries = 4;
-set ndb_index_stat_update_freq = 2;
-show session variables like 'ndb_index_stat_%';
-Variable_name	Value
-ndb_index_stat_cache_entries	4
-ndb_index_stat_enable	ON
-ndb_index_stat_update_freq	2
-create table t1 (a int, b int, c varchar(10) not null,
-primary key using hash (a), index(b,c)) engine=ndb;
-insert into t1 values
-(1,10,'aaa'),(2,10,'bbb'),(3,10,'ccc'),
-(4,20,'aaa'),(5,20,'bbb'),(6,20,'ccc'),
-(7,30,'aaa'),(8,30,'bbb'),(9,30,'ccc');
-select count(*) from t1 where b < 10;
-count(*)
-0
-select count(*) from t1 where b >= 10 and c >= 'bbb';
-count(*)
-6
-select count(*) from t1 where b > 10;
-count(*)
-6
-select count(*) from t1 where b <= 20 and c < 'ccc';
-count(*)
-4
-select count(*) from t1 where b = 20 and c = 'ccc';
-count(*)
-1
-select count(*) from t1 where b > 20;
-count(*)
-3
-select count(*) from t1 where b = 30 and c > 'aaa';
-count(*)
-2
-select count(*) from t1 where b <= 20;
-count(*)
-6
-select count(*) from t1 where b >= 20 and c > 'aaa';
-count(*)
-4
-drop table t1;
-set ndb_index_stat_enable = @@global.ndb_index_stat_enable;
-set ndb_index_stat_cache_entries = @@global.ndb_index_stat_cache_entries;
-set ndb_index_stat_update_freq = @@global.ndb_index_stat_update_freq;
-show session variables like 'ndb_index_stat_%';
-Variable_name	Value
-ndb_index_stat_cache_entries	32
-ndb_index_stat_enable	OFF
-ndb_index_stat_update_freq	20
 create table t1 (a int primary key) engine = ndb;
 insert into t1 values (1), (2), (3);
 begin;

=== modified file 'mysql-test/suite/ndb/t/ndb_basic.test'
--- a/mysql-test/suite/ndb/t/ndb_basic.test	2011-04-11 13:36:12 +0000
+++ b/mysql-test/suite/ndb/t/ndb_basic.test	2011-06-15 10:37:56 +0000
@@ -758,8 +758,9 @@ drop table t10,t20;
 --echo #
 --echo # bug #39872 - explain causes segv
 --echo # (ndb_index_stat_enable=1 must be set to trigger bug)
+--echo # index stats v4: do not set, the v2 setting was local
 --echo #
-set ndb_index_stat_enable=1;
+# set ndb_index_stat_enable=1;
 CREATE TABLE `t1` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   PRIMARY KEY (`id`)
@@ -771,6 +772,7 @@ CREATE TABLE `t2` (
   KEY `obj_id` (`obj_id`)
 ) ENGINE=ndbcluster DEFAULT CHARSET=utf8;
 --echo # here we used to segv
+--replace_column 9 #
 explain SELECT t1.id FROM t1 INNER JOIN t2 ON t1.id = t2.id WHERE t2.obj_id=1;
 drop table t1, t2;
 

=== modified file 'mysql-test/suite/ndb/t/ndb_index_ordered.test'
--- a/mysql-test/suite/ndb/t/ndb_index_ordered.test	2010-10-20 17:39:53 +0000
+++ b/mysql-test/suite/ndb/t/ndb_index_ordered.test	2011-06-15 10:37:56 +0000
@@ -355,78 +355,7 @@ insert into t1 (a, c) values (1,'aaa'),(
 select count(*) from t1 where c<'bbb';
 drop table t1;
 
-# -- index statistics --
-
-set autocommit=1;
-show session variables like 'ndb_index_stat_%';
-
-set ndb_index_stat_enable = off;
-show session variables like 'ndb_index_stat_%';
-
-create table t1 (a int, b int, c varchar(10) not null,
-  primary key using hash (a), index(b,c)) engine=ndb;
-insert into t1 values
-  (1,10,'aaa'),(2,10,'bbb'),(3,10,'ccc'),
-  (4,20,'aaa'),(5,20,'bbb'),(6,20,'ccc'),
-  (7,30,'aaa'),(8,30,'bbb'),(9,30,'ccc');
-select count(*) from t1 where b < 10;
-select count(*) from t1 where b >= 10 and c >= 'bbb';
-select count(*) from t1 where b > 10;
-select count(*) from t1 where b <= 20 and c < 'ccc';
-select count(*) from t1 where b = 20 and c = 'ccc';
-select count(*) from t1 where b > 20;
-select count(*) from t1 where b = 30 and c > 'aaa';
-select count(*) from t1 where b <= 20;
-select count(*) from t1 where b >= 20 and c > 'aaa';
-drop table t1;
-
-set ndb_index_stat_enable = on;
-set ndb_index_stat_cache_entries = 0;
-show session variables like 'ndb_index_stat_%';
-
-create table t1 (a int, b int, c varchar(10) not null,
-  primary key using hash (a), index(b,c)) engine=ndb;
-insert into t1 values
-  (1,10,'aaa'),(2,10,'bbb'),(3,10,'ccc'),
-  (4,20,'aaa'),(5,20,'bbb'),(6,20,'ccc'),
-  (7,30,'aaa'),(8,30,'bbb'),(9,30,'ccc');
-select count(*) from t1 where b < 10;
-select count(*) from t1 where b >= 10 and c >= 'bbb';
-select count(*) from t1 where b > 10;
-select count(*) from t1 where b <= 20 and c < 'ccc';
-select count(*) from t1 where b = 20 and c = 'ccc';
-select count(*) from t1 where b > 20;
-select count(*) from t1 where b = 30 and c > 'aaa';
-select count(*) from t1 where b <= 20;
-select count(*) from t1 where b >= 20 and c > 'aaa';
-drop table t1;
-
-set ndb_index_stat_enable = on;
-set ndb_index_stat_cache_entries = 4;
-set ndb_index_stat_update_freq = 2;
-show session variables like 'ndb_index_stat_%';
-
-create table t1 (a int, b int, c varchar(10) not null,
-  primary key using hash (a), index(b,c)) engine=ndb;
-insert into t1 values
-  (1,10,'aaa'),(2,10,'bbb'),(3,10,'ccc'),
-  (4,20,'aaa'),(5,20,'bbb'),(6,20,'ccc'),
-  (7,30,'aaa'),(8,30,'bbb'),(9,30,'ccc');
-select count(*) from t1 where b < 10;
-select count(*) from t1 where b >= 10 and c >= 'bbb';
-select count(*) from t1 where b > 10;
-select count(*) from t1 where b <= 20 and c < 'ccc';
-select count(*) from t1 where b = 20 and c = 'ccc';
-select count(*) from t1 where b > 20;
-select count(*) from t1 where b = 30 and c > 'aaa';
-select count(*) from t1 where b <= 20;
-select count(*) from t1 where b >= 20 and c > 'aaa';
-drop table t1;
-
-set ndb_index_stat_enable = @@global.ndb_index_stat_enable;
-set ndb_index_stat_cache_entries = @@global.ndb_index_stat_cache_entries;
-set ndb_index_stat_update_freq = @@global.ndb_index_stat_update_freq;
-show session variables like 'ndb_index_stat_%';
+# index stats v4: old v2 tests are not meaningful and are removed
 
 # End of 4.1 tests
 

=== modified file 'mysql-test/suite/ndb_rpl/ndb_master-slave_2ch.inc'
--- a/mysql-test/suite/ndb_rpl/ndb_master-slave_2ch.inc	2011-05-13 07:40:50 +0000
+++ b/mysql-test/suite/ndb_rpl/ndb_master-slave_2ch.inc	2011-06-16 12:36:28 +0000
@@ -3,10 +3,11 @@
 # Set up circular cluster replication where each 
 # cluster has two mysqlds and replication directions are 
 # following:
+#              1       2
 #          master ---> slave  
 #           /            \
 #     cluster A        cluster B
-#           \            /
+#           \  3       4 /
 #         master1 <--- slave1
 #
 # ==== Usage ====
@@ -26,8 +27,9 @@
 #   $rpl_skip_start_slave, $rpl_debug, $slave_timeout
 #     See include/master-slave.inc
 
+#--let $rpl_debug= 1
 --let $rpl_topology= 1->2,4->3
---let $rpl_skip_check_server_ids= 1
+--let $rpl_skip_start_slave= 1
 --source include/rpl_init.inc
 
 # Make connections to mysqlds
@@ -37,7 +39,7 @@
 --source include/rpl_connect.inc
 
 --let $rpl_connection_name= master1
---let $rpl_server_number= 1
+--let $rpl_server_number= 3
 --source include/rpl_connect.inc
 
 --let $rpl_connection_name= slave
@@ -45,9 +47,23 @@
 --source include/rpl_connect.inc
 
 --let $rpl_connection_name= slave1
---let $rpl_server_number= 2
+--let $rpl_server_number= 4
 --source include/rpl_connect.inc
 
+# Now add IGNORE_SERVER_IDS
+--disable_query_log
+connection master;
+CHANGE MASTER TO IGNORE_SERVER_IDS= (1,3);
+connection master1;
+CHANGE MASTER TO IGNORE_SERVER_IDS= (1,3);
+connection slave;
+CHANGE MASTER TO IGNORE_SERVER_IDS= (2,4);
+connection slave1;
+CHANGE MASTER TO IGNORE_SERVER_IDS= (2,4);
+
+# Now start replication
+--source include/rpl_start_slaves.inc
+--enable_query_log
 
 # Check that all mysqld are compiled with ndb support
 --let $_rpl_server= 4

=== added file 'mysql-test/suite/ndb_rpl/ndb_master-slave_2ch_end.inc'
--- a/mysql-test/suite/ndb_rpl/ndb_master-slave_2ch_end.inc	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/ndb_rpl/ndb_master-slave_2ch_end.inc	2011-06-16 12:36:28 +0000
@@ -0,0 +1,28 @@
+# ==== Purpose ====
+#
+# Clean up replication configuration after using a 2ch
+# setup.
+# We need to explicitly reset the IGNORE_SERVER_IDS parameters
+# on all Servers to avoid testcase check errors.
+#
+# ==== Usage ====
+#
+#   [--let $rpl_debug= 1]
+#   --source suite/ndb_rpl/ndb_master-slave_2ch_end.inc
+#
+# Parameters:
+#   $rpl_debug
+#     See include/master-slave.inc
+
+--source include/rpl_stop_slaves.inc
+--connection master
+CHANGE MASTER TO IGNORE_SERVER_IDS= ();
+--connection master1
+CHANGE MASTER TO IGNORE_SERVER_IDS= ();
+--connection slave
+CHANGE MASTER TO IGNORE_SERVER_IDS= ();
+--connection slave1
+CHANGE MASTER TO IGNORE_SERVER_IDS= ();
+--source include/rpl_start_slaves.inc
+
+--source include/rpl_end.inc
\ No newline at end of file

=== modified file 'mysql-test/suite/ndb_rpl/r/ndb_rpl_circular_2ch.result'
--- a/mysql-test/suite/ndb_rpl/r/ndb_rpl_circular_2ch.result	2011-05-13 07:40:50 +0000
+++ b/mysql-test/suite/ndb_rpl/r/ndb_rpl_circular_2ch.result	2011-06-16 12:36:28 +0000
@@ -3,6 +3,7 @@ include/rpl_connect.inc [creating master
 include/rpl_connect.inc [creating master1]
 include/rpl_connect.inc [creating slave]
 include/rpl_connect.inc [creating slave1]
+include/rpl_start_slaves.inc
 
 *** Check server_id of mysqld servers ***
 SHOW VARIABLES LIKE "server_id";
@@ -12,7 +13,7 @@ SET auto_increment_offset = 1;
 SET auto_increment_increment = 2;
 SHOW VARIABLES LIKE "server_id";
 Variable_name	Value
-server_id	1
+server_id	3
 SET auto_increment_offset = 1;
 SET auto_increment_increment = 2;
 SHOW VARIABLES LIKE "server_id";
@@ -22,7 +23,7 @@ SET auto_increment_offset = 2;
 SET auto_increment_increment = 2;
 SHOW VARIABLES LIKE "server_id";
 Variable_name	Value
-server_id	2
+server_id	4
 SET auto_increment_offset = 2;
 SET auto_increment_increment = 2;
 
@@ -48,4 +49,10 @@ Check data on both clusters
 include/diff_tables.inc [master:t1, slave:t1]
 DROP TABLE IF EXISTS t1;
 
+include/rpl_stop_slaves.inc
+CHANGE MASTER TO IGNORE_SERVER_IDS= ();
+CHANGE MASTER TO IGNORE_SERVER_IDS= ();
+CHANGE MASTER TO IGNORE_SERVER_IDS= ();
+CHANGE MASTER TO IGNORE_SERVER_IDS= ();
+include/rpl_start_slaves.inc
 include/rpl_end.inc

=== modified file 'mysql-test/suite/ndb_rpl/r/ndb_rpl_conflict_max.result'
--- a/mysql-test/suite/ndb_rpl/r/ndb_rpl_conflict_max.result	2011-05-13 07:40:50 +0000
+++ b/mysql-test/suite/ndb_rpl/r/ndb_rpl_conflict_max.result	2011-06-16 14:34:56 +0000
@@ -41,6 +41,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -67,6 +70,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -87,6 +93,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -114,6 +123,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -162,10 +174,19 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
+2	1	#	1	#	#
+2	1	#	2	#	#
+2	1	#	3	#	#
 SELECT * FROM `t1$EX` ORDER BY a, d;
 server_id	master_server_id	master_epoch	count	a	d
+2	1	#	#	1	111
+2	1	#	#	2	111222
+2	1	#	#	3	111222333
 SELECT * FROM `t2$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 SELECT * FROM `t2$EX` ORDER BY a, d;
 *** slave - check update some data that causes conflicts
@@ -198,10 +219,19 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
+2	1	#	1	#	#
+2	1	#	2	#	#
+2	1	#	3	#	#
 SELECT * FROM `t1$EX` ORDER BY a, d;
 server_id	master_server_id	master_epoch	count	a	d
+2	1	#	#	1	111
+2	1	#	#	2	111222
+2	1	#	#	3	111222333
 SELECT * FROM `t2$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 SELECT * FROM `t2$EX` ORDER BY a, d;
 *** slave - check higer timestamp
@@ -259,6 +289,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -285,6 +318,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -305,6 +341,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -332,6 +371,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -380,10 +422,19 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
+2	1	#	1	#	#
+2	1	#	2	#	#
+2	1	#	3	#	#
 SELECT * FROM `t1$EX` ORDER BY a, d;
 server_id	master_server_id	master_epoch	count	a	d
+2	1	#	#	1	111
+2	1	#	#	2	111222
+2	1	#	#	3	111222333
 SELECT * FROM `t2$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 SELECT * FROM `t2$EX` ORDER BY a, d;
 *** slave - check update some data that causes conflicts
@@ -416,10 +467,19 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
+2	1	#	1	#	#
+2	1	#	2	#	#
+2	1	#	3	#	#
 SELECT * FROM `t1$EX` ORDER BY a, d;
 server_id	master_server_id	master_epoch	count	a	d
+2	1	#	#	1	111
+2	1	#	#	2	111222
+2	1	#	#	3	111222333
 SELECT * FROM `t2$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 SELECT * FROM `t2$EX` ORDER BY a, d;
 *** slave - check higer timestamp
@@ -473,10 +533,13 @@ a	b	c	d
 3	Master t2 c=2	2	123
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX";
 VARIABLE_VALUE-@init_ndb_conflict_fn_max
-0
+3
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
-3
+0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 2	1	#	1	#	#
@@ -505,10 +568,13 @@ a	b	c	d
 3	Master t2 a=3 at c=3	3	123
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX";
 VARIABLE_VALUE-@init_ndb_conflict_fn_max
-0
+4
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
-4
+0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 2	1	#	1	#	#
@@ -533,10 +599,13 @@ select * from t2 order by a, d;
 a	b	c	d
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX";
 VARIABLE_VALUE-@init_ndb_conflict_fn_max
-0
+4
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
-4
+0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 2	1	#	1	#	#
@@ -572,6 +641,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -616,10 +688,13 @@ commit;
 *** slave - check conflict info, there should be some
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX";
 VARIABLE_VALUE-@init_ndb_conflict_fn_max
-0
+3
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
-3
+0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 2	1	#	5	#	#
@@ -658,10 +733,13 @@ commit;
 *** slave - check conflict info, change depends on calling test
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX";
 VARIABLE_VALUE-@init_ndb_conflict_fn_max
-0
+6
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
-6
+0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 2	1	#	5	#	#
@@ -733,6 +811,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -759,6 +840,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -779,6 +863,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -806,6 +893,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -850,15 +940,24 @@ commit;
 *** slave - check conflict info, there should be some
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX";
 VARIABLE_VALUE-@init_ndb_conflict_fn_max
-3
+4
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
-1
+0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 2	1	#	1	#	#
+2	1	#	2	#	#
+2	1	#	3	#	#
+2	1	#	4	#	#
 SELECT * FROM `t1$EX` ORDER BY a, d;
 server_id	master_server_id	master_epoch	count	a	d
+2	1	#	#	1	111
+2	1	#	#	2	111222
+2	1	#	#	3	111222333
 2	1	#	#	4	111222333
 SELECT * FROM `t2$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 SELECT * FROM `t2$EX` ORDER BY a, d;
@@ -889,15 +988,24 @@ commit;
 *** slave - check conflict info, change depends on calling test
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX";
 VARIABLE_VALUE-@init_ndb_conflict_fn_max
-3
+4
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
-1
+0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 2	1	#	1	#	#
+2	1	#	2	#	#
+2	1	#	3	#	#
+2	1	#	4	#	#
 SELECT * FROM `t1$EX` ORDER BY a, d;
 server_id	master_server_id	master_epoch	count	a	d
+2	1	#	#	1	111
+2	1	#	#	2	111222
+2	1	#	#	3	111222333
 2	1	#	#	4	111222333
 SELECT * FROM `t2$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 SELECT * FROM `t2$EX` ORDER BY a, d;

=== modified file 'mysql-test/suite/ndb_rpl/r/ndb_rpl_conflict_max_delete_win.result'
--- a/mysql-test/suite/ndb_rpl/r/ndb_rpl_conflict_max_delete_win.result	2011-05-13 07:40:50 +0000
+++ b/mysql-test/suite/ndb_rpl/r/ndb_rpl_conflict_max_delete_win.result	2011-06-16 14:34:56 +0000
@@ -41,6 +41,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -67,6 +70,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -87,6 +93,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -114,6 +123,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -158,14 +170,23 @@ commit;
 *** slave - check conflict info, there should be some
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX";
 VARIABLE_VALUE-@init_ndb_conflict_fn_max
-3
+0
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+3
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
+2	1	#	1	#	#
+2	1	#	2	#	#
+2	1	#	3	#	#
 SELECT * FROM `t1$EX` ORDER BY a, d;
 server_id	master_server_id	master_epoch	count	a	d
+2	1	#	#	1	111
+2	1	#	#	2	111222
+2	1	#	#	3	111222333
 SELECT * FROM `t2$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 SELECT * FROM `t2$EX` ORDER BY a, d;
 *** slave - check update some data that causes conflicts
@@ -194,14 +215,23 @@ commit;
 *** slave - check conflict info, change depends on calling test
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX";
 VARIABLE_VALUE-@init_ndb_conflict_fn_max
-3
+0
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+3
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
+2	1	#	1	#	#
+2	1	#	2	#	#
+2	1	#	3	#	#
 SELECT * FROM `t1$EX` ORDER BY a, d;
 server_id	master_server_id	master_epoch	count	a	d
+2	1	#	#	1	111
+2	1	#	#	2	111222
+2	1	#	#	3	111222333
 SELECT * FROM `t2$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 SELECT * FROM `t2$EX` ORDER BY a, d;
 *** slave - check higer timestamp
@@ -259,6 +289,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -285,6 +318,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -305,6 +341,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -332,6 +371,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -376,14 +418,23 @@ commit;
 *** slave - check conflict info, there should be some
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX";
 VARIABLE_VALUE-@init_ndb_conflict_fn_max
-3
+0
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+3
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
+2	1	#	1	#	#
+2	1	#	2	#	#
+2	1	#	3	#	#
 SELECT * FROM `t1$EX` ORDER BY a, d;
 server_id	master_server_id	master_epoch	count	a	d
+2	1	#	#	1	111
+2	1	#	#	2	111222
+2	1	#	#	3	111222333
 SELECT * FROM `t2$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 SELECT * FROM `t2$EX` ORDER BY a, d;
 *** slave - check update some data that causes conflicts
@@ -412,14 +463,23 @@ commit;
 *** slave - check conflict info, change depends on calling test
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX";
 VARIABLE_VALUE-@init_ndb_conflict_fn_max
-3
+0
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+3
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
+2	1	#	1	#	#
+2	1	#	2	#	#
+2	1	#	3	#	#
 SELECT * FROM `t1$EX` ORDER BY a, d;
 server_id	master_server_id	master_epoch	count	a	d
+2	1	#	#	1	111
+2	1	#	#	2	111222
+2	1	#	#	3	111222333
 SELECT * FROM `t2$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 SELECT * FROM `t2$EX` ORDER BY a, d;
 *** slave - check higer timestamp
@@ -476,6 +536,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 0
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
+0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
 3
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
@@ -508,6 +571,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 0
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
+0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
 4
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
@@ -536,6 +602,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 0
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
+0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
 4
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
@@ -572,6 +641,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -619,6 +691,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 0
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
+0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
 3
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
@@ -661,6 +736,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 0
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
+0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
 6
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
@@ -733,6 +811,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -759,6 +840,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -779,6 +863,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -806,6 +893,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -850,14 +940,23 @@ commit;
 *** slave - check conflict info, there should be some
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX";
 VARIABLE_VALUE-@init_ndb_conflict_fn_max
-3
+0
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+3
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
+2	1	#	1	#	#
+2	1	#	2	#	#
+2	1	#	3	#	#
 SELECT * FROM `t1$EX` ORDER BY a, d;
 server_id	master_server_id	master_epoch	count	a	d
+2	1	#	#	1	111
+2	1	#	#	2	111222
+2	1	#	#	3	111222333
 SELECT * FROM `t2$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 SELECT * FROM `t2$EX` ORDER BY a, d;
 *** slave - check update some data that causes conflicts
@@ -886,14 +985,23 @@ commit;
 *** slave - check conflict info, change depends on calling test
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX";
 VARIABLE_VALUE-@init_ndb_conflict_fn_max
-3
+0
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+3
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
+2	1	#	1	#	#
+2	1	#	2	#	#
+2	1	#	3	#	#
 SELECT * FROM `t1$EX` ORDER BY a, d;
 server_id	master_server_id	master_epoch	count	a	d
+2	1	#	#	1	111
+2	1	#	#	2	111222
+2	1	#	#	3	111222333
 SELECT * FROM `t2$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 SELECT * FROM `t2$EX` ORDER BY a, d;
 *** slave - check higer timestamp

=== modified file 'mysql-test/suite/ndb_rpl/r/ndb_rpl_conflict_old.result'
--- a/mysql-test/suite/ndb_rpl/r/ndb_rpl_conflict_old.result	2011-05-13 07:40:50 +0000
+++ b/mysql-test/suite/ndb_rpl/r/ndb_rpl_conflict_old.result	2011-06-16 14:34:56 +0000
@@ -41,6 +41,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -67,6 +70,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -87,6 +93,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -114,6 +123,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -162,6 +174,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 3
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 2	1	#	1	#	#
@@ -204,6 +219,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 5
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 2	1	#	1	#	#
@@ -275,6 +293,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -301,6 +322,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -321,6 +345,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -348,6 +375,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -396,6 +426,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 3
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 2	1	#	1	#	#
@@ -438,6 +471,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 5
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 2	1	#	1	#	#
@@ -509,6 +545,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 3
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 2	1	#	1	#	#
@@ -541,6 +580,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 4
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 2	1	#	1	#	#
@@ -569,6 +611,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 4
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 2	1	#	1	#	#
@@ -604,6 +649,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -652,6 +700,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 3
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 2	1	#	5	#	#
@@ -694,6 +745,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 6
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 2	1	#	5	#	#
@@ -765,6 +819,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -791,6 +848,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -811,6 +871,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -838,6 +901,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 0
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 SELECT * FROM `t1$EX` ORDER BY a, d;
@@ -886,6 +952,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 4
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 2	1	#	1	#	#
@@ -931,6 +1000,9 @@ VARIABLE_VALUE-@init_ndb_conflict_fn_max
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
 VARIABLE_VALUE-@init_ndb_conflict_fn_old
 6
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
+VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win
+0
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;
 server_id	master_server_id	master_epoch	count	a	d
 2	1	#	1	#	#

=== modified file 'mysql-test/suite/ndb_rpl/r/ndb_rpl_rep_error.result'
--- a/mysql-test/suite/ndb_rpl/r/ndb_rpl_rep_error.result	2011-05-13 07:40:50 +0000
+++ b/mysql-test/suite/ndb_rpl/r/ndb_rpl_rep_error.result	2011-06-16 14:34:56 +0000
@@ -6,9 +6,11 @@ Error	1626	Bad schema for mysql.ndb_repl
 drop table t1;
 insert into mysql.ndb_replication values ("test", "t1", 0, NULL, "NDB$X(X)");
 create table t1 (a int key, X int) engine ndb;
-Warnings:
+ERROR HY000: Can't create table 'test.t1' (errno: 1627)
+show warnings;
+Level	Code	Message
 Error	1627	Error in parsing conflict function. Message: NDB$X(X), unknown conflict resolution function at 'NDB$X(X)'
-drop table t1;
+Error	1005	Can't create table 'test.t1' (errno: 1627)
 delete from mysql.ndb_replication;
 insert into mysql.ndb_replication values ("test", "t1", 0, NULL, "NDB$MAX(X)");
 create table t1 (a int key, X int) engine ndb;
@@ -18,13 +20,17 @@ drop table t1;
 delete from mysql.ndb_replication;
 insert into mysql.ndb_replication values ("test", "t1", 0, NULL, "NDB$MAX()");
 create table t1 (a int key, X int) engine ndb;
-Warnings:
+ERROR HY000: Can't create table 'test.t1' (errno: 1627)
+show warnings;
+Level	Code	Message
 Error	1627	Error in parsing conflict function. Message: NDB$MAX(), missing function argument at ')'
-drop table t1;
+Error	1005	Can't create table 'test.t1' (errno: 1627)
 delete from mysql.ndb_replication;
 insert into mysql.ndb_replication values ("test", "t1", 0, NULL, "NDB$MAX(X Y)");
 create table t1 (a int key, X int) engine ndb;
-Warnings:
+ERROR HY000: Can't create table 'test.t1' (errno: 1627)
+show warnings;
+Level	Code	Message
 Error	1627	Error in parsing conflict function. Message: NDB$MAX(X Y), missing ')' at 'Y)'
-drop table t1;
+Error	1005	Can't create table 'test.t1' (errno: 1627)
 delete from mysql.ndb_replication;

=== modified file 'mysql-test/suite/ndb_rpl/t/ndb_conflict_info.inc'
--- a/mysql-test/suite/ndb_rpl/t/ndb_conflict_info.inc	2011-05-13 07:40:50 +0000
+++ b/mysql-test/suite/ndb_rpl/t/ndb_conflict_info.inc	2011-06-16 14:34:56 +0000
@@ -1,5 +1,6 @@
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX";
 SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_old FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
+SELECT VARIABLE_VALUE-@init_ndb_conflict_fn_max_del_win FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
 --replace_column 3 # 5 # 6 #
 --error 0,1146
 SELECT server_id, master_server_id, master_epoch, count, a, d FROM `t1$EX` ORDER BY server_id, master_server_id, master_epoch, count;

=== modified file 'mysql-test/suite/ndb_rpl/t/ndb_conflict_info_init.inc'
--- a/mysql-test/suite/ndb_rpl/t/ndb_conflict_info_init.inc	2011-05-13 07:40:50 +0000
+++ b/mysql-test/suite/ndb_rpl/t/ndb_conflict_info_init.inc	2011-06-16 14:34:56 +0000
@@ -2,6 +2,7 @@
 --disable_result_log
 SELECT @init_ndb_conflict_fn_max:=(VARIABLE_VALUE+0) FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX";
 SELECT @init_ndb_conflict_fn_old:=(VARIABLE_VALUE+0) FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_OLD";
+SELECT @init_ndb_conflict_fn_max_del_win:=(VARIABLE_VALUE+0) FROM INFORMATION_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME LIKE "NDB_CONFLICT_FN_MAX_DEL_WIN";
 --error 0,1146
 DELETE FROM `t1$EX`;
 --enable_query_log

=== modified file 'mysql-test/suite/ndb_rpl/t/ndb_rpl_circular_2ch.cnf'
--- a/mysql-test/suite/ndb_rpl/t/ndb_rpl_circular_2ch.cnf	2011-05-13 07:40:50 +0000
+++ b/mysql-test/suite/ndb_rpl/t/ndb_rpl_circular_2ch.cnf	2011-06-16 12:36:28 +0000
@@ -2,34 +2,39 @@
 
 # 2 clusters, each with 2 MySQLDs
 # All MySQLDs log-slave-updates
-# Potential infinite loops are broken by both servers 
-# on each cluster having the same server-id
-# To support > 2 clusters and/or different server-ids per
-# MySQLD server, we need some other loop breaking 
-# mechanism 
+# All MySQLDs log-apply-status
+# Infinite loops broken in the test using Ignore_server_ids mechanism
 
 [mysqld.1.1]
 server-id= 1
 log-bin
+ndb_connectstring=	@mysql_cluster.1.ndb_connectstring
 log-slave-updates
+skip-slave-start
 
 [mysqld.2.1]
-server-id= 1
+server-id= 3
 log-bin
+ndb_connectstring=	@mysql_cluster.1.ndb_connectstring
 log-slave-updates
+skip-slave-start
 
 [mysqld.1.slave]
 server-id= 2
 log-bin
+ndb_connectstring=	@mysql_cluster.slave.ndb_connectstring
 log-slave-updates
 skip-slave-start
 
 [mysqld.2.slave]
-server-id= 2
+server-id= 4
 log-bin
 ndb_connectstring=	@mysql_cluster.slave.ndb_connectstring
+log-slave-updates
+skip-slave-start
 
 [ENV]
-
-SLAVE_MYPORT1=		@mysqld.2.slave.port
-SLAVE_MYSOCK1=		@mysqld.2.slave.socket
+SERVER_MYPORT_1=        @mysqld.1.1.port
+SERVER_MYPORT_2=        @mysqld.1.slave.port
+SERVER_MYPORT_3=        @mysqld.2.1.port
+SERVER_MYPORT_4=        @mysqld.2.slave.port

=== modified file 'mysql-test/suite/ndb_rpl/t/ndb_rpl_circular_2ch.test'
--- a/mysql-test/suite/ndb_rpl/t/ndb_rpl_circular_2ch.test	2011-05-13 07:40:50 +0000
+++ b/mysql-test/suite/ndb_rpl/t/ndb_rpl_circular_2ch.test	2011-06-16 12:36:28 +0000
@@ -163,5 +163,8 @@ DROP TABLE IF EXISTS t1;
 --source include/wait_for_query_to_fail.inc
 --echo
 
+--connection master
+
 # End of test 5.1
---source include/rpl_end.inc
+--source suite/ndb_rpl/ndb_master-slave_2ch_end.inc
+

=== modified file 'mysql-test/suite/ndb_rpl/t/ndb_rpl_rep_error.test'
--- a/mysql-test/suite/ndb_rpl/t/ndb_rpl_rep_error.test	2011-05-13 07:40:50 +0000
+++ b/mysql-test/suite/ndb_rpl/t/ndb_rpl_rep_error.test	2011-06-16 14:34:56 +0000
@@ -47,10 +47,11 @@ CREATE TABLE mysql.ndb_replication
 --enable_query_log
 
 # Non existant conflict_fn
-# gives warning when creating table
+# gives error when creating table
 insert into mysql.ndb_replication values ("test", "t1", 0, NULL, "NDB$X(X)");
+--error 1005
 create table t1 (a int key, X int) engine ndb;
-drop table t1;
+show warnings;
 delete from mysql.ndb_replication;
 
 # Column type cannot be used for this function
@@ -61,17 +62,19 @@ drop table t1;
 delete from mysql.ndb_replication;
 
 # Too few arguments
-# gives warning when creating table
+# gives error when creating table
 insert into mysql.ndb_replication values ("test", "t1", 0, NULL, "NDB$MAX()");
+--error 1005
 create table t1 (a int key, X int) engine ndb;
-drop table t1;
+show warnings;
 delete from mysql.ndb_replication;
 
 # Too many arguments
-# gives warning when creating table
+# gives error when creating table
 insert into mysql.ndb_replication values ("test", "t1", 0, NULL, "NDB$MAX(X Y)");
+--error 1005
 create table t1 (a int key, X int) engine ndb;
-drop table t1;
+show warnings;
 delete from mysql.ndb_replication;
 
 --disable_query_log

=== modified file 'sql/Makefile.am'
--- a/sql/Makefile.am	2011-04-10 17:32:41 +0000
+++ b/sql/Makefile.am	2011-06-17 12:41:11 +0000
@@ -63,6 +63,7 @@ noinst_HEADERS =	item.h item_func.h item
 			ha_ndbcluster_connection.h ha_ndbcluster_connection.h \
 			ha_ndbcluster_lock_ext.h ha_ndbinfo.h \
 			ha_ndbcluster_glue.h \
+			ha_ndb_index_stat.h \
 			ha_partition.h rpl_constants.h \
 			debug_sync.h \
 			opt_range.h protocol.h rpl_tblmap.h rpl_utility.h \
@@ -139,6 +140,7 @@ libndb_la_SOURCES=	ha_ndbcluster.cc \
 			ha_ndbcluster_connection.cc \
 			ha_ndbcluster_cond.cc \
 			ha_ndbcluster_push.cc \
+			ha_ndb_index_stat.cc \
 			ha_ndbinfo.cc
 
 gen_lex_hash_SOURCES =	gen_lex_hash.cc

=== added file 'sql/ha_ndb_index_stat.cc'
--- a/sql/ha_ndb_index_stat.cc	1970-01-01 00:00:00 +0000
+++ b/sql/ha_ndb_index_stat.cc	2011-06-16 11:36:45 +0000
@@ -0,0 +1,1962 @@
+/*
+   Copyright (c) 2011, 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
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+*/
+
+#include "ha_ndbcluster_glue.h"
+
+#ifdef WITH_NDBCLUSTER_STORAGE_ENGINE
+
+#include "ha_ndbcluster.h"
+#include "ha_ndb_index_stat.h"
+#include <mysql/plugin.h>
+#include <ctype.h>
+
+// copied from ha_ndbcluster_binlog.h
+
+extern handlerton *ndbcluster_hton;
+
+inline
+void
+set_thd_ndb(THD *thd, Thd_ndb *thd_ndb)
+{ thd_set_ha_data(thd, ndbcluster_hton, thd_ndb); }
+
+// Typedefs for long names 
+typedef NdbDictionary::Table NDBTAB;
+typedef NdbDictionary::Index NDBINDEX;
+
+struct Ndb_index_stat {
+  enum {
+    LT_Undef= 0,
+    LT_New= 1,          /* new entry added by a table handler */
+    LT_Update = 2,      /* force kernel update from analyze table */
+    LT_Read= 3,         /* read or reread stats into new query cache */
+    LT_Idle= 4,         /* stats exist */
+    LT_Check= 5,        /* check for new stats */
+    LT_Delete= 6,       /* delete the entry */
+    LT_Error= 7,        /* error, on hold for a while */
+    LT_Count= 8
+  };
+  NdbIndexStat* is;
+  int index_id;
+  int index_version;
+#ifndef DBUG_OFF
+  char id[32];
+#endif
+  time_t access_time;   /* by any table handler */
+  time_t load_time;     /* when stats were created by kernel */
+  time_t read_time;     /* when stats were read by us (>= load_time) */
+  uint sample_version;  /* goes with read_time */
+  time_t check_time;    /* when checked for updated stats (>= read_time) */
+  bool cache_clean;     /* old caches have been deleted */
+  uint force_update;    /* one-time force update from analyze table */
+  NdbIndexStat::Error error;
+  time_t error_time;
+  int error_count;
+  struct Ndb_index_stat *share_next; /* per-share list */
+  int lt;
+  int lt_old;     /* for info only */
+  struct Ndb_index_stat *list_next;
+  struct Ndb_index_stat *list_prev;
+  struct st_ndbcluster_share *share;
+  Ndb_index_stat();
+};
+
+struct Ndb_index_stat_list {
+  const char *name;
+  int lt;
+  struct Ndb_index_stat *head;
+  struct Ndb_index_stat *tail;
+  uint count;
+  Ndb_index_stat_list(int the_lt, const char* the_name);
+};
+
+extern Ndb_index_stat_list ndb_index_stat_list[];
+
+time_t ndb_index_stat_time_now= 0;
+
+time_t
+ndb_index_stat_time()
+{
+  time_t now= time(0);
+
+  if (unlikely(ndb_index_stat_time_now == 0))
+    ndb_index_stat_time_now= now;
+
+  if (unlikely(now < ndb_index_stat_time_now))
+  {
+    DBUG_PRINT("index_stat", ("time moved backwards %d seconds",
+                              int(ndb_index_stat_time_now - now)));
+    now= ndb_index_stat_time_now;
+  }
+
+  ndb_index_stat_time_now= now;
+  return now;
+}
+
+bool ndb_index_stat_allow_flag= false;
+
+bool
+ndb_index_stat_allow(int flag= -1)
+{
+  if (flag != -1) {
+    pthread_mutex_lock(&ndb_index_stat_list_mutex);
+    ndb_index_stat_allow_flag= (bool)flag;
+    pthread_mutex_unlock(&ndb_index_stat_list_mutex);
+  }
+  return ndb_index_stat_allow_flag;
+}
+
+/* Options */
+
+/* Options in string format buffer size */
+static const uint ndb_index_stat_option_sz= 512;
+void ndb_index_stat_opt2str(const struct Ndb_index_stat_opt&, char*);
+
+struct Ndb_index_stat_opt {
+  enum Unit {
+    Ubool = 1,
+    Usize = 2,
+    Utime = 3,
+    Umsec = 4
+  };
+  enum Flag {
+    Freadonly = (1 << 0)
+  };
+  struct Val {
+    const char* name;
+    uint val;
+    uint minval;
+    uint maxval;
+    Unit unit;
+    uint flag;
+  };
+  enum Idx {
+    Iloop_checkon = 0,
+    Iloop_idle = 1,
+    Iloop_busy = 2,
+    Iupdate_batch = 3,
+    Iread_batch = 4,
+    Iidle_batch = 5,
+    Icheck_batch = 6,
+    Icheck_delay = 7,
+    Idelete_batch = 8,
+    Iclean_delay = 9,
+    Ierror_batch = 10,
+    Ierror_delay = 11,
+    Ievict_batch = 12,
+    Ievict_delay = 13,
+    Icache_limit = 14,
+    Icache_lowpct = 15,
+    Imax = 16
+  };
+  Val val[Imax];
+  /* Options in string format (SYSVAR ndb_index_stat_option) */
+  char *option;
+  Ndb_index_stat_opt(char* buf);
+  uint get(Idx i) const {
+    assert(i < Imax);
+    return val[i].val;
+  }
+};
+
+Ndb_index_stat_opt::Ndb_index_stat_opt(char* buf) :
+  option(buf)
+{
+#define ival(aname, aval, aminval, amaxval, aunit, aflag) \
+  val[I##aname].name = #aname; \
+  val[I##aname].val = aval; \
+  val[I##aname].minval = aminval; \
+  val[I##aname].maxval = amaxval; \
+  val[I##aname].unit = aunit; \
+  val[I##aname].flag = aflag
+  ival(loop_checkon, 1000, 0, ~0, Umsec, 0);
+  ival(loop_idle, 1000, 0, ~0, Umsec, 0);
+  ival(loop_busy, 100, 0, ~0, Umsec, 0);
+  ival(update_batch, 1, 1, ~0, Usize, 0);
+  ival(read_batch, 4, 1, ~0, Usize, 0);
+  ival(idle_batch, 32, 1, ~0, Usize, 0);
+  ival(check_batch, 32, 1, ~0, Usize, 0);
+  ival(check_delay, 60, 0, ~0, Utime, 0);
+  ival(clean_delay, 0, 0, ~0, Utime, 0);
+  ival(delete_batch, 8, 1, ~0, Usize, 0);
+  ival(error_batch, 4, 1, ~0, Usize, 0);
+  ival(error_delay, 60, 0, ~0, Utime, 0);
+  ival(evict_batch, 8, 1, ~0, Usize, 0);
+  ival(evict_delay, 60, 0, ~0, Utime, 0);
+  ival(cache_limit, 32*1024*1024, 1024*1024, ~0, Usize, 0);
+  ival(cache_lowpct, 90, 0, 100, Usize, 0);
+#undef ival
+
+  ndb_index_stat_opt2str(*this, option);
+}
+
+/* Hard limits */
+static const uint ndb_index_stat_max_evict_batch = 32;
+
+char ndb_index_stat_option_buf[ndb_index_stat_option_sz];
+Ndb_index_stat_opt ndb_index_stat_opt(ndb_index_stat_option_buf);
+
+/* Copy option struct to string buffer */
+void
+ndb_index_stat_opt2str(const Ndb_index_stat_opt& opt, char* str)
+{
+  DBUG_ENTER("ndb_index_stat_opt2str");
+
+  char buf[ndb_index_stat_option_sz];
+  char *const end= &buf[sizeof(buf)];
+  char* ptr= buf;
+  *ptr= 0;
+
+  const uint imax= Ndb_index_stat_opt::Imax;
+  for (uint i= 0; i < imax; i++)
+  {
+    const Ndb_index_stat_opt::Val& v= opt.val[i];
+    ptr+= strlen(ptr);
+    const char* sep= (ptr == buf ? "" : ",");
+    const uint sz= ptr < end ? end - ptr : 0;
+
+    switch (v.unit) {
+    case Ndb_index_stat_opt::Ubool:
+      {
+        DBUG_ASSERT(v.val == 0 || v.val == 1);
+        if (v.val == 0)
+          my_snprintf(ptr, sz, "%s%s=OFF", sep, v.name);
+        else
+          my_snprintf(ptr, sz, "%s%s=ON", sep, v.name);
+      }
+      break;
+
+    case Ndb_index_stat_opt::Usize:
+      {
+        uint m;
+        if (v.val == 0)
+          my_snprintf(ptr, sz, "%s%s=0", sep, v.name);
+        else if (v.val % (m= 1024*1024*1024) == 0)
+          my_snprintf(ptr, sz, "%s%s=%uG", sep, v.name, v.val / m);
+        else if (v.val % (m= 1024*1024) == 0)
+          my_snprintf(ptr, sz, "%s%s=%uM", sep, v.name, v.val / m);
+        else if (v.val % (m= 1024) == 0)
+          my_snprintf(ptr, sz, "%s%s=%uK", sep, v.name, v.val / m);
+        else
+          my_snprintf(ptr, sz, "%s%s=%u", sep, v.name, v.val);
+      }
+      break;
+
+    case Ndb_index_stat_opt::Utime:
+      {
+        uint m;
+        if (v.val == 0)
+          my_snprintf(ptr, sz, "%s%s=0", sep, v.name);
+        else if (v.val % (m= 60*60*24) == 0)
+          my_snprintf(ptr, sz, "%s%s=%ud", sep, v.name, v.val / m);
+        else if (v.val % (m= 60*60) == 0)
+          my_snprintf(ptr, sz, "%s%s=%uh", sep, v.name, v.val / m);
+        else if (v.val % (m= 60) == 0)
+          my_snprintf(ptr, sz, "%s%s=%um", sep, v.name, v.val / m);
+        else
+          my_snprintf(ptr, sz, "%s%s=%us", sep, v.name, v.val);
+      }
+      break;
+
+    case Ndb_index_stat_opt::Umsec:
+      {
+        if (v.val == 0)
+          my_snprintf(ptr, sz, "%s%s=0", sep, v.name);
+        else
+          my_snprintf(ptr, sz, "%s%s=%ums", sep, v.name, v.val);
+      }
+      break;
+
+    default:
+      DBUG_ASSERT(false);
+      break;
+    }
+  }
+
+  memset(str, 0, ndb_index_stat_option_sz);
+  strcpy(str, buf);
+  DBUG_PRINT("index_stat", ("str: \"%s\"", str));
+  DBUG_VOID_RETURN;
+}
+
+int
+ndb_index_stat_option_parse(char* p, Ndb_index_stat_opt& opt)
+{
+  DBUG_ENTER("ndb_index_stat_option_parse");
+
+  char *r= strchr(p, '=');
+  if (r == 0)
+    DBUG_RETURN(-1);
+  *r++= 0;
+
+  while (isspace(*r))
+    *r++= 0;
+  if (*r == 0)
+    DBUG_RETURN(-1);
+
+  const uint imax= Ndb_index_stat_opt::Imax;
+  for (uint i= 0; i < imax; i++)
+  {
+    Ndb_index_stat_opt::Val& v= opt.val[i];
+    if (strcmp(p, v.name) != 0)
+      continue;
+
+    char *s;
+    for (s= r; *s != 0; s++)
+      *s= tolower(*s);
+    ulonglong val= strtoull(r, &s, 10);
+
+    switch (v.unit) {
+    case Ndb_index_stat_opt::Ubool:
+      {
+        if ((s > r && *s == 0 && val == 0) ||
+            strcmp(r, "off") == 0 ||
+            strcmp(r, "false") == 0)
+          val= 0;
+        else if ((s > r && *s == 0 && val == 1) ||
+            strcmp(r, "on") == 0 ||
+            strcmp(r, "true") == 0)
+          val= 1;
+        else
+          DBUG_RETURN(-1);
+        v.val= (uint)val;
+      }
+      break;
+
+    case Ndb_index_stat_opt::Usize:
+      {
+        if (s == r)
+          DBUG_RETURN(-1);
+        if (strcmp(s, "") == 0)
+          ;
+        else if (strcmp(s, "k") == 0)
+          val*= 1024;
+        else if (strcmp(s, "m") == 0)
+          val*= 1024*1024;
+        else if (strcmp(s, "g") == 0)
+          val*= 1024*1024*1024;
+        else
+          DBUG_RETURN(-1);
+        if (val < v.minval || val > v.maxval)
+          DBUG_RETURN(-1);
+        v.val= (uint)val;
+      }
+      break;
+
+    case Ndb_index_stat_opt::Utime:
+      {
+        if (s == r)
+          DBUG_RETURN(-1);
+        if (strcmp(s, "") == 0)
+          ;
+        else if (strcmp(s, "s") == 0)
+          ;
+        else if (strcmp(s, "m") == 0)
+          val*= 60;
+        else if (strcmp(s, "h") == 0)
+          val*= 60*60;
+        else if (strcmp(s, "d") == 0)
+          val*= 24*60*60;
+        else
+          DBUG_RETURN(-1);
+        if (val < v.minval || val > v.maxval)
+          DBUG_RETURN(-1);
+        v.val= (uint)val;
+      }
+      break;
+
+    case Ndb_index_stat_opt::Umsec:
+      {
+        if (s == r)
+          DBUG_RETURN(-1);
+        if (strcmp(s, "") == 0)
+          ;
+        else if (strcmp(s, "ms") == 0)
+          ;
+        else
+          DBUG_RETURN(-1);
+        if (val < v.minval || val > v.maxval)
+          DBUG_RETURN(-1);
+        v.val= (uint)val;
+      }
+      break;
+
+    default:
+      DBUG_ASSERT(false);
+      break;
+    }
+  }
+  DBUG_RETURN(0);
+}
+
+/* Copy option string to option struct */
+int
+ndb_index_stat_str2opt(const char *str, Ndb_index_stat_opt& opt)
+{
+  DBUG_ENTER("ndb_index_stat_str2opt");
+  DBUG_PRINT("index_stat", ("str: \"%s\"", str));
+
+  char buf[ndb_index_stat_option_sz];
+
+  assert(str != 0);
+  if (strlen(str) >= sizeof(buf))
+    DBUG_RETURN(-1);
+  strcpy(buf, str);
+
+  char *p= buf;
+  while (1)
+  {
+    while (isspace(*p))
+      p++;
+    if (*p == 0)
+      break;
+
+    char *q= strchr(p, ',');
+    if (q == p)
+      DBUG_RETURN(-1);
+    if (q != 0)
+      *q= 0;
+
+    DBUG_PRINT("index_stat", ("parse: %s", p));
+    if (ndb_index_stat_option_parse(p, opt) == -1)
+      DBUG_RETURN(-1);
+
+    if (q == 0)
+      break;
+    p= q + 1;
+  }
+
+  ndb_index_stat_opt2str(opt, opt.option);
+  DBUG_RETURN(0);
+}
+
+/* Thanks to ha_innodb.cc */
+
+/* Need storage between check and update (assume locked) */
+char ndb_index_stat_option_tmp[ndb_index_stat_option_sz];
+ 
+int
+ndb_index_stat_option_check(MYSQL_THD,
+                            struct st_mysql_sys_var *var,
+                            void *save,
+                            struct st_mysql_value *value)
+{
+  DBUG_ENTER("ndb_index_stat_option_check");
+  char buf[ndb_index_stat_option_sz];
+  int len= sizeof(buf);
+  const char *str= value->val_str(value, buf, &len);
+  if (str != 0)
+  {
+    /* Seems to be nothing in buf */
+    DBUG_PRINT("index_stat", ("str: %s len: %d", str, len));
+    char buf2[ndb_index_stat_option_sz];
+    Ndb_index_stat_opt opt(buf2);
+    if (ndb_index_stat_str2opt(str, opt) == 0)
+    {
+      /* Passed to update */
+      strcpy(ndb_index_stat_option_tmp, str);
+      *(const char**)save= ndb_index_stat_option_tmp;
+      DBUG_RETURN(0);
+    }
+  }
+  DBUG_RETURN(1);
+}
+
+void
+ndb_index_stat_option_update(MYSQL_THD,
+                             struct st_mysql_sys_var *var,
+                             void *var_ptr,
+                             const void *save)
+{
+  DBUG_ENTER("ndb_index_stat_option_update");
+  const char *str= *(const char**)save;
+  DBUG_PRINT("index_stat", ("str: %s", str));
+  Ndb_index_stat_opt& opt= ndb_index_stat_opt;
+  int ret= ndb_index_stat_str2opt(str, opt);
+  assert(ret == 0);
+  *(const char**)var_ptr= ndb_index_stat_opt.option;
+  DBUG_VOID_RETURN;
+}
+
+/* Global stuff */
+
+struct Ndb_index_stat_glob {
+  uint list_count[Ndb_index_stat::LT_Count]; /* Temporary use */
+  uint total_count;
+  uint force_update;
+  uint wait_update;
+  uint cache_query_bytes; /* In use */
+  uint cache_clean_bytes; /* Obsolete versions not yet removed */
+  bool is_locked;
+  Ndb_index_stat_glob() :
+    total_count(0),
+    force_update(0),
+    wait_update(0),
+    cache_query_bytes(0),
+    cache_clean_bytes(0),
+    is_locked(false)
+  {
+  }
+  void set_list_count()
+  {
+    int lt;
+    for (lt= 0; lt < Ndb_index_stat::LT_Count; lt++)
+    {
+      const Ndb_index_stat_list &list= ndb_index_stat_list[lt];
+      list_count[lt]= list.count;
+    }
+  }
+  void lock()
+  {
+    pthread_mutex_lock(&ndb_index_stat_glob_mutex);
+    assert(!is_locked);
+    is_locked= true;
+  }
+  void unlock()
+  {
+    assert(is_locked);
+    g_ndb_status_index_stat_cache_query= cache_query_bytes;
+    g_ndb_status_index_stat_cache_clean= cache_clean_bytes;
+    is_locked= false;
+    pthread_mutex_unlock(&ndb_index_stat_glob_mutex);
+  }
+};
+
+Ndb_index_stat_glob ndb_index_stat_glob;
+
+/* Shared index entries */
+
+Ndb_index_stat::Ndb_index_stat()
+{
+  is= 0;
+  index_id= 0;
+  index_version= 0;
+#ifndef DBUG_OFF
+  memset(id, 0, sizeof(id));
+#endif
+  access_time= 0;
+  load_time= 0;
+  read_time= 0;
+  sample_version= 0;
+  check_time= 0;
+  cache_clean= false;
+  force_update= 0;
+  error_time= 0;
+  error_count= 0;
+  share_next= 0;
+  lt= 0;
+  lt_old= 0;
+  list_next= 0;
+  list_prev= 0;
+  share= 0;
+}
+
+void
+ndb_index_stat_error(Ndb_index_stat *st, const char* place, int line)
+{
+  pthread_mutex_lock(&ndb_index_stat_stat_mutex);
+  time_t now= ndb_index_stat_time();
+  NdbIndexStat::Error error= st->is->getNdbError();
+  if (error.code == 0)
+  {
+    // XXX why this if
+    NdbIndexStat::Error error2;
+    error= error2;
+    error.code= NdbIndexStat::InternalError;
+    error.status= NdbError::TemporaryError;
+  }
+  st->error= error;
+  st->error_time= now;
+  st->error_count++;
+  pthread_cond_broadcast(&ndb_index_stat_stat_cond);
+  pthread_mutex_unlock(&ndb_index_stat_stat_mutex);
+
+  DBUG_PRINT("index_stat", ("%s line %d: error %d line %d extra %d",
+                            place, line, error.code, error.line, error.extra));
+}
+
+/* Lists across shares */
+
+Ndb_index_stat_list::Ndb_index_stat_list(int the_lt, const char* the_name)
+{
+  lt= the_lt;
+  name= the_name;
+  head= 0;
+  tail= 0;
+  count= 0;
+}
+
+Ndb_index_stat_list ndb_index_stat_list[Ndb_index_stat::LT_Count] = {
+  Ndb_index_stat_list(0, 0),
+  Ndb_index_stat_list(Ndb_index_stat::LT_New,    "New"),
+  Ndb_index_stat_list(Ndb_index_stat::LT_Update, "Update"),
+  Ndb_index_stat_list(Ndb_index_stat::LT_Read,   "Read"),
+  Ndb_index_stat_list(Ndb_index_stat::LT_Idle,   "Idle"),
+  Ndb_index_stat_list(Ndb_index_stat::LT_Check,  "Check"),
+  Ndb_index_stat_list(Ndb_index_stat::LT_Delete, "Delete"),
+  Ndb_index_stat_list(Ndb_index_stat::LT_Error,  "Error")
+};
+
+void
+ndb_index_stat_list_add(Ndb_index_stat* st, int lt, int place= +1)
+{
+  Ndb_index_stat_glob &glob= ndb_index_stat_glob;
+  assert(st != 0 && st->lt == 0);
+  assert(st->list_next == 0 && st->list_prev == 0);
+  assert(1 <= lt && lt < Ndb_index_stat::LT_Count);
+  Ndb_index_stat_list &list= ndb_index_stat_list[lt];
+
+  DBUG_PRINT("index_stat", ("st %s -> %s", st->id, list.name));
+
+  if (list.count == 0)
+  {
+    assert(list.head == 0 && list.tail == 0);
+    list.head= st;
+    list.tail= st;
+  }
+  else if (place < 0)
+  {
+    assert(list.head != 0 && list.head->list_prev == 0);
+    st->list_next= list.head;
+    list.head->list_prev= st;
+    list.head= st;
+  }
+  else
+  {
+    assert(list.tail != 0 && list.tail->list_next == 0);
+    st->list_prev= list.tail;
+    list.tail->list_next= st;
+    list.tail= st;
+  }
+  list.count++;
+  glob.lock();
+  glob.total_count++;
+  glob.unlock();
+
+  st->lt= lt;
+}
+
+void
+ndb_index_stat_list_remove(Ndb_index_stat* st)
+{
+  Ndb_index_stat_glob &glob= ndb_index_stat_glob;
+  assert(st != 0);
+  int lt= st->lt;
+  assert(1 <= lt && lt < Ndb_index_stat::LT_Count);
+  Ndb_index_stat_list &list= ndb_index_stat_list[lt];
+
+  DBUG_PRINT("index_stat", ("st %s <- %s", st->id, list.name));
+
+  Ndb_index_stat* next= st->list_next;
+  Ndb_index_stat* prev= st->list_prev;
+
+  if (list.head == st)
+    list.head= next;
+  if (list.tail == st)
+    list.tail= prev;
+  assert(list.count != 0);
+  list.count--;
+  glob.lock();
+  assert(glob.total_count != 0);
+  glob.total_count--;
+  glob.unlock();
+
+  if (next != 0)
+    next->list_prev= prev;
+  if (prev != 0)
+    prev->list_next= next;
+
+  st->lt= 0;
+  st->lt_old= 0;
+  st->list_next= 0;
+  st->list_prev= 0;
+}
+
+void
+ndb_index_stat_list_move(Ndb_index_stat *st, int lt, int place= +1)
+{
+  assert(st != 0);
+  ndb_index_stat_list_remove(st);
+  ndb_index_stat_list_add(st, lt, place);
+}
+
+/* Move entry in / out error list */
+
+void
+ndb_index_stat_list_to_error(Ndb_index_stat *st)
+{
+  Ndb_index_stat_glob &glob= ndb_index_stat_glob;
+
+  assert(st != 0);
+  const int lt= st->lt;
+  assert(1 <= lt && lt < Ndb_index_stat::LT_Count);
+  assert(lt != Ndb_index_stat::LT_Error);
+
+  if (st->force_update != 0)
+  {
+    glob.lock();
+    assert(glob.force_update >= st->force_update);
+    glob.force_update-= st->force_update;
+    glob.unlock();
+    st->force_update= 0;
+  }
+
+  time_t now= ndb_index_stat_time();
+  st->error_time= now;
+  ndb_index_stat_list_move(st, Ndb_index_stat::LT_Error);
+}
+
+void
+ndb_index_stat_list_from_error(Ndb_index_stat *st)
+{
+  assert(st != 0);
+  assert(st->lt == Ndb_index_stat::LT_Error);
+  if (st->force_update)
+    ndb_index_stat_list_move(st, Ndb_index_stat::LT_Update);
+  else
+    ndb_index_stat_list_move(st, Ndb_index_stat::LT_Read);
+  st->error.code= 0;
+  st->error.status= NdbError::Success;
+}
+
+/* Find or add entry under the share */
+
+Ndb_index_stat*
+ndb_index_stat_alloc()
+{
+  Ndb_index_stat *st= new Ndb_index_stat;
+  NdbIndexStat *is= new NdbIndexStat;
+  if (st != 0 && is != 0)
+  {
+    st->is= is;
+    return st;
+  }
+  delete is;
+  delete st;
+  return 0;
+}
+
+/* Subroutine, have lock */
+Ndb_index_stat*
+ndb_index_stat_find_share(NDB_SHARE *share,
+                          const NDBINDEX *index,
+                          Ndb_index_stat *&st_last)
+{
+  struct Ndb_index_stat *st= share->index_stat_list;
+  st_last= 0;
+  while (st != 0)
+  {
+    assert(st->share == share);
+    assert(st->is != 0);
+    NdbIndexStat::Head head;
+    st->is->get_head(head);
+    if (head.m_indexId == (uint)index->getObjectId() &&
+        head.m_indexVersion == (uint)index->getObjectVersion())
+      break;
+    st_last= st;
+    st= st->share_next;
+  }
+  return st;
+}
+
+/* Subroutine, have lock */
+Ndb_index_stat*
+ndb_index_stat_add_share(NDB_SHARE *share,
+                         const NDBINDEX *index,
+                         const NDBTAB *table,
+                         Ndb_index_stat *st_last)
+{
+  struct Ndb_index_stat *st= ndb_index_stat_alloc();
+  if (st != 0)
+  {
+    st->share= share;
+    if (st_last == 0)
+      share->index_stat_list= st;
+    else
+      st_last->share_next= st;
+    st->index_id= index->getObjectId();
+    st->index_version= index->getObjectVersion();
+#ifndef DBUG_OFF
+    my_snprintf(st->id, sizeof(st->id), "%d.%d", st->index_id, st->index_version);
+#endif
+    if (st->is->set_index(*index, *table) == -1)
+    {
+      ndb_index_stat_error(st, "set_index", __LINE__);
+      /* Caller assigns list */
+    }
+  }
+  return st;
+}
+
+Ndb_index_stat*
+ndb_index_stat_get_share(NDB_SHARE *share,
+                         const NDBINDEX *index,
+                         const NDBTAB *table,
+                         bool allow_add,
+                         bool force_update)
+{
+  pthread_mutex_lock(&share->mutex);
+  pthread_mutex_lock(&ndb_index_stat_list_mutex);
+  time_t now= ndb_index_stat_time();
+
+  struct Ndb_index_stat *st= 0;
+  struct Ndb_index_stat *st_last= 0;
+  if (ndb_index_stat_allow())
+  {
+    st= ndb_index_stat_find_share(share, index, st_last);
+    if (st == 0 && allow_add)
+    {
+      st= ndb_index_stat_add_share(share, index, table, st_last);
+      if (st != 0)
+        ndb_index_stat_list_add(st, Ndb_index_stat::LT_New);
+    }
+    if (st != 0)
+    {
+      if (force_update != 0)
+      {
+        st->force_update++;
+        Ndb_index_stat_glob &glob= ndb_index_stat_glob;
+        glob.lock();
+        glob.force_update++;
+        glob.unlock();
+      }
+      st->access_time= now;
+    }
+  }
+
+  pthread_mutex_unlock(&ndb_index_stat_list_mutex);
+  pthread_mutex_unlock(&share->mutex);
+  return st;
+}
+
+void
+ndb_index_stat_free(Ndb_index_stat *st)
+{
+  pthread_mutex_lock(&ndb_index_stat_list_mutex);
+  NDB_SHARE *share= st->share;
+  assert(share != 0);
+
+  Ndb_index_stat *st_head= 0;
+  Ndb_index_stat *st_tail= 0;
+  Ndb_index_stat *st_loop= share->index_stat_list;
+  bool found= false;
+  while (st_loop != 0) {
+    if (st == st_loop) {
+      st->share= 0;
+      assert(st->lt != 0);
+      assert(st->lt != Ndb_index_stat::LT_Delete);
+      ndb_index_stat_list_move(st, Ndb_index_stat::LT_Delete);
+      st_loop= st_loop->share_next;
+      assert(!found);
+      found++;
+    } else {
+      if (st_head == 0)
+        st_head= st_loop;
+      else
+        st_tail->share_next= st_loop;
+      st_tail= st_loop;
+      st_loop= st_loop->share_next;
+      st_tail->share_next= 0;
+    }
+  }
+  assert(found);
+  share->index_stat_list= st_head;
+  pthread_mutex_unlock(&ndb_index_stat_list_mutex);
+}
+
+void
+ndb_index_stat_free(NDB_SHARE *share)
+{
+  pthread_mutex_lock(&ndb_index_stat_list_mutex);
+  Ndb_index_stat *st;
+  while ((st= share->index_stat_list) != 0)
+  {
+    share->index_stat_list= st->share_next;
+    st->share= 0;
+    assert(st->lt != 0);
+    assert(st->lt != Ndb_index_stat::LT_Delete);
+    ndb_index_stat_list_move(st, Ndb_index_stat::LT_Delete);
+  }
+  pthread_mutex_unlock(&ndb_index_stat_list_mutex);
+}
+
+/* Statistics thread sub-routines */
+
+void
+ndb_index_stat_cache_move(Ndb_index_stat *st)
+{
+  Ndb_index_stat_glob &glob= ndb_index_stat_glob;
+  NdbIndexStat::CacheInfo infoBuild;
+  NdbIndexStat::CacheInfo infoQuery;
+
+  st->is->get_cache_info(infoBuild, NdbIndexStat::CacheBuild);
+  st->is->get_cache_info(infoQuery, NdbIndexStat::CacheQuery);
+  const uint new_query_bytes= infoBuild.m_totalBytes;
+  const uint old_query_bytes= infoQuery.m_totalBytes;
+  DBUG_PRINT("index_stat", ("st %s cache move: query:%u clean:%u",
+                            st->id, new_query_bytes, old_query_bytes));
+  st->is->move_cache();
+  glob.lock();
+  assert(glob.cache_query_bytes >= old_query_bytes);
+  glob.cache_query_bytes-= old_query_bytes;
+  glob.cache_query_bytes+= new_query_bytes;
+  glob.cache_clean_bytes+= old_query_bytes;
+  glob.unlock();
+}
+
+void
+ndb_index_stat_cache_clean(Ndb_index_stat *st)
+{
+  Ndb_index_stat_glob &glob= ndb_index_stat_glob;
+  NdbIndexStat::CacheInfo infoClean;
+
+  st->is->get_cache_info(infoClean, NdbIndexStat::CacheClean);
+  const uint old_clean_bytes= infoClean.m_totalBytes;
+  DBUG_PRINT("index_stat", ("st %s cache clean: clean:%u",
+                            st->id, old_clean_bytes));
+  st->is->clean_cache();
+  glob.lock();
+  assert(glob.cache_clean_bytes >= old_clean_bytes);
+  glob.cache_clean_bytes-= old_clean_bytes;
+  glob.unlock();
+}
+
+/* Misc in/out parameters for process steps */
+struct Ndb_index_stat_proc {
+  Ndb *ndb;
+  time_t now;
+  int lt;
+  bool busy;
+  bool end;
+  Ndb_index_stat_proc() :
+    ndb(0),
+    now(0),
+    lt(0),
+    busy(false),
+    end(false)
+  {}
+};
+
+void
+ndb_index_stat_proc_new(Ndb_index_stat_proc &pr, Ndb_index_stat *st)
+{
+  if (st->error.code != 0)
+    pr.lt= Ndb_index_stat::LT_Error;
+  else if (st->force_update)
+    pr.lt= Ndb_index_stat::LT_Update;
+  else
+    pr.lt= Ndb_index_stat::LT_Read;
+}
+
+void
+ndb_index_stat_proc_new(Ndb_index_stat_proc &pr)
+{
+  pthread_mutex_lock(&ndb_index_stat_list_mutex);
+  const int lt= Ndb_index_stat::LT_New;
+  Ndb_index_stat_list &list= ndb_index_stat_list[lt];
+
+  Ndb_index_stat *st_loop= list.head;
+  while (st_loop != 0)
+  {
+    Ndb_index_stat *st= st_loop;
+    st_loop= st_loop->list_next;
+    DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name));
+    ndb_index_stat_proc_new(pr, st);
+    ndb_index_stat_list_move(st, pr.lt);
+  }
+  pthread_mutex_unlock(&ndb_index_stat_list_mutex);
+}
+
+void
+ndb_index_stat_proc_update(Ndb_index_stat_proc &pr, Ndb_index_stat *st)
+{
+  if (st->is->update_stat(pr.ndb) == -1)
+  {
+    ndb_index_stat_error(st, "update_stat", __LINE__);
+    pr.lt= Ndb_index_stat::LT_Error;
+    return;
+  }
+  pr.lt= Ndb_index_stat::LT_Read;
+}
+
+void
+ndb_index_stat_proc_update(Ndb_index_stat_proc &pr)
+{
+  const int lt= Ndb_index_stat::LT_Update;
+  Ndb_index_stat_list &list= ndb_index_stat_list[lt];
+  const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
+  const uint batch= opt.get(Ndb_index_stat_opt::Iupdate_batch);
+
+  Ndb_index_stat *st_loop= list.head;
+  uint cnt= 0;
+  while (st_loop != 0 && cnt < batch)
+  {
+    Ndb_index_stat *st= st_loop;
+    st_loop= st_loop->list_next;
+    DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name));
+    ndb_index_stat_proc_update(pr, st);
+    ndb_index_stat_list_move(st, pr.lt);
+    cnt++;
+  }
+  if (cnt == batch)
+    pr.busy= true;
+}
+
+void
+ndb_index_stat_proc_read(Ndb_index_stat_proc &pr, Ndb_index_stat *st)
+{
+  NdbIndexStat::Head head;
+  if (st->is->read_stat(pr.ndb) == -1)
+  {
+    ndb_index_stat_error(st, "read_stat", __LINE__);
+    pr.lt= Ndb_index_stat::LT_Error;
+    return;
+  }
+
+  pthread_mutex_lock(&ndb_index_stat_stat_mutex);
+  pr.now= ndb_index_stat_time();
+  st->is->get_head(head);
+  st->load_time= head.m_loadTime;
+  st->read_time= pr.now;
+  st->sample_version= head.m_sampleVersion;
+
+  if (st->force_update != 0)
+  {
+    Ndb_index_stat_glob &glob= ndb_index_stat_glob;
+    glob.lock();
+    assert(glob.force_update >= st->force_update);
+    glob.force_update-= st->force_update;
+    glob.unlock();
+    st->force_update= 0;
+  }
+
+  ndb_index_stat_cache_move(st);
+  st->cache_clean= false;
+  pr.lt= Ndb_index_stat::LT_Idle;
+  pthread_cond_broadcast(&ndb_index_stat_stat_cond);
+  pthread_mutex_unlock(&ndb_index_stat_stat_mutex);
+}
+
+void
+ndb_index_stat_proc_read(Ndb_index_stat_proc &pr)
+{
+  const int lt= Ndb_index_stat::LT_Read;
+  Ndb_index_stat_list &list= ndb_index_stat_list[lt];
+  const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
+  const uint batch= opt.get(Ndb_index_stat_opt::Iread_batch);
+
+  Ndb_index_stat *st_loop= list.head;
+  uint cnt= 0;
+  while (st_loop != 0 && cnt < batch)
+  {
+    Ndb_index_stat *st= st_loop;
+    st_loop= st_loop->list_next;
+    DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name));
+    ndb_index_stat_proc_read(pr, st);
+    ndb_index_stat_list_move(st, pr.lt);
+    cnt++;
+  }
+  if (cnt == batch)
+    pr.busy= true;
+}
+
+void
+ndb_index_stat_proc_idle(Ndb_index_stat_proc &pr, Ndb_index_stat *st)
+{
+  const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
+  const int clean_delay= opt.get(Ndb_index_stat_opt::Iclean_delay);
+  const int check_delay= opt.get(Ndb_index_stat_opt::Icheck_delay);
+  const time_t clean_wait=
+    st->cache_clean ? 0 : st->read_time + clean_delay - pr.now;
+  const time_t check_wait=
+    st->check_time == 0 ? 0 : st->check_time + check_delay - pr.now;
+
+  DBUG_PRINT("index_stat", ("st %s check wait:%ds force update:%u"
+                            " clean wait:%ds cache clean:%d",
+                            st->id, check_wait, st->force_update,
+                            clean_wait, st->cache_clean));
+
+  if (!st->cache_clean && clean_wait <= 0)
+  {
+    ndb_index_stat_cache_clean(st);
+    st->cache_clean= true;
+  }
+  if (st->force_update)
+  {
+    pr.lt= Ndb_index_stat::LT_Update;
+    return;
+  }
+  if (check_wait <= 0)
+  {
+    pr.lt= Ndb_index_stat::LT_Check;
+    return;
+  }
+  pr.lt= Ndb_index_stat::LT_Idle;
+}
+
+void
+ndb_index_stat_proc_idle(Ndb_index_stat_proc &pr)
+{
+  const int lt= Ndb_index_stat::LT_Idle;
+  Ndb_index_stat_list &list= ndb_index_stat_list[lt];
+  const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
+  const uint batch= opt.get(Ndb_index_stat_opt::Iidle_batch);
+  pr.now= ndb_index_stat_time();
+
+  Ndb_index_stat *st_loop= list.head;
+  uint cnt= 0;
+  while (st_loop != 0 && cnt < batch)
+  {
+    Ndb_index_stat *st= st_loop;
+    st_loop= st_loop->list_next;
+    DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name));
+    ndb_index_stat_proc_idle(pr, st);
+    if (pr.lt != lt)
+    {
+      ndb_index_stat_list_move(st, pr.lt);
+      cnt++;
+    }
+  }
+  if (cnt == batch)
+    pr.busy= true;
+}
+
+void
+ndb_index_stat_proc_check(Ndb_index_stat_proc &pr, Ndb_index_stat *st)
+{
+  pr.now= ndb_index_stat_time();
+  st->check_time= pr.now;
+  NdbIndexStat::Head head;
+  if (st->is->read_head(pr.ndb) == -1)
+  {
+    ndb_index_stat_error(st, "read_head", __LINE__);
+    pr.lt= Ndb_index_stat::LT_Error;
+    return;
+  }
+  st->is->get_head(head);
+  const uint version_old= st->sample_version;
+  const uint version_new= head.m_sampleVersion;
+  if (version_old != version_new)
+  {
+    DBUG_PRINT("index_stat", ("st %s sample version old:%u new:%u",
+                              st->id, version_old, version_new));
+    pr.lt= Ndb_index_stat::LT_Read;
+    return;
+  }
+  pr.lt= Ndb_index_stat::LT_Idle;
+}
+
+void
+ndb_index_stat_proc_check(Ndb_index_stat_proc &pr)
+{
+  const int lt= Ndb_index_stat::LT_Check;
+  Ndb_index_stat_list &list= ndb_index_stat_list[lt];
+  const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
+  const uint batch= opt.get(Ndb_index_stat_opt::Icheck_batch);
+
+  Ndb_index_stat *st_loop= list.head;
+  uint cnt= 0;
+  while (st_loop != 0 && cnt < batch)
+  {
+    Ndb_index_stat *st= st_loop;
+    st_loop= st_loop->list_next;
+    DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name));
+    ndb_index_stat_proc_check(pr, st);
+    ndb_index_stat_list_move(st, pr.lt);
+    cnt++;
+  }
+  if (cnt == batch)
+    pr.busy= true;
+}
+
+void
+ndb_index_stat_proc_evict(Ndb_index_stat_proc &pr, Ndb_index_stat *st)
+{
+  NdbIndexStat::Head head;
+  NdbIndexStat::CacheInfo infoBuild;
+  NdbIndexStat::CacheInfo infoQuery;
+  NdbIndexStat::CacheInfo infoClean;
+  st->is->get_head(head);
+  st->is->get_cache_info(infoBuild, NdbIndexStat::CacheBuild);
+  st->is->get_cache_info(infoQuery, NdbIndexStat::CacheQuery);
+  st->is->get_cache_info(infoClean, NdbIndexStat::CacheClean);
+
+  DBUG_PRINT("index_stat",
+             ("evict table: %u index: %u version: %u"
+              " sample version: %u"
+              " cache bytes build:%u query:%u clean:%u",
+              head.m_tableId, head.m_indexId, head.m_indexVersion,
+              head.m_sampleVersion,
+              infoBuild.m_totalBytes, infoQuery.m_totalBytes, infoClean.m_totalBytes));
+
+  /* Twice to move all caches to clean */
+  ndb_index_stat_cache_move(st);
+  ndb_index_stat_cache_move(st);
+  ndb_index_stat_cache_clean(st);
+}
+
+bool
+ndb_index_stat_proc_evict()
+{
+  const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
+  Ndb_index_stat_glob &glob= ndb_index_stat_glob;
+  glob.lock();
+  uint curr_size= glob.cache_query_bytes + glob.cache_clean_bytes;
+  glob.unlock();
+  const uint cache_lowpct= opt.get(Ndb_index_stat_opt::Icache_lowpct);
+  const uint cache_limit= opt.get(Ndb_index_stat_opt::Icache_limit);
+  if (100 * curr_size <= cache_lowpct * cache_limit)
+    return false;
+  return true;
+}
+
+void
+ndb_index_stat_proc_evict(Ndb_index_stat_proc &pr, int lt)
+{
+  Ndb_index_stat_list &list= ndb_index_stat_list[lt];
+  const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
+  const uint batch= opt.get(Ndb_index_stat_opt::Ievict_batch);
+  const int evict_delay= opt.get(Ndb_index_stat_opt::Ievict_delay);
+  pr.now= ndb_index_stat_time();
+
+  if (!ndb_index_stat_proc_evict())
+    return;
+
+  /* Create a LRU batch */
+  Ndb_index_stat* st_lru_arr[ndb_index_stat_max_evict_batch + 1];
+  uint st_lru_cnt= 0;
+  Ndb_index_stat *st_loop= list.head;
+  while (st_loop != 0 && st_lru_cnt < batch)
+  {
+    Ndb_index_stat *st= st_loop;
+    st_loop= st_loop->list_next;
+    if (st->read_time + evict_delay <= pr.now)
+    {
+      /* Insertion sort into the batch from the end */
+      if (st_lru_cnt == 0)
+        st_lru_arr[st_lru_cnt++]= st;
+      else
+      {
+        uint i= st_lru_cnt;
+        while (i != 0)
+        {
+          if (st_lru_arr[i-1]->access_time < st->access_time)
+            break;
+          i--;
+        }
+        if (i < st_lru_cnt)
+        {
+          uint j= st_lru_cnt; /* There is place for one more at end */
+          while (j > i)
+          {
+            st_lru_arr[j]= st_lru_arr[j-1];
+            j--;
+          }
+          st_lru_arr[i]= st;
+          if (st_lru_cnt < batch)
+            st_lru_cnt++;
+        }
+      }
+    }
+  }
+
+  /* Process the LRU batch */
+  uint cnt= 0;
+  while (cnt < st_lru_cnt)
+  {
+    if (!ndb_index_stat_proc_evict())
+      break;
+
+    Ndb_index_stat *st= st_lru_arr[cnt];
+    DBUG_PRINT("index_stat", ("st %s proc evict %s", st->id, list.name));
+    ndb_index_stat_proc_evict(pr, st);
+    ndb_index_stat_free(st);
+    cnt++;
+  }
+  if (cnt == batch)
+    pr.busy= true;
+}
+
+void
+ndb_index_stat_proc_evict(Ndb_index_stat_proc &pr)
+{
+  ndb_index_stat_proc_evict(pr, Ndb_index_stat::LT_Error);
+  ndb_index_stat_proc_evict(pr, Ndb_index_stat::LT_Idle);
+}
+
+void
+ndb_index_stat_proc_delete(Ndb_index_stat_proc &pr)
+{
+  const int lt= Ndb_index_stat::LT_Delete;
+  Ndb_index_stat_list &list= ndb_index_stat_list[lt];
+  const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
+  const uint delete_batch= opt.get(Ndb_index_stat_opt::Idelete_batch);
+  const uint batch= !pr.end ? delete_batch : 0xFFFFFFFF;
+
+  Ndb_index_stat *st_loop= list.head;
+  uint cnt= 0;
+  while (st_loop != 0 && cnt < batch)
+  {
+    Ndb_index_stat *st= st_loop;
+    st_loop= st_loop->list_next;
+    DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name));
+    ndb_index_stat_proc_evict(pr, st);
+    ndb_index_stat_list_remove(st);
+    delete st->is;
+    delete st;
+    cnt++;
+  }
+  if (cnt == batch)
+    pr.busy= true;
+}
+
+void
+ndb_index_stat_proc_error(Ndb_index_stat_proc &pr, Ndb_index_stat *st)
+{
+  const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
+  const int error_delay= opt.get(Ndb_index_stat_opt::Ierror_delay);
+  const time_t error_wait= st->error_time + error_delay - pr.now;
+
+  if (error_wait <= 0)
+  {
+    ndb_index_stat_list_from_error(st);
+    DBUG_PRINT("index_stat", ("st %s error wait:%ds error count:%u",
+                              st->id, (int)error_wait, st->error_count));
+    if (st->force_update)
+      pr.lt= Ndb_index_stat::LT_Update;
+    else
+      pr.lt= Ndb_index_stat::LT_Read;
+    return;
+  }
+  pr.lt= Ndb_index_stat::LT_Error;
+}
+
+void
+ndb_index_stat_proc_error(Ndb_index_stat_proc &pr)
+{
+  const int lt= Ndb_index_stat::LT_Error;
+  Ndb_index_stat_list &list= ndb_index_stat_list[lt];
+  const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
+  const uint batch= opt.get(Ndb_index_stat_opt::Ierror_batch);
+  pr.now= ndb_index_stat_time();
+
+  Ndb_index_stat *st_loop= list.head;
+  uint cnt= 0;
+  while (st_loop != 0 && cnt < batch)
+  {
+    Ndb_index_stat *st= st_loop;
+    st_loop= st_loop->list_next;
+    DBUG_PRINT("index_stat", ("st %s proc %s", st->id, list.name));
+    ndb_index_stat_proc_error(pr, st);
+    if (pr.lt != lt)
+    {
+      ndb_index_stat_list_move(st, pr.lt);
+      cnt++;
+    }
+  }
+  if (cnt == batch)
+    pr.busy= true;
+}
+
+#ifndef DBUG_OFF
+void
+ndb_index_stat_report(const Ndb_index_stat_glob& old_glob)
+{
+  Ndb_index_stat_glob new_glob= ndb_index_stat_glob;
+  new_glob.set_list_count();
+
+  /* List counts */
+  {
+    const uint (&old_count)[Ndb_index_stat::LT_Count]= old_glob.list_count;
+    const uint (&new_count)[Ndb_index_stat::LT_Count]= new_glob.list_count;
+    bool any= false;
+    int lt;
+    for (lt=1; lt < Ndb_index_stat::LT_Count; lt++)
+    {
+      const Ndb_index_stat_list &list= ndb_index_stat_list[lt];
+      const char* name= list.name;
+      if (old_count[lt] != new_count[lt])
+      {
+        DBUG_PRINT("index_stat", ("%s: %u -> %u",
+                                  name, old_count[lt], new_count[lt]));
+        any= true;
+      }
+    }
+    if (any)
+    {
+      const uint bufsz= 20 * Ndb_index_stat::LT_Count;
+      char buf[bufsz];
+      char *ptr= buf;
+      for (lt= 1; lt < Ndb_index_stat::LT_Count; lt++)
+      {
+        const Ndb_index_stat_list &list= ndb_index_stat_list[lt];
+        const char* name= list.name;
+        sprintf(ptr, " %s:%u", name, new_count[lt]);
+        ptr+= strlen(ptr);
+      }
+      DBUG_PRINT("index_stat", ("list:%s", buf));
+    }
+  }
+
+  /* Cache summary */
+  {
+    const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
+    uint query_size= new_glob.cache_query_bytes;
+    uint clean_size= new_glob.cache_clean_bytes;
+    uint total_size= query_size + clean_size;
+    const uint limit= opt.get(Ndb_index_stat_opt::Icache_limit);
+    double pct= 100.0;
+    if (limit != 0)
+      pct= 100.0 * (double)total_size / (double)limit;
+    DBUG_PRINT("index_stat", ("cache query:%u clean:%u (%.2f pct)",
+                              query_size, clean_size, pct));
+  }
+
+  /* Updates waited for and forced updates */
+  {
+    pthread_mutex_lock(&ndb_index_stat_list_mutex);
+    uint wait_update= new_glob.wait_update;
+    uint force_update= new_glob.force_update;
+    pthread_mutex_unlock(&ndb_index_stat_list_mutex);
+    if (wait_update != 0 || force_update != 0)
+    {
+      DBUG_PRINT("index_stat", ("wait update:%u force update:%u",
+                                wait_update, force_update));
+    }
+  }
+}
+#endif
+
+void
+ndb_index_stat_proc(Ndb_index_stat_proc &pr)
+{
+#ifndef DBUG_OFF
+  Ndb_index_stat_glob old_glob= ndb_index_stat_glob;
+  old_glob.set_list_count();
+#endif
+
+  DBUG_ENTER("ndb_index_stat_proc");
+
+  ndb_index_stat_proc_new(pr);
+  ndb_index_stat_proc_update(pr);
+  ndb_index_stat_proc_read(pr);
+  ndb_index_stat_proc_idle(pr);
+  ndb_index_stat_proc_check(pr);
+  ndb_index_stat_proc_evict(pr);
+  ndb_index_stat_proc_delete(pr);
+  ndb_index_stat_proc_error(pr);
+
+#ifndef DBUG_OFF
+  ndb_index_stat_report(old_glob);
+#endif
+  DBUG_VOID_RETURN;
+}
+
+void
+ndb_index_stat_end()
+{
+  DBUG_ENTER("ndb_index_stat_end");
+  Ndb_index_stat_proc pr;
+  pr.end= true;
+
+  /*
+   * Shares have been freed so any index stat entries left should be
+   * in LT_Delete.  The first two steps here should be unnecessary.
+   */
+
+  ndb_index_stat_allow(0);
+
+  int lt;
+  for (lt= 1; lt < Ndb_index_stat::LT_Count; lt++)
+  {
+    if (lt == (int)Ndb_index_stat::LT_Delete)
+      continue;
+    Ndb_index_stat_list &list= ndb_index_stat_list[lt];
+    Ndb_index_stat *st_loop= list.head;
+    while (st_loop != 0)
+    {
+      Ndb_index_stat *st= st_loop;
+      st_loop= st_loop->list_next;
+      DBUG_PRINT("index_stat", ("st %s end %s", st->id, list.name));
+      pr.lt= Ndb_index_stat::LT_Delete;
+      ndb_index_stat_list_move(st, pr.lt);
+    }
+  }
+
+  /* Real free */
+  ndb_index_stat_proc_delete(pr);
+  DBUG_VOID_RETURN;
+}
+
+/* Index stats thread */
+
+static int
+ndb_index_stat_check_or_create_systables(NdbIndexStat* is, Ndb* ndb)
+{
+  DBUG_ENTER("ndb_index_stat_check_or_create_systables");
+
+  if (is->check_systables(ndb) == 0)
+  {
+    DBUG_PRINT("index_stat", ("using existing index stats tables"));
+    DBUG_RETURN(0);
+  }
+
+  if (is->create_systables(ndb) == 0)
+  {
+    DBUG_PRINT("index_stat", ("created index stats tables"));
+    DBUG_RETURN(0);
+  }
+
+  if (is->getNdbError().code == 721)
+  {
+    // race between mysqlds, maybe
+    DBUG_PRINT("index_stat", ("create index stats tables failed: error %d line %d",
+                              is->getNdbError().code, is->getNdbError().line));
+    DBUG_RETURN(-1);
+  }
+
+  sql_print_warning("create index stats tables failed: error %d line %d",
+                    is->getNdbError().code, is->getNdbError().line);
+  DBUG_RETURN(-1);
+}
+
+pthread_handler_t
+ndb_index_stat_thread_func(void *arg __attribute__((unused)))
+{
+  THD *thd; /* needs to be first for thread_stack */
+  struct timespec abstime;
+  Thd_ndb *thd_ndb= NULL;
+
+  my_thread_init();
+  DBUG_ENTER("ndb_index_stat_thread_func");
+
+  // wl4124_todo remove useless stuff copied from utility thread
+ 
+  pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
+
+  thd= new THD; /* note that contructor of THD uses DBUG_ */
+  if (thd == NULL)
+  {
+    my_errno= HA_ERR_OUT_OF_MEM;
+    DBUG_RETURN(NULL);
+  }
+  THD_CHECK_SENTRY(thd);
+  pthread_detach_this_thread();
+  ndb_index_stat_thread= pthread_self();
+
+  thd->thread_stack= (char*)&thd; /* remember where our stack is */
+  if (thd->store_globals())
+    goto ndb_index_stat_thread_fail;
+  lex_start(thd);
+  thd->init_for_queries();
+#ifndef NDB_THD_HAS_NO_VERSION
+  thd->version=refresh_version;
+#endif
+  thd->client_capabilities = 0;
+  thd->security_ctx->skip_grants();
+  my_net_init(&thd->net, 0);
+
+  CHARSET_INFO *charset_connection;
+  charset_connection= get_charset_by_csname("utf8",
+                                            MY_CS_PRIMARY, MYF(MY_WME));
+  thd->variables.character_set_client= charset_connection;
+  thd->variables.character_set_results= charset_connection;
+  thd->variables.collation_connection= charset_connection;
+  thd->update_charset();
+
+  /* Signal successful initialization */
+  ndb_index_stat_thread_running= 1;
+  pthread_cond_signal(&COND_ndb_index_stat_ready);
+  pthread_mutex_unlock(&LOCK_ndb_index_stat_thread);
+
+  /*
+    wait for mysql server to start
+  */
+  pthread_mutex_lock(&LOCK_server_started);
+  while (!mysqld_server_started)
+  {
+    set_timespec(abstime, 1);
+    pthread_cond_timedwait(&COND_server_started, &LOCK_server_started,
+	                       &abstime);
+    if (ndbcluster_terminating)
+    {
+      pthread_mutex_unlock(&LOCK_server_started);
+      pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
+      goto ndb_index_stat_thread_end;
+    }
+  }
+  pthread_mutex_unlock(&LOCK_server_started);
+
+  /*
+    Wait for cluster to start
+  */
+  pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
+  while (!g_ndb_status.cluster_node_id && (ndbcluster_hton->slot != ~(uint)0))
+  {
+    /* ndb not connected yet */
+    pthread_cond_wait(&COND_ndb_index_stat_thread, &LOCK_ndb_index_stat_thread);
+    if (ndbcluster_terminating)
+      goto ndb_index_stat_thread_end;
+  }
+  pthread_mutex_unlock(&LOCK_ndb_index_stat_thread);
+
+  /* Get thd_ndb for this thread */
+  if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb()))
+  {
+    sql_print_error("Could not allocate Thd_ndb object");
+    pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
+    goto ndb_index_stat_thread_end;
+  }
+  set_thd_ndb(thd, thd_ndb);
+  thd_ndb->options|= TNO_NO_LOG_SCHEMA_OP;
+  if (thd_ndb->ndb->setDatabaseName(NDB_INDEX_STAT_DB) == -1)
+  {
+    sql_print_error("Could not change index stats thd_ndb database to %s",
+                    NDB_INDEX_STAT_DB);
+    pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
+    goto ndb_index_stat_thread_end;
+  }
+
+  ndb_index_stat_allow(1);
+  bool enable_ok;
+  enable_ok= false;
+
+  set_timespec(abstime, 0);
+  for (;;)
+  {
+    pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
+    if (!ndbcluster_terminating) {
+      int ret= pthread_cond_timedwait(&COND_ndb_index_stat_thread,
+                                      &LOCK_ndb_index_stat_thread,
+                                      &abstime);
+      const char* reason= ret == ETIMEDOUT ? "timed out" : "wake up";
+      (void)reason; // USED
+      DBUG_PRINT("index_stat", ("loop: %s", reason));
+    }
+    if (ndbcluster_terminating) /* Shutting down server */
+      goto ndb_index_stat_thread_end;
+    pthread_mutex_unlock(&LOCK_ndb_index_stat_thread);
+
+    pthread_mutex_lock(&LOCK_global_system_variables);
+    /* const bool enable_ok_new= THDVAR(NULL, index_stat_enable); */
+    const bool enable_ok_new= ndb_index_stat_get_enable(NULL);
+    pthread_mutex_unlock(&LOCK_global_system_variables);
+
+    Ndb_index_stat_proc pr;
+    pr.ndb= thd_ndb->ndb;
+
+    do
+    {
+      if (enable_ok != enable_ok_new)
+      {
+        DBUG_PRINT("index_stat", ("global enable: %d -> %d",
+                                  enable_ok, enable_ok_new));
+
+        if (enable_ok_new)
+        {
+          // at enable check or create stats tables
+          NdbIndexStat is;
+          if (ndb_index_stat_check_or_create_systables(&is, thd_ndb->ndb) == -1)
+          {
+            // try again in next loop
+            break;
+          }
+        }
+        enable_ok= enable_ok_new;
+      }
+
+      if (!enable_ok)
+        break;
+
+      pr.busy= false;
+      ndb_index_stat_proc(pr);
+    } while (0);
+
+    /* Calculate new time to wake up */
+
+    const Ndb_index_stat_opt &opt= ndb_index_stat_opt;
+    uint msecs= 0;
+    if (!enable_ok)
+      msecs= opt.get(Ndb_index_stat_opt::Iloop_checkon);
+    else if (!pr.busy)
+      msecs= opt.get(Ndb_index_stat_opt::Iloop_idle);
+    else
+      msecs= opt.get(Ndb_index_stat_opt::Iloop_busy);
+    DBUG_PRINT("index_stat", ("sleep %dms", msecs));
+
+    set_timespec_nsec(abstime, msecs * 1000000ULL);
+  }
+
+ndb_index_stat_thread_end:
+  net_end(&thd->net);
+
+ndb_index_stat_thread_fail:
+  if (thd_ndb)
+  {
+    ha_ndbcluster::release_thd_ndb(thd_ndb);
+    set_thd_ndb(thd, NULL);
+  }
+  thd->cleanup();
+  delete thd;
+  
+  /* signal termination */
+  ndb_index_stat_thread_running= 0;
+  pthread_cond_signal(&COND_ndb_index_stat_ready);
+  pthread_mutex_unlock(&LOCK_ndb_index_stat_thread);
+  DBUG_PRINT("exit", ("ndb_index_stat_thread"));
+
+  DBUG_LEAVE;
+  my_thread_end();
+  pthread_exit(0);
+  return NULL;
+}
+
+/* Optimizer queries */
+
+static ulonglong
+ndb_index_stat_round(double x)
+{
+  char buf[100];
+  if (x < 0.0)
+    x= 0.0;
+  // my_snprintf has no float and windows has no snprintf
+  sprintf(buf, "%.0f", x);
+  /* mysql provides strtoull */
+  ulonglong n= strtoull(buf, 0, 10);
+  return n;
+}
+
+int
+ha_ndbcluster::ndb_index_stat_wait(Ndb_index_stat *st,
+                                   uint sample_version)
+{
+  DBUG_ENTER("ha_ndbcluster::ndb_index_stat_wait");
+
+  pthread_mutex_lock(&ndb_index_stat_stat_mutex);
+  int err= 0;
+  uint count= 0;
+  (void)count; // USED
+  struct timespec abstime;
+  while (true) {
+    int ret= 0;
+    if (st->error.code != 0 &&
+        (st->error.code != NdbIndexStat::NoIndexStats ||
+         st->force_update == 0))
+    {
+      err= st->error.code;
+      break;
+    }
+    if (st->sample_version > sample_version)
+      break;
+    DBUG_PRINT("index_stat", ("st %s wait count:%u",
+                              st->id, ++count));
+    pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
+    pthread_cond_signal(&COND_ndb_index_stat_thread);
+    pthread_mutex_unlock(&LOCK_ndb_index_stat_thread);
+    set_timespec(abstime, 1);
+    ret= pthread_cond_timedwait(&ndb_index_stat_stat_cond,
+                                &ndb_index_stat_stat_mutex,
+                                &abstime);
+    if (ret != 0 && ret != ETIMEDOUT)
+    {
+      err= ret;
+      break;
+    }
+  }
+  pthread_mutex_unlock(&ndb_index_stat_stat_mutex);
+  if (err != 0) {
+    DBUG_PRINT("index_stat", ("st %s wait error: %d",
+                               st->id, err));
+    DBUG_RETURN(err);
+  }
+  DBUG_PRINT("index_stat", ("st %s wait ok: sample_version %u -> %u",
+                            st->id, sample_version, st->sample_version));
+  DBUG_RETURN(0);
+}
+
+int
+ha_ndbcluster::ndb_index_stat_query(uint inx,
+                                    const key_range *min_key,
+                                    const key_range *max_key,
+                                    NdbIndexStat::Stat& stat)
+{
+  DBUG_ENTER("ha_ndbcluster::ndb_index_stat_query");
+
+  const KEY *key_info= table->key_info + inx;
+  const NDB_INDEX_DATA &data= m_index[inx];
+  const NDBINDEX *index= data.index;
+  DBUG_PRINT("index_stat", ("index: %s", index->getName()));
+
+  int err= 0;
+
+  /* Create an IndexBound struct for the keys */
+  NdbIndexScanOperation::IndexBound ib;
+  compute_index_bounds(ib, key_info, min_key, max_key);
+  ib.range_no= 0;
+
+  Ndb_index_stat *st=
+    ndb_index_stat_get_share(m_share, index, m_table, true, false);
+  if (st == 0)
+  {
+    DBUG_PRINT("index_stat", ("failed to add index stat share"));
+    DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+  }
+
+  err= ndb_index_stat_wait(st, 0);
+  if (err != 0)
+    DBUG_RETURN(err);
+
+  if (st->read_time == 0)
+  {
+    DBUG_PRINT("index_stat", ("no index stats"));
+    pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
+    pthread_cond_signal(&COND_ndb_index_stat_thread);
+    pthread_mutex_unlock(&LOCK_ndb_index_stat_thread);
+    DBUG_RETURN(NdbIndexStat::NoIndexStats);
+  }
+
+  uint8 bound_lo_buffer[NdbIndexStat::BoundBufferBytes];
+  uint8 bound_hi_buffer[NdbIndexStat::BoundBufferBytes];
+  NdbIndexStat::Bound bound_lo(st->is, bound_lo_buffer);
+  NdbIndexStat::Bound bound_hi(st->is, bound_hi_buffer);
+  NdbIndexStat::Range range(bound_lo, bound_hi);
+
+  const NdbRecord* key_record= data.ndb_record_key;
+  if (st->is->convert_range(range, key_record, &ib) == -1)
+  {
+    ndb_index_stat_error(st, "convert_range", __LINE__);
+    DBUG_RETURN(st->error.code);
+  }
+  if (st->is->query_stat(range, stat) == -1)
+  {
+    /* Invalid cache - should remove the entry */
+    ndb_index_stat_error(st, "query_stat", __LINE__);
+    DBUG_RETURN(st->error.code);
+  }
+
+  DBUG_RETURN(0);
+}
+
+int
+ha_ndbcluster::ndb_index_stat_get_rir(uint inx,
+                                      key_range *min_key,
+                                      key_range *max_key,
+                                      ha_rows *rows_out)
+{
+  DBUG_ENTER("ha_ndbcluster::ndb_index_stat_get_rir");
+  uint8 stat_buffer[NdbIndexStat::StatBufferBytes];
+  NdbIndexStat::Stat stat(stat_buffer);
+  int err= ndb_index_stat_query(inx, min_key, max_key, stat);
+  if (err == 0)
+  {
+    double rir= -1.0;
+    NdbIndexStat::get_rir(stat, &rir);
+    DBUG_PRINT("index_stat", ("stat rir: %.2f", rir));
+    ha_rows rows= ndb_index_stat_round(rir);
+    /* Estimate only so cannot return exact zero */
+    if (rows == 0)
+      rows= 1;
+    *rows_out= rows;
+    DBUG_PRINT("index_stat", ("rir: %u", (uint)rows));
+    DBUG_RETURN(0);
+  }
+  DBUG_RETURN(err);
+}
+
+int
+ha_ndbcluster::ndb_index_stat_set_rpk(uint inx)
+{
+  DBUG_ENTER("ha_ndbcluster::ndb_index_stat_set_rpk");
+
+  KEY *key_info= table->key_info + inx;
+  int err= 0;
+
+  uint8 stat_buffer[NdbIndexStat::StatBufferBytes];
+  NdbIndexStat::Stat stat(stat_buffer);
+  const key_range *min_key= 0;
+  const key_range *max_key= 0;
+  err= ndb_index_stat_query(inx, min_key, max_key, stat);
+  if (err == 0)
+  {
+    uint k;
+    for (k= 0; k < key_info->key_parts; k++)
+    {
+      double rpk= -1.0;
+      NdbIndexStat::get_rpk(stat, k, &rpk);
+      ulonglong recs= ndb_index_stat_round(rpk);
+      key_info->rec_per_key[k]= (ulong)recs;
+      DBUG_PRINT("index_stat", ("rpk[%u]: %u", k, (uint)recs));
+    }
+    DBUG_RETURN(0);
+  }
+  DBUG_RETURN(err);
+}
+
+int
+ha_ndbcluster::ndb_index_stat_analyze(Ndb *ndb,
+                                      uint *inx_list,
+                                      uint inx_count)
+{
+  DBUG_ENTER("ha_ndbcluster::ndb_index_stat_analyze");
+
+  struct {
+    uint sample_version;
+    uint error_count;
+  } old[MAX_INDEXES];
+
+  int err= 0;
+  uint i;
+
+  /* Force stats update on each index */
+  for (i= 0; i < inx_count; i++)
+  {
+    uint inx= inx_list[i];
+    const NDB_INDEX_DATA &data= m_index[inx];
+    const NDBINDEX *index= data.index;
+    DBUG_PRINT("index_stat", ("force update: %s", index->getName()));
+
+    Ndb_index_stat *st=
+      ndb_index_stat_get_share(m_share, index, m_table, true, true);
+
+    if (st == 0)
+      DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+
+    old[i].sample_version= st->sample_version;
+    old[i].error_count= st->error_count;
+  }
+
+  /* Wait for each update (or error) */
+  for (i = 0; i < inx_count; i++)
+  {
+    uint inx= inx_list[i];
+    const NDB_INDEX_DATA &data= m_index[inx];
+    const NDBINDEX *index= data.index;
+    DBUG_PRINT("index_stat", ("wait for update: %s", index->getName()));
+
+    Ndb_index_stat *st=
+      ndb_index_stat_get_share(m_share, index, m_table, false, false);
+    if (st == 0)
+      DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);
+
+    err= ndb_index_stat_wait(st, old[i].sample_version);
+    if (err != 0)
+      DBUG_RETURN(err);
+  }
+
+  DBUG_RETURN(0);
+}
+
+#endif

=== added file 'sql/ha_ndb_index_stat.h'
--- a/sql/ha_ndb_index_stat.h	1970-01-01 00:00:00 +0000
+++ b/sql/ha_ndb_index_stat.h	2011-06-15 10:37:56 +0000
@@ -0,0 +1,38 @@
+/*
+   Copyright (c) 2011, 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
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+*/
+
+/* provides declarations only to index_stat.cc */
+
+extern struct st_ndb_status g_ndb_status;
+
+extern pthread_t ndb_index_stat_thread;
+extern pthread_cond_t COND_ndb_index_stat_thread;
+extern pthread_mutex_t LOCK_ndb_index_stat_thread;
+extern pthread_mutex_t ndb_index_stat_glob_mutex;
+extern pthread_mutex_t ndb_index_stat_list_mutex;
+extern pthread_mutex_t ndb_index_stat_stat_mutex;
+extern pthread_cond_t ndb_index_stat_stat_cond;
+
+extern bool ndb_index_stat_get_enable(THD *thd);
+
+extern long g_ndb_status_index_stat_cache_query;
+extern long g_ndb_status_index_stat_cache_clean;
+
+void 
+compute_index_bounds(NdbIndexScanOperation::IndexBound & bound,
+                     const KEY *key_info,
+                     const key_range *start_key, const key_range *end_key);

=== modified file 'sql/ha_ndbcluster.cc'
--- a/sql/ha_ndbcluster.cc	2011-06-17 11:15:01 +0000
+++ b/sql/ha_ndbcluster.cc	2011-06-17 12:41:11 +0000
@@ -69,6 +69,7 @@ static ulong opt_ndb_wait_connected;
 ulong opt_ndb_wait_setup;
 static ulong opt_ndb_cache_check_time;
 static uint opt_ndb_cluster_connection_pool;
+static char* opt_ndb_index_stat_option;
 static char* opt_ndb_connectstring;
 static uint opt_ndb_nodeid;
 
@@ -182,7 +183,7 @@ static MYSQL_THDVAR_BOOL(
 static MYSQL_THDVAR_ULONG(
   index_stat_cache_entries,          /* name */
   PLUGIN_VAR_NOCMDARG,
-  "",
+  "Obsolete (ignored and will be removed later).",
   NULL,                              /* check func. */
   NULL,                              /* update func. */
   32,                                /* default */
@@ -195,7 +196,7 @@ static MYSQL_THDVAR_ULONG(
 static MYSQL_THDVAR_ULONG(
   index_stat_update_freq,            /* name */
   PLUGIN_VAR_NOCMDARG,
-  "",
+  "Obsolete (ignored and will be removed later).",
   NULL,                              /* check func. */
   NULL,                              /* update func. */
   20,                                /* default */
@@ -278,6 +279,15 @@ static MYSQL_THDVAR_BOOL(
 );
 
 /*
+  Required in index_stat.cc but available only from here
+  thanks to use of top level anonymous structs.
+*/
+bool ndb_index_stat_get_enable(THD *thd)
+{
+  return THDVAR(thd, index_stat_enable);
+}
+
+/*
   Default value for max number of transactions createable against NDB from
   the handler. Should really be 2 but there is a transaction to much allocated
   when lock table is used, and one extra to used for global schema lock.
@@ -356,6 +366,11 @@ ndbcluster_alter_table_flags(uint flags)
 
 #define NDB_AUTO_INCREMENT_RETRIES 100
 #define BATCH_FLUSH_SIZE (32768)
+/*
+  Room for 10 instruction words, two labels (@ 2words/label)
+  + 2 extra words for the case of resolve_size == 8
+*/
+#define MAX_CONFLICT_INTERPRETED_PROG_SIZE 16
 
 static void set_ndb_err(THD *thd, const NdbError &err);
 static int ndb_to_mysql_error(const NdbError *ndberr);
@@ -424,33 +439,27 @@ pthread_cond_t COND_ndb_util_thread;
 pthread_cond_t COND_ndb_util_ready;
 pthread_handler_t ndb_util_thread_func(void *arg);
 
+// Index stats thread variables
+pthread_t ndb_index_stat_thread;
+int ndb_index_stat_thread_running= 0;
+pthread_mutex_t LOCK_ndb_index_stat_thread;
+pthread_cond_t COND_ndb_index_stat_thread;
+pthread_cond_t COND_ndb_index_stat_ready;
+pthread_mutex_t ndb_index_stat_glob_mutex;
+pthread_mutex_t ndb_index_stat_list_mutex;
+pthread_mutex_t ndb_index_stat_stat_mutex;
+pthread_cond_t ndb_index_stat_stat_cond;
+pthread_handler_t ndb_index_stat_thread_func(void *arg);
+
+extern void ndb_index_stat_free(NDB_SHARE *share);
+extern void ndb_index_stat_end();
+
 /* Status variables shown with 'show status like 'Ndb%' */
 
-struct st_ndb_status {
-  st_ndb_status() { bzero(this, sizeof(struct st_ndb_status)); }
-  long cluster_node_id;
-  const char * connected_host;
-  long connected_port;
-  long number_of_replicas;
-  long number_of_data_nodes;
-  long number_of_ready_data_nodes;
-  long connect_count;
-  long execute_count;
-  long scan_count;
-  long pruned_scan_count;
-  long sorted_scan_count;
-  long pushed_queries_defined;
-  long pushed_queries_dropped;
-  long pushed_queries_executed;
-  long pushed_reads;
-  long transaction_no_hint_count[MAX_NDB_NODES];
-  long transaction_hint_count[MAX_NDB_NODES];
-  long long api_client_stats[Ndb::NumClientStatistics];
-};
+struct st_ndb_status g_ndb_status;
 
-static struct st_ndb_status g_ndb_status;
-static long g_ndb_status_conflict_fn_max= 0;
-static long g_ndb_status_conflict_fn_old= 0;
+long g_ndb_status_index_stat_cache_query = 0;
+long g_ndb_status_index_stat_cache_clean = 0;
 
 long long g_event_data_count = 0;
 long long g_event_nondata_count = 0;
@@ -467,6 +476,37 @@ update_slave_api_stats(Ndb* ndb)
     g_slave_api_client_stats[i] = ndb->getClientStat(i);
 }
 
+st_ndb_slave_state g_ndb_slave_state;
+
+st_ndb_slave_state::st_ndb_slave_state()
+  : current_conflict_defined_op_count(0)
+{
+  memset(current_violation_count, 0, sizeof(current_violation_count));
+  memset(total_violation_count, 0, sizeof(total_violation_count));
+};
+
+void
+st_ndb_slave_state::atTransactionAbort()
+{
+  /* Reset current-transaction counters + state */
+  memset(current_violation_count, 0, sizeof(current_violation_count));
+  current_conflict_defined_op_count = 0;
+}
+
+void
+st_ndb_slave_state::atTransactionCommit()
+{
+  /* Merge committed transaction counters into total state
+   * Then reset current transaction counters
+   */
+  for (int i=0; i < CFT_NUMBER_OF_CFTS; i++)
+  {
+    total_violation_count[i]+= current_violation_count[i];
+    current_violation_count[i] = 0;
+  }
+  current_conflict_defined_op_count = 0;
+}
+
 static int update_status_variables(Thd_ndb *thd_ndb,
                                    st_ndb_status *ns,
                                    Ndb_cluster_connection *c)
@@ -594,8 +634,9 @@ SHOW_VAR ndb_status_variables_dynamic[]=
 };
 
 SHOW_VAR ndb_status_conflict_variables[]= {
-  {"fn_max",     (char*) &g_ndb_status_conflict_fn_max, SHOW_LONG},
-  {"fn_old",     (char*) &g_ndb_status_conflict_fn_old, SHOW_LONG},
+  {"fn_max",       (char*) &g_ndb_slave_state.total_violation_count[CFT_NDB_MAX], SHOW_LONG},
+  {"fn_old",       (char*) &g_ndb_slave_state.total_violation_count[CFT_NDB_OLD], SHOW_LONG},
+  {"fn_max_del_win", (char*) &g_ndb_slave_state.total_violation_count[CFT_NDB_MAX_DEL_WIN], SHOW_LONG},
   {NullS, NullS, SHOW_LONG}
 };
 
@@ -642,6 +683,12 @@ static int show_ndb_server_api_stats(THD
   return 0;
 }
 
+SHOW_VAR ndb_status_index_stat_variables[]= {
+  {"cache_query",     (char*) &g_ndb_status_index_stat_cache_query, SHOW_LONG},
+  {"cache_clean",     (char*) &g_ndb_status_index_stat_cache_clean, SHOW_LONG},
+  {NullS, NullS, SHOW_LONG}
+};
+
 #ifndef NO_PUSHED_JOIN
 
 /**
@@ -781,6 +828,24 @@ static int write_conflict_row(NDB_SHARE
 }
 #endif
 
+#ifdef HAVE_NDB_BINLOG
+int
+handle_conflict_op_error(Thd_ndb* thd_ndb,
+                         NdbTransaction* trans,
+                         const NdbError& err,
+                         const NdbOperation* op);
+
+int
+handle_row_conflict(NDB_CONFLICT_FN_SHARE* cfn_share,
+                    const NdbRecord* key_rec,
+                    const uchar* pk_row,
+                    enum_conflicting_op_type op_type,
+                    enum_conflict_cause conflict_cause,
+                    const NdbError& conflict_error,
+                    NdbTransaction* conflict_trans,
+                    NdbError& err);
+#endif
+
 inline int
 check_completed_operations_pre_commit(Thd_ndb *thd_ndb, NdbTransaction *trans,
                                       const NdbOperation *first,
@@ -801,102 +866,42 @@ check_completed_operations_pre_commit(Th
     or exceptions to report
   */
 #ifdef HAVE_NDB_BINLOG
-  uint conflict_rows_written= 0;
+  const NdbOperation* lastUserOp = trans->getLastDefinedOperation();
 #endif
   while (true)
   {
     const NdbError &err= first->getNdbError();
-    if (err.classification != NdbError::NoError
-#ifndef HAVE_NDB_BINLOG
-        && err.classification != NdbError::ConstraintViolation
-        && err.classification != NdbError::NoDataFound
-#endif
-        )
+    const bool op_has_conflict_detection = (first->getCustomData() != NULL);
+    if (!op_has_conflict_detection)
     {
-#ifdef HAVE_NDB_BINLOG
-      DBUG_PRINT("info", ("ndb error: %d", err.code));
-      if (err.code == (int) error_conflict_fn_max_violation)
-      {
-        DBUG_PRINT("info", ("err.code == (int) error_conflict_fn_max_violation"));
-        thd_ndb->m_max_violation_count++;
-      }
-      else if (err.code == (int) error_conflict_fn_old_violation ||
-               err.classification == NdbError::ConstraintViolation ||
-               err.classification == NdbError::NoDataFound)
-      {
-        DBUG_PRINT("info",
-                   ("err.code %s (int) error_conflict_fn_old_violation, "
-                    "err.classification %s",
-                    err.code == (int) error_conflict_fn_old_violation ? "==" : "!=",
-                    err.classification
-                    == NdbError::ConstraintViolation
-                    ? "== NdbError::ConstraintViolation"
-                    : (err.classification == NdbError::NoDataFound
-                       ? "== NdbError::NoDataFound" : "!=")));
-        thd_ndb->m_old_violation_count++;
-        const void* buffer= first->getCustomData();
-        if (buffer != NULL)
-        {
-          Ndb_exceptions_data ex_data;
-          memcpy(&ex_data, buffer, sizeof(ex_data));
-          NDB_SHARE *share= ex_data.share;
-          const uchar* row= ex_data.row;
-          DBUG_ASSERT(share != NULL && row != NULL);
-
-          NDB_CONFLICT_FN_SHARE* cfn_share= share->m_cfn_share;
-          if (cfn_share && cfn_share->m_ex_tab)
-          {
-            NdbError ex_err;
-            if (write_conflict_row(share, trans, row, ex_err))
-            {
-              char msg[FN_REFLEN];
-              my_snprintf(msg, sizeof(msg), "table %s NDB error %d '%s'",
-                          cfn_share->m_ex_tab->getName(),
-                          ex_err.code, ex_err.message);
-
-              NdbDictionary::Dictionary* dict= thd_ndb->ndb->getDictionary();
-
-              if (ex_err.classification == NdbError::SchemaError)
-              {
-                dict->removeTableGlobal(*(cfn_share->m_ex_tab), false);
-                cfn_share->m_ex_tab= NULL;
-              }
-              else if (ex_err.status == NdbError::TemporaryError)
-              {
-                /* Slave will roll back and retry entire transaction. */
-                ERR_RETURN(ex_err);
-              }
-              else
-              {
-                push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
-                                    ER_EXCEPTIONS_WRITE_ERROR,
-                                    ER(ER_EXCEPTIONS_WRITE_ERROR), msg);
-                /* Slave will stop replication. */
-                DBUG_RETURN(ER_EXCEPTIONS_WRITE_ERROR);
-              }
-            }
-            else
-            {
-              conflict_rows_written++;
-            }
-          }
-          else
-          {
-            DBUG_PRINT("info", ("missing %s", !cfn_share ? "cfn_share" : "ex_tab"));
-          }
-        }
-        else
-        {
-          DBUG_PRINT("info", ("missing custom data"));
-        }
-      }
-      else
-#endif
+      /* 'Normal path' - ignore key (not) present, others are errors */
+      if (err.classification != NdbError::NoError &&
+          err.classification != NdbError::ConstraintViolation &&
+          err.classification != NdbError::NoDataFound)
       {
+        /* Non ignored error, report it */
         DBUG_PRINT("info", ("err.code == %u", err.code));
         DBUG_RETURN(err.code);
       }
     }
+#ifdef HAVE_NDB_BINLOG
+    else
+    {
+      /*
+         Op with conflict detection, use special error handling method
+       */
+
+      if (err.classification != NdbError::NoError)
+      {
+        int res = handle_conflict_op_error(thd_ndb,
+                                           trans,
+                                           err,
+                                           first);
+        if (res != 0)
+          DBUG_RETURN(res);
+      }
+    } // if (!op_has_conflict_detection)
+#endif
     if (err.classification != NdbError::NoError)
       ignores++;
 
@@ -908,7 +913,11 @@ check_completed_operations_pre_commit(Th
   if (ignore_count)
     *ignore_count= ignores;
 #ifdef HAVE_NDB_BINLOG
-  if (conflict_rows_written)
+  /*
+     Conflict detection related error handling above may have defined
+     new operations on the transaction.  If so, execute them now
+  */
+  if (trans->getLastDefinedOperation() != lastUserOp)
   {
     if (trans->execute(NdbTransaction::NoCommit,
                        NdbOperation::AO_IgnoreError,
@@ -1013,10 +1022,10 @@ int execute_no_commit(Thd_ndb *thd_ndb,
                                                     ignore_count));
 }
 
-int execute_commit(Thd_ndb *thd_ndb, NdbTransaction *trans,
+int execute_commit(THD* thd, Thd_ndb *thd_ndb, NdbTransaction *trans,
                    int force_send, int ignore_error, uint *ignore_count= 0);
 inline
-int execute_commit(Thd_ndb *thd_ndb, NdbTransaction *trans,
+int execute_commit(THD* thd, Thd_ndb *thd_ndb, NdbTransaction *trans,
                    int force_send, int ignore_error, uint *ignore_count)
 {
   DBUG_ENTER("execute_commit");
@@ -1033,19 +1042,19 @@ int execute_commit(Thd_ndb *thd_ndb, Ndb
   const NdbOperation *first= trans->getFirstDefinedOperation();
   const NdbOperation *last= trans->getLastDefinedOperation();
   thd_ndb->m_execute_count++;
-  thd_ndb->m_conflict_fn_usage_count= 0;
   thd_ndb->m_unsent_bytes= 0;
   DBUG_PRINT("info", ("execute_count: %u", thd_ndb->m_execute_count));
   if (trans->execute(NdbTransaction::Commit, ao, force_send))
   {
-    thd_ndb->m_max_violation_count= 0;
-    thd_ndb->m_old_violation_count= 0;
+    if (thd->slave_thread)
+      g_ndb_slave_state.atTransactionAbort();
     DBUG_RETURN(-1);
   }
-  g_ndb_status_conflict_fn_max+= thd_ndb->m_max_violation_count;
-  g_ndb_status_conflict_fn_old+= thd_ndb->m_old_violation_count;
-  thd_ndb->m_max_violation_count= 0;
-  thd_ndb->m_old_violation_count= 0;
+  /* Success of some sort */
+  if (thd->slave_thread)
+  {
+    g_ndb_slave_state.atTransactionCommit();
+  }
   if (!ignore_error || trans->getNdbError().code == 0)
     DBUG_RETURN(trans->getNdbError().code);
   DBUG_RETURN(check_completed_operations(thd_ndb, trans, first, last,
@@ -1101,14 +1110,10 @@ Thd_ndb::Thd_ndb()
   m_execute_count= 0;
   m_scan_count= 0;
   m_pruned_scan_count= 0;
-  m_sorted_scan_count= 0;
   m_pushed_queries_defined= 0;
   m_pushed_queries_dropped= 0;
   m_pushed_queries_executed= 0;
   m_pushed_reads= 0;
-  m_max_violation_count= 0;
-  m_old_violation_count= 0;
-  m_conflict_fn_usage_count= 0;
   bzero(m_transaction_no_hint_count, sizeof(m_transaction_no_hint_count));
   bzero(m_transaction_hint_count, sizeof(m_transaction_hint_count));
   global_schema_lock_trans= NULL;
@@ -1196,7 +1201,19 @@ void ha_ndbcluster::set_rec_per_key()
     case ORDERED_INDEX:
       // 'Records pr. key' are unknown for non-unique indexes.
       // (May change when we get better index statistics.)
+    {
+      THD *thd= current_thd;
+      const bool index_stat_enable= THDVAR(NULL, index_stat_enable) &&
+                                    THDVAR(thd, index_stat_enable);
+      if (index_stat_enable)
+      {
+        int err= ndb_index_stat_set_rpk(i);
+        if (err == 0)
+          break;
+      }
+      // no fallback method...
       break;
+    }
     default:
       DBUG_ASSERT(false);
     }
@@ -2224,10 +2241,6 @@ static void ndb_init_index(NDB_INDEX_DAT
   data.unique_index= NULL;
   data.index= NULL;
   data.unique_index_attrid_map= NULL;
-  data.index_stat=NULL;
-  data.index_stat_cache_entries=0;
-  data.index_stat_update_freq=0;
-  data.index_stat_query_count=0;
   data.ndb_record_key= NULL;
   data.ndb_unique_record_key= NULL;
   data.ndb_unique_record_row= NULL;
@@ -2239,10 +2252,6 @@ static void ndb_clear_index(NDBDICT *dic
   {
     my_free((char*)data.unique_index_attrid_map, MYF(0));
   }
-  if (data.index_stat)
-  {
-    delete data.index_stat;
-  }
   if (data.ndb_unique_record_key)
     dict->releaseRecord(data.ndb_unique_record_key);
   if (data.ndb_unique_record_row)
@@ -2314,25 +2323,6 @@ int ha_ndbcluster::add_index_handle(THD
       break;
     } while (1);
     m_index[index_no].index= index;
-    // ordered index - add stats
-    NDB_INDEX_DATA& d=m_index[index_no];
-    delete d.index_stat;
-    d.index_stat=NULL;
-    if (THDVAR(thd, index_stat_enable))
-    {
-      d.index_stat=new NdbIndexStat();
-      d.index_stat_cache_entries= THDVAR(thd, index_stat_cache_entries);
-      d.index_stat_update_freq= THDVAR(thd, index_stat_update_freq);
-      d.index_stat_query_count=0;
-      d.index_stat->alloc_cache(d.index_stat_cache_entries);
-      DBUG_PRINT("info", ("index %s stat=on cache_entries=%u update_freq=%u",
-                          index->getName(),
-                          d.index_stat_cache_entries,
-                          d.index_stat_update_freq));
-    } else
-    {
-      DBUG_PRINT("info", ("index %s stat=off", index->getName()));
-    }
   }
   if (idx_type == UNIQUE_ORDERED_INDEX || idx_type == UNIQUE_INDEX)
   {
@@ -3131,9 +3121,8 @@ int ha_ndbcluster::ndb_pk_update_row(THD
     DBUG_PRINT("info", ("insert failed"));
     if (trans->commitStatus() == NdbConnection::Started)
     {
-      m_thd_ndb->m_max_violation_count= 0;
-      m_thd_ndb->m_old_violation_count= 0;
-      m_thd_ndb->m_conflict_fn_usage_count= 0;
+      if (thd->slave_thread)
+        g_ndb_slave_state.atTransactionAbort();
       m_thd_ndb->m_unsent_bytes= 0;
       m_thd_ndb->m_execute_count++;
       DBUG_PRINT("info", ("execute_count: %u", m_thd_ndb->m_execute_count));
@@ -3871,7 +3860,7 @@ count_key_columns(const KEY *key_info, c
 }
 
 /* Helper method to compute NDB index bounds. Note: does not set range_no. */
-static void
+void
 compute_index_bounds(NdbIndexScanOperation::IndexBound & bound,
                      const KEY *key_info,
                      const key_range *start_key, const key_range *end_key)
@@ -3922,6 +3911,11 @@ compute_index_bounds(NdbIndexScanOperati
     bound.high_key= NULL;
     bound.high_key_count= 0;
   }
+  DBUG_PRINT("info", ("start_flag=0x%x end_flag=0x%x"
+                      " lo_keys=%d lo_incl=%d hi_keys=%d hi_incl=%d",
+                      start_key?start_key->flag:0, end_key?end_key->flag:0,
+                      bound.low_key_count, bound.low_inclusive,
+                      bound.high_key_count, bound.high_inclusive));
 }
 
 /**
@@ -4385,6 +4379,239 @@ thd_allow_batch(const THD* thd)
 #endif
 }
 
+#ifdef HAVE_NDB_BINLOG
+/**
+   prepare_conflict_detection
+
+   This method is called during operation definition by the slave,
+   when writing to a table with conflict detection defined.
+
+   It is responsible for defining and adding any operation filtering
+   required, and for saving any operation definition state required
+   for post-execute analysis
+*/
+int
+ha_ndbcluster::prepare_conflict_detection(enum_conflicting_op_type op_type,
+                                          const NdbRecord* key_rec,
+                                          const uchar* old_data,
+                                          const uchar* new_data,
+                                          NdbInterpretedCode* code,
+                                          NdbOperation::OperationOptions* options)
+{
+  DBUG_ENTER("prepare_conflict_detection");
+
+  int res = 0;
+  const st_conflict_fn_def* conflict_fn = m_share->m_cfn_share->m_conflict_fn;
+  assert( conflict_fn != NULL );
+
+
+  /*
+     Prepare interpreted code for operation (update + delete only) according
+     to algorithm used
+  */
+  if (op_type != WRITE_ROW)
+  {
+    res = conflict_fn->prep_func(m_share->m_cfn_share,
+                                 op_type,
+                                 old_data,
+                                 new_data,
+                                 table->write_set,
+                                 code);
+
+    if (!res)
+    {
+      /* Attach conflict detecting filter program to operation */
+      options->optionsPresent|=NdbOperation::OperationOptions::OO_INTERPRETED;
+      options->interpretedCode= code;
+    }
+  } // if (op_type != WRITE_ROW)
+
+  g_ndb_slave_state.current_conflict_defined_op_count++;
+
+  /* Now save data for potential insert to exceptions table... */
+  const uchar* row_to_save = (op_type == DELETE_ROW)? old_data : new_data;
+  Ndb_exceptions_data ex_data;
+  ex_data.share= m_share;
+  ex_data.key_rec= key_rec;
+  ex_data.op_type= op_type;
+  /*
+    We need to save the row data for possible conflict resolution after
+    execute().
+  */
+  ex_data.row= copy_row_to_buffer(m_thd_ndb, row_to_save);
+  uchar* ex_data_buffer= get_buffer(m_thd_ndb, sizeof(ex_data));
+  if (ex_data.row == NULL || ex_data_buffer == NULL)
+  {
+    DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+  }
+  memcpy(ex_data_buffer, &ex_data, sizeof(ex_data));
+
+  /* Store ptr to exceptions data in operation 'customdata' ptr */
+  options->optionsPresent|= NdbOperation::OperationOptions::OO_CUSTOMDATA;
+  options->customData= (void*)ex_data_buffer;
+
+  DBUG_RETURN(0);
+}
+
+/**
+   handle_conflict_op_error
+
+   This method is called when an error is detected after executing an
+   operation with conflict detection active.
+
+   If the operation error is related to conflict detection, handling
+   starts.
+
+   Handling involves incrementing the relevant counter, and optionally
+   refreshing the row and inserting an entry into the exceptions table
+*/
+
+int
+handle_conflict_op_error(Thd_ndb* thd_ndb,
+                         NdbTransaction* trans,
+                         const NdbError& err,
+                         const NdbOperation* op)
+{
+  DBUG_ENTER("handle_conflict_op_error");
+  DBUG_PRINT("info", ("ndb error: %d", err.code));
+
+  if ((err.code == (int) error_conflict_fn_violation) ||
+      (err.classification == NdbError::ConstraintViolation) ||
+      (err.classification == NdbError::NoDataFound))
+  {
+    DBUG_PRINT("info",
+               ("err.code %s (int) error_conflict_fn_violation, "
+                "err.classification %s",
+                err.code == (int) error_conflict_fn_violation ? "==" : "!=",
+                err.classification
+                == NdbError::ConstraintViolation
+                ? "== NdbError::ConstraintViolation"
+                : (err.classification == NdbError::NoDataFound
+                   ? "== NdbError::NoDataFound" : "!=")));
+
+    enum_conflict_cause conflict_cause;
+
+    if (err.code == (int) error_conflict_fn_violation)
+    {
+      conflict_cause= ROW_IN_CONFLICT;
+    }
+    else if (err.classification == NdbError::ConstraintViolation)
+    {
+      conflict_cause= ROW_ALREADY_EXISTS;
+    }
+    else
+    {
+      assert(err.classification == NdbError::NoDataFound);
+      conflict_cause= ROW_DOES_NOT_EXIST;
+    }
+
+    const void* buffer=op->getCustomData();
+    assert(buffer);
+    Ndb_exceptions_data ex_data;
+    memcpy(&ex_data, buffer, sizeof(ex_data));
+    NDB_SHARE *share= ex_data.share;
+    const NdbRecord* key_rec= ex_data.key_rec;
+    const uchar* row= ex_data.row;
+    enum_conflicting_op_type op_type = ex_data.op_type;
+    DBUG_ASSERT(share != NULL && row != NULL);
+
+    NDB_CONFLICT_FN_SHARE* cfn_share= share->m_cfn_share;
+    if (cfn_share)
+    {
+      enum_conflict_fn_type cft = cfn_share->m_conflict_fn->type;
+      bool haveExTable = cfn_share->m_ex_tab != NULL;
+
+      g_ndb_slave_state.current_violation_count[cft]++;
+
+      {
+        NdbError handle_error;
+        if (handle_row_conflict(cfn_share,
+                                key_rec,
+                                row,
+                                op_type,
+                                conflict_cause,
+                                err,
+                                trans,
+                                handle_error))
+        {
+          /* Error with handling of row conflict */
+          char msg[FN_REFLEN];
+          my_snprintf(msg, sizeof(msg), "Row conflict handling "
+                      "on table %s hit Ndb error %d '%s'",
+                      share->table_name,
+                      handle_error.code,
+                      handle_error.message);
+
+          if (handle_error.status == NdbError::TemporaryError)
+          {
+            /* Slave will roll back and retry entire transaction. */
+            ERR_RETURN(handle_error);
+          }
+          else
+          {
+            push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+                                ER_EXCEPTIONS_WRITE_ERROR,
+                                ER(ER_EXCEPTIONS_WRITE_ERROR), msg);
+            /* Slave will stop replication. */
+            DBUG_RETURN(ER_EXCEPTIONS_WRITE_ERROR);
+          }
+        }
+      }
+
+
+      if (haveExTable)
+      {
+        NdbError ex_err;
+        if (write_conflict_row(share, trans, row, ex_err))
+        {
+          char msg[FN_REFLEN];
+          my_snprintf(msg, sizeof(msg), "table %s NDB error %d '%s'",
+                      cfn_share->m_ex_tab->getName(),
+                      ex_err.code, ex_err.message);
+
+          NdbDictionary::Dictionary* dict= thd_ndb->ndb->getDictionary();
+
+          if (ex_err.classification == NdbError::SchemaError)
+          {
+            dict->removeTableGlobal(*(cfn_share->m_ex_tab), false);
+            cfn_share->m_ex_tab= NULL;
+          }
+          else if (ex_err.status == NdbError::TemporaryError)
+          {
+            /* Slave will roll back and retry entire transaction. */
+            ERR_RETURN(ex_err);
+          }
+          else
+          {
+            push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+                                ER_EXCEPTIONS_WRITE_ERROR,
+                                ER(ER_EXCEPTIONS_WRITE_ERROR), msg);
+            /* Slave will stop replication. */
+            DBUG_RETURN(ER_EXCEPTIONS_WRITE_ERROR);
+          }
+        }
+      } // if (haveExTable)
+
+      DBUG_RETURN(0);
+    }
+    else
+    {
+      DBUG_PRINT("info", ("missing cfn_share"));
+      DBUG_RETURN(0); // TODO : Correct?
+    }
+  }
+  else
+  {
+    /* Non conflict related error */
+    DBUG_PRINT("info", ("err.code == %u", err.code));
+    DBUG_RETURN(err.code);
+  }
+
+  DBUG_RETURN(0); // Reachable?
+}
+#endif /* HAVE_NDB_BINLOG */
+
+
 int ha_ndbcluster::write_row(uchar *record)
 {
   DBUG_ENTER("ha_ndbcluster::write_row");
@@ -4569,14 +4796,16 @@ int ha_ndbcluster::ndb_write_row(uchar *
   MY_BITMAP tmpBitmap;
   MY_BITMAP *user_cols_written_bitmap;
 #ifdef HAVE_NDB_BINLOG
-  uchar* ex_data_buffer= NULL;
+  bool haveConflictFunction =
+    (thd->slave_thread &&
+     m_share->m_cfn_share &&
+     m_share->m_cfn_share->m_conflict_fn);
 #endif
   
   if (m_use_write
 #ifdef HAVE_NDB_BINLOG
-      && !(thd->slave_thread &&
-           m_share->m_cfn_share &&
-           m_share->m_cfn_share->m_resolve_cft != CFT_NDB_UNDEF)
+      /* Conflict detection must use normal Insert */
+      && !haveConflictFunction
 #endif
       )
   {
@@ -4608,52 +4837,7 @@ int ha_ndbcluster::ndb_write_row(uchar *
       user_cols_written_bitmap= NULL;
       mask= NULL;
     }
-
-#if 0 /* NOT YET, interpeted function not supported for write */
-#ifdef HAVE_NDB_BINLOG
-    /*
-      Room for 10 instruction words, two labels (@ 2words/label)
-      + 2 extra words for the case of resolve_size == 8
-    */
-    Uint32 buffer[16];
-    NdbInterpretedCode code(m_table, buffer,
-                            sizeof(buffer)/sizeof(buffer[0]));
-    if (useWriteSet)
-    {
-      /* Conflict resolution in slave thread. */
-      enum_conflict_fn_type cft= m_share->m_cfn_share ? m_share->m_cfn_share->m_resolve_cft : CFT_NDB_UNDEF;
-      if (cft != CFT_NDB_UNDEF)
-      {
-        if (!write_row_conflict_fn(cft, record, &code))
-        {
-          options.optionsPresent|=NdbOperation::OperationOptions::OO_INTERPRETED;
-          options.interpretedCode= &code;
-          thd_ndb->m_conflict_fn_usage_count++;
-        }
-        
-        Ndb_exceptions_data ex_data;
-        ex_data.share= m_share;
-        /*
-          We need to save the row data for possible conflict resolution after
-          execute().
-        */
-        ex_data.row= copy_row_to_buffer(thd_ndb, record);
-        ex_data_buffer= get_buffer(thd_ndb, sizeof(ex_data));
-        if (ex_data.row == NULL || ex_data_buffer == NULL)
-        {
-          DBUG_RETURN(HA_ERR_OUT_OF_MEM);
-        }
-        memcpy(ex_data_buffer, &ex_data, sizeof(ex_data));
-
-        options.optionsPresent|= NdbOperation::OperationOptions::OO_CUSTOMDATA;
-        options.customData= (void*)ex_data_buffer;
-        if (options.optionsPresent != 0)
-          poptions=&options;
-      }
-    }
-#endif  /* HAVE_NDB_BINLOG */
-#endif  /* NOT YET */
-
+    /* TODO : Add conflict detection etc when interpreted write supported */
     op= trans->writeTuple(key_rec, (const char *)key_row, m_ndb_record,
                           (char *)record, mask,
                           poptions, sizeof(NdbOperation::OperationOptions));
@@ -4661,27 +4845,16 @@ int ha_ndbcluster::ndb_write_row(uchar *
   else
   {
 #ifdef HAVE_NDB_BINLOG
-    if (m_use_write)
+    if (haveConflictFunction)
     {
-      thd_ndb->m_conflict_fn_usage_count++;
-
-      Ndb_exceptions_data ex_data;
-      ex_data.share= m_share;
-      /*
-        We need to save the row data for possible conflict resolution after
-        execute().
-      */
-      ex_data.row= copy_row_to_buffer(thd_ndb, record);
-      ex_data_buffer= get_buffer(thd_ndb, sizeof(ex_data));
-      if (ex_data.row == NULL || ex_data_buffer == NULL)
-      {
-        DBUG_RETURN(HA_ERR_OUT_OF_MEM);
-      }
-      memcpy(ex_data_buffer, &ex_data, sizeof(ex_data));
-
-      options.optionsPresent|= NdbOperation::OperationOptions::OO_CUSTOMDATA;
-      options.customData= (void*)ex_data_buffer;
-      poptions=&options;
+      /* Conflict detection in slave thread */
+      if (unlikely((error = prepare_conflict_detection(WRITE_ROW,
+                                                       key_rec,
+                                                       NULL,    /* old_data */
+                                                       record,  /* new_data */
+                                                       NULL,    /* code */
+                                                       &options))))
+        DBUG_RETURN(error);
     }
 #endif
     uchar *mask;
@@ -4831,248 +5004,20 @@ int ha_ndbcluster::primary_key_cmp(const
 
 #ifdef HAVE_NDB_BINLOG
 
-/**
-  CFT_NDB_NEW
-
-  To perform conflict resolution, an interpreted program is used to read
-  the timestamp stored locally and compare to what is going to be applied.
-  If timestamp is lower, an error for this operation (9999) will be raised,
-  and new row will not be applied. The error codes for the operations will
-  be checked on return.  For this to work is is vital that the operation
-  is run with ignore error option.
-*/
-
 int
-ha_ndbcluster::row_conflict_fn_max(const uchar *new_data,
-                                   NdbInterpretedCode *code)
+handle_row_conflict(NDB_CONFLICT_FN_SHARE* cfn_share,
+                    const NdbRecord* key_rec,
+                    const uchar* pk_row,
+                    enum_conflicting_op_type op_type,
+                    enum_conflict_cause conflict_cause,
+                    const NdbError& conflict_error,
+                    NdbTransaction* conflict_trans,
+                    NdbError& err)
 {
-  uint32 resolve_column= m_share->m_cfn_share->m_resolve_column;
-  uint32 resolve_size= m_share->m_cfn_share->m_resolve_size;
+  DBUG_ENTER("handle_row_conflict");
 
-  DBUG_PRINT("info", ("interpreted update pre equal on column %u",
-                      resolve_column));
-
-  DBUG_ASSERT(resolve_size == 4 || resolve_size == 8);
-
-  if (!bitmap_is_set(table->write_set, resolve_column))
-  {
-    sql_print_information("NDB Slave: missing data for NDB_MAX");
-    return 1;
-  }
-
-  const uint label_0= 0;
-  const Uint32 RegNewValue= 1, RegCurrentValue= 2;
-  int r;
-  Field *field= table->field[resolve_column];
-  DBUG_PRINT("info", ("interpreted update post equal"));
-  /*
-   * read new value from record
-   */
-  union {
-    uint32 new_value_32;
-    uint64 new_value_64;
-  };
-  {
-    const uchar *field_ptr= field->ptr + (new_data - table->record[0]);
-    if (resolve_size == 4)
-    {
-      memcpy(&new_value_32, field_ptr, resolve_size);
-      DBUG_PRINT("info", ("new_value_32: %u", new_value_32));
-    }
-    else
-    {
-      memcpy(&new_value_64, field_ptr, resolve_size);
-      DBUG_PRINT("info", ("new_value_64: %llu",
-                          (unsigned long long) new_value_64));
-    }
-  }
-  /*
-   * Load registers RegNewValue and RegCurrentValue
-   */
-  if (resolve_size == 4)
-    r= code->load_const_u32(RegNewValue, new_value_32);
-  else
-    r= code->load_const_u64(RegNewValue, new_value_64);
-  DBUG_ASSERT(r == 0);
-  r= code->read_attr(RegCurrentValue, resolve_column);
-  DBUG_ASSERT(r == 0);
-  /*
-   * if RegNewValue > RegCurrentValue goto label_0
-   * else raise error for this row
-   */
-  r= code->branch_gt(RegNewValue, RegCurrentValue, label_0);
-  DBUG_ASSERT(r == 0);
-  r= code->interpret_exit_nok(error_conflict_fn_max_violation);
-  DBUG_ASSERT(r == 0);
-  r= code->def_label(label_0);
-  DBUG_ASSERT(r == 0);
-  r= code->interpret_exit_ok();
-  DBUG_ASSERT(r == 0);
-  r= code->finalise();
-  DBUG_ASSERT(r == 0);
-  return r;
-}
-
-/**
-  CFT_NDB_OLD
-
-  To perform conflict detection, an interpreted program is used to read
-  the timestamp stored locally and compare to what was on the master.
-  If timestamp is not equal, an error for this operation (9998) will be raised,
-  and new row will not be applied. The error codes for the operations will
-  be checked on return.  For this to work is is vital that the operation
-  is run with ignore error option.
-
-  As an independent feature, phase 2 also saves the
-  conflicts into the table's exceptions table.
-*/
-
-int
-ha_ndbcluster::row_conflict_fn_old(const uchar *old_data,
-                                   NdbInterpretedCode *code)
-{
-  uint32 resolve_column= m_share->m_cfn_share->m_resolve_column;
-  uint32 resolve_size= m_share->m_cfn_share->m_resolve_size;
-
-  DBUG_PRINT("info", ("interpreted update pre equal on column %u",
-                      resolve_column));
-
-  DBUG_ASSERT(resolve_size == 4 || resolve_size == 8);
-
-  if (!bitmap_is_set(table->write_set, resolve_column))
-  {
-    sql_print_information("NDB Slave: missing data for NDB_OLD");
-    return -1;
-  }
-
-  const uint label_0= 0;
-  const Uint32 RegOldValue= 1, RegCurrentValue= 2;
-  int r;
-  Field *field= table->field[resolve_column];
-  DBUG_PRINT("info", ("interpreted update post equal"));
-  /*
-   * read old value from record
-   */
-  union {
-    uint32 old_value_32;
-    uint64 old_value_64;
-  };
-  {
-    const uchar *field_ptr= field->ptr + (old_data - table->record[0]);
-    if (resolve_size == 4)
-    {
-      memcpy(&old_value_32, field_ptr, resolve_size);
-      DBUG_PRINT("info", ("old_value_32: %u", old_value_32));
-    }
-    else
-    {
-      memcpy(&old_value_64, field_ptr, resolve_size);
-      DBUG_PRINT("info", ("old_value_64: %llu",
-                          (unsigned long long) old_value_64));
-    }
-  }
-  /*
-   * Load registers RegOldValue and RegCurrentValue
-   */
-  if (resolve_size == 4)
-    r= code->load_const_u32(RegOldValue, old_value_32);
-  else
-    r= code->load_const_u64(RegOldValue, old_value_64);
-  DBUG_ASSERT(r == 0);
-  r= code->read_attr(RegCurrentValue, resolve_column);
-  DBUG_ASSERT(r == 0);
-  /*
-   * if RegOldValue == RegCurrentValue goto label_0
-   * else raise error for this row
-   */
-  r= code->branch_eq(RegOldValue, RegCurrentValue, label_0);
-  DBUG_ASSERT(r == 0);
-  r= code->interpret_exit_nok(error_conflict_fn_old_violation);
-  DBUG_ASSERT(r == 0);
-  r= code->def_label(label_0);
-  DBUG_ASSERT(r == 0);
-  r= code->interpret_exit_ok();
-  DBUG_ASSERT(r == 0);
-  r= code->finalise();
-  DBUG_ASSERT(r == 0);
-  return r;
-}
-
-int
-ha_ndbcluster::update_row_conflict_fn(enum_conflict_fn_type cft,
-                                      const uchar *old_data,
-                                      uchar *new_data,
-                                      NdbInterpretedCode *code)
-{
-  switch (cft) {
-  case CFT_NDB_MAX:
-  case CFT_NDB_MAX_DEL_WIN:
-    return row_conflict_fn_max(new_data, code);
-  case CFT_NDB_OLD:
-    return row_conflict_fn_old(old_data, code);
-  case CFT_NDB_UNDEF:
-    abort();
-  }
-  DBUG_ASSERT(false);
-  return 1;
-}
-
-#if 0 /* NOT YET, interpeted function not supported for write */
-int
-ha_ndbcluster::write_row_conflict_fn(enum_conflict_fn_type cft,
-                                     uchar *data,
-                                     NdbInterpretedCode *code)
-{
-  switch (cft) {
-  case CFT_NDB_MAX:
-  case CFT_NDB_MAX_DEL_WIN:
-    return row_conflict_fn_max(data, code);
-  case CFT_NDB_OLD:
-    /*
-      No conflict function here, instead detect if tuple
-      already exists
-     */
-    return 1;
-  case CFT_NDB_UNDEF:
-    abort();
-  }
-  DBUG_ASSERT(false);
-  return 1;
-}
-#endif
-
-int
-ha_ndbcluster::delete_row_conflict_fn(enum_conflict_fn_type cft,
-                                      const uchar *old_data,
-                                      NdbInterpretedCode *code)
-{
-  switch (cft) {
-  case CFT_NDB_MAX:
-    /*
-      As we do not have a timestamp for the actual delete,
-      the best we can do is to detect a possible conflict
-      on the old data.
-    */
-    return row_conflict_fn_old(old_data, code);
-  case CFT_NDB_MAX_DEL_WIN:
-  {
-    /**
-     * let delete always win
-     */
-    int r = code->interpret_exit_ok();
-    DBUG_ASSERT(r == 0);
-    r = code->finalise();
-    DBUG_ASSERT(r == 0);
-    return r;
-  }
-  case CFT_NDB_OLD:
-    return row_conflict_fn_old(old_data, code);
-  case CFT_NDB_UNDEF:
-    abort();
-  }
-  DBUG_ASSERT(false);
-  return 1;
-}
+  DBUG_RETURN(0);
+};
 #endif /* HAVE_NDB_BINLOG */
 
 /**
@@ -5123,7 +5068,7 @@ int ha_ndbcluster::exec_bulk_update(uint
     DBUG_PRINT("info", ("committing auto-commit+rbwr early"));
     uint ignore_count= 0;
     const int ignore_error= 1;
-    if (execute_commit(m_thd_ndb, trans,
+    if (execute_commit(table->in_use, m_thd_ndb, trans,
                        m_thd_ndb->m_force_send, ignore_error,
                        &ignore_count) != 0)
     {
@@ -5402,42 +5347,21 @@ int ha_ndbcluster::ndb_update_row(const
 				 m_read_before_write_removal_used);
 
 #ifdef HAVE_NDB_BINLOG
-    uchar* ex_data_buffer= NULL;
-    /*
-      Room for 10 instruction words, two labels (@ 2words/label)
-      + 2 extra words for the case of resolve_size == 8
-    */
-    Uint32 buffer[16];
+    Uint32 buffer[ MAX_CONFLICT_INTERPRETED_PROG_SIZE ];
     NdbInterpretedCode code(m_table, buffer,
                             sizeof(buffer)/sizeof(buffer[0]));
+
     if (thd->slave_thread && m_share->m_cfn_share &&
-        (m_share->m_cfn_share->m_resolve_cft != CFT_NDB_UNDEF))
+        m_share->m_cfn_share->m_conflict_fn)
     {
-      /* Conflict resolution in slave thread. */
-      enum_conflict_fn_type cft= m_share->m_cfn_share->m_resolve_cft;
-      if (!update_row_conflict_fn(cft, old_data, new_data, &code))
-      {
-        options.optionsPresent|=NdbOperation::OperationOptions::OO_INTERPRETED;
-        options.interpretedCode= &code;
-        thd_ndb->m_conflict_fn_usage_count++;
-      }
-
-      Ndb_exceptions_data ex_data;
-      ex_data.share= m_share;
-      /*
-        We need to save the row data for possible conflict resolution after
-        execute().
-      */
-      ex_data.row= copy_row_to_buffer(thd_ndb, new_data);
-      ex_data_buffer= get_buffer(thd_ndb, sizeof(ex_data));
-      if (ex_data.row == NULL || ex_data_buffer == NULL)
-      {
-        DBUG_RETURN(HA_ERR_OUT_OF_MEM);
-      }
-      memcpy(ex_data_buffer, &ex_data, sizeof(ex_data));
-
-      options.optionsPresent|= NdbOperation::OperationOptions::OO_CUSTOMDATA;
-      options.customData= (void*)ex_data_buffer;
+       /* Conflict resolution in slave thread. */
+      if (unlikely((error = prepare_conflict_detection(UPDATE_ROW,
+                                                       key_rec,
+                                                       old_data,
+                                                       new_data,
+                                                       &code,
+                                                       &options))))
+        DBUG_RETURN(error);
     }
 #endif /* HAVE_NDB_BINLOG */
     if (options.optionsPresent !=0)
@@ -5532,7 +5456,7 @@ int ha_ndbcluster::end_bulk_delete()
     DBUG_PRINT("info", ("committing auto-commit+rbwr early"));
     uint ignore_count= 0;
     const int ignore_error= 1;
-    if (execute_commit(m_thd_ndb, trans,
+    if (execute_commit(table->in_use, m_thd_ndb, trans,
                        m_thd_ndb->m_force_send, ignore_error,
                        &ignore_count) != 0)
     {
@@ -5698,42 +5622,20 @@ int ha_ndbcluster::ndb_delete_row(const
 				 m_read_before_write_removal_used);
 
 #ifdef HAVE_NDB_BINLOG
-    uchar* ex_data_buffer= NULL;
-    /*
-      Room for 10 instruction words, two labels (@ 2words/label)
-      + 2 extra words for the case of resolve_size == 8
-    */
-    Uint32 buffer[16];
+    Uint32 buffer[ MAX_CONFLICT_INTERPRETED_PROG_SIZE ];
     NdbInterpretedCode code(m_table, buffer,
                             sizeof(buffer)/sizeof(buffer[0]));
     if (thd->slave_thread && m_share->m_cfn_share &&
-        (m_share->m_cfn_share->m_resolve_cft != CFT_NDB_UNDEF))
+        m_share->m_cfn_share->m_conflict_fn)
     {
       /* Conflict resolution in slave thread. */
-      enum_conflict_fn_type cft= m_share->m_cfn_share->m_resolve_cft;
-      if (!delete_row_conflict_fn(cft, record, &code))
-      {
-        options.optionsPresent|=NdbOperation::OperationOptions::OO_INTERPRETED;
-        options.interpretedCode= &code;
-        thd_ndb->m_conflict_fn_usage_count++;
-      }
-
-      Ndb_exceptions_data ex_data;
-      ex_data.share= m_share;
-      /*
-        We need to save the row data for possible conflict resolution after
-        execute().
-      */
-      ex_data.row= copy_row_to_buffer(thd_ndb, record);
-      ex_data_buffer= get_buffer(thd_ndb, sizeof(ex_data));
-      if (ex_data.row == NULL || ex_data_buffer == NULL)
-      {
-        DBUG_RETURN(HA_ERR_OUT_OF_MEM);
-      }
-      memcpy(ex_data_buffer, &ex_data, sizeof(ex_data));
-
-      options.optionsPresent|= NdbOperation::OperationOptions::OO_CUSTOMDATA;
-      options.customData= (void*)ex_data_buffer;
+      if (unlikely((error = prepare_conflict_detection(DELETE_ROW,
+                                                       key_rec,
+                                                       key_row, /* old_data */
+                                                       NULL,    /* new_data */
+                                                       &code,
+                                                       &options))))
+        DBUG_RETURN(error);
     }
 #endif /* HAVE_NDB_BINLOG */
     if (options.optionsPresent != 0)
@@ -6688,9 +6590,11 @@ int ha_ndbcluster::info(uint flag)
     result= update_stats(thd, 1);
     break;
   }
-  if (flag & HA_STATUS_CONST)
+  /* RPK moved to variable part */
+  if (flag & HA_STATUS_VARIABLE)
   {
-    DBUG_PRINT("info", ("HA_STATUS_CONST"));
+    /* No meaningful way to return error */
+    DBUG_PRINT("info", ("rec_per_key"));
     set_rec_per_key();
   }
   if (flag & HA_STATUS_ERRKEY)
@@ -6958,7 +6862,7 @@ ha_ndbcluster::flush_bulk_insert(bool al
     THD *thd= table->in_use;
     thd->transaction.all.modified_non_trans_table=
       thd->transaction.stmt.modified_non_trans_table= TRUE;
-    if (execute_commit(m_thd_ndb, trans, m_thd_ndb->m_force_send,
+    if (execute_commit(thd, m_thd_ndb, trans, m_thd_ndb->m_force_send,
                        m_ignore_no_key) != 0)
     {
       no_uncommitted_rows_execute_failure();
@@ -7728,9 +7632,10 @@ int ndbcluster_commit(handlerton *hton,
 
   if (thd->slave_thread)
   {
-    if (!thd_ndb->m_conflict_fn_usage_count || !thd_ndb->m_unsent_bytes ||
+    if (!g_ndb_slave_state.current_conflict_defined_op_count ||
+        !thd_ndb->m_unsent_bytes ||
         !(res= execute_no_commit(thd_ndb, trans, TRUE)))
-      res= execute_commit(thd_ndb, trans, 1, TRUE);
+      res= execute_commit(thd, thd_ndb, trans, 1, TRUE);
 
     update_slave_api_stats(thd_ndb->ndb);
   }
@@ -7753,7 +7658,7 @@ int ndbcluster_commit(handlerton *hton,
       }
     }
     else
-      res= execute_commit(thd_ndb, trans, THDVAR(thd, force_send), FALSE);
+      res= execute_commit(thd, thd_ndb, trans, THDVAR(thd, force_send), FALSE);
   }
 
   if (res != 0)
@@ -7828,9 +7733,8 @@ static int ndbcluster_rollback(handlerto
     DBUG_RETURN(0);
   }
   thd_ndb->save_point_count= 0;
-  thd_ndb->m_max_violation_count= 0;
-  thd_ndb->m_old_violation_count= 0;
-  thd_ndb->m_conflict_fn_usage_count= 0;
+  if (thd->slave_thread)
+    g_ndb_slave_state.atTransactionAbort();
   thd_ndb->m_unsent_bytes= 0;
   thd_ndb->m_execute_count++;
   DBUG_PRINT("info", ("execute_count: %u", thd_ndb->m_execute_count));
@@ -8912,6 +8816,34 @@ int ha_ndbcluster::create(const char *na
                         create_info->comment.length);
   const NDB_Modifier * mod_nologging = table_modifiers.get("NOLOGGING");
 
+#ifdef HAVE_NDB_BINLOG
+  /* Read ndb_replication entry for this table, if any */
+  Uint32 binlog_flags;
+  const st_conflict_fn_def* conflict_fn= NULL;
+  st_conflict_fn_arg args[MAX_CONFLICT_ARGS];
+  Uint32 num_args = MAX_CONFLICT_ARGS;
+
+  int rep_read_rc= ndbcluster_get_binlog_replication_info(thd,
+                                                          ndb,
+                                                          m_dbname,
+                                                          m_tabname,
+                                                          ::server_id,
+                                                          form,
+                                                          &binlog_flags,
+                                                          &conflict_fn,
+                                                          args,
+                                                          &num_args);
+  if (rep_read_rc != 0)
+  {
+    DBUG_RETURN(rep_read_rc);
+  }
+
+  /* Reset database name */
+  ndb->setDatabaseName(m_dbname);
+
+  /* Use ndb_replication information as required */
+#endif
+
   if ((dict->beginSchemaTrans() == -1))
   {
     DBUG_PRINT("info", ("Failed to start schema transaction"));
@@ -9340,8 +9272,18 @@ cleanup_failed:
     {
 #ifdef HAVE_NDB_BINLOG
       if (share)
-        ndbcluster_read_binlog_replication(thd, ndb, share, m_table,
-                                           ::server_id, form, TRUE);
+      {
+        /* Set the Binlogging information we retrieved above */
+        ndbcluster_apply_binlog_replication_info(thd,
+                                                 share,
+                                                 m_table,
+                                                 form,
+                                                 conflict_fn,
+                                                 args,
+                                                 num_args,
+                                                 TRUE, /* Do set binlog flags */
+                                                 binlog_flags);
+      }
 #endif
       String event_name(INJECTOR_EVENT_LEN);
       ndb_rep_event_name(&event_name, m_dbname, m_tabname,
@@ -10494,7 +10436,51 @@ int ha_ndbcluster::ndb_optimize_table(TH
 
 int ha_ndbcluster::analyze(THD* thd, HA_CHECK_OPT* check_opt)
 {
-  return update_stats(thd, 1);
+  int err;
+  if ((err= update_stats(thd, 1)) != 0)
+    return err;
+  const bool index_stat_enable= THDVAR(NULL, index_stat_enable) &&
+                                THDVAR(thd, index_stat_enable);
+  if (index_stat_enable)
+  {
+    if ((err= analyze_index(thd)) != 0)
+      return err;
+  }
+  return 0;
+}
+
+int
+ha_ndbcluster::analyze_index(THD *thd)
+{
+  DBUG_ENTER("ha_ndbcluster::analyze_index");
+
+  Thd_ndb *thd_ndb= get_thd_ndb(thd);
+  Ndb *ndb= thd_ndb->ndb;
+
+  uint inx_list[MAX_INDEXES];
+  uint inx_count= 0;
+
+  uint inx;
+  for (inx= 0; inx < table_share->keys; inx++)
+  {
+    NDB_INDEX_TYPE idx_type= get_index_type(inx);  
+
+    if ((idx_type == PRIMARY_KEY_ORDERED_INDEX ||
+         idx_type == UNIQUE_ORDERED_INDEX ||
+         idx_type == ORDERED_INDEX))
+    {
+      if (inx_count < MAX_INDEXES)
+        inx_list[inx_count++]= inx;
+    }
+  }
+
+  if (inx_count != 0)
+  {
+    int err= ndb_index_stat_analyze(ndb, inx_list, inx_count);
+    if (err != 0)
+      DBUG_RETURN(err);
+  }
+  DBUG_RETURN(0);
 }
 
 /*
@@ -11499,6 +11485,14 @@ static int ndbcluster_init(void *p)
   pthread_cond_init(&COND_ndb_util_ready, NULL);
   pthread_cond_init(&COND_ndb_setup_complete, NULL);
   ndb_util_thread_running= -1;
+  pthread_mutex_init(&LOCK_ndb_index_stat_thread, MY_MUTEX_INIT_FAST);
+  pthread_cond_init(&COND_ndb_index_stat_thread, NULL);
+  pthread_cond_init(&COND_ndb_index_stat_ready, NULL);
+  pthread_mutex_init(&ndb_index_stat_glob_mutex, MY_MUTEX_INIT_FAST);
+  pthread_mutex_init(&ndb_index_stat_list_mutex, MY_MUTEX_INIT_FAST);
+  pthread_mutex_init(&ndb_index_stat_stat_mutex, MY_MUTEX_INIT_FAST);
+  pthread_cond_init(&ndb_index_stat_stat_cond, NULL);
+  ndb_index_stat_thread_running= -1;
   ndbcluster_terminating= 0;
   ndb_dictionary_is_mysqld= 1;
   ndb_setup_complete= 0;
@@ -11596,6 +11590,44 @@ static int ndbcluster_init(void *p)
     goto ndbcluster_init_error;
   }
 
+  // Create index statistics thread
+  pthread_t tmp2;
+  if (pthread_create(&tmp2, &connection_attrib, ndb_index_stat_thread_func, 0))
+  {
+    DBUG_PRINT("error", ("Could not create ndb index statistics thread"));
+    hash_free(&ndbcluster_open_tables);
+    pthread_mutex_destroy(&ndbcluster_mutex);
+    pthread_mutex_destroy(&LOCK_ndb_index_stat_thread);
+    pthread_cond_destroy(&COND_ndb_index_stat_thread);
+    pthread_cond_destroy(&COND_ndb_index_stat_ready);
+    pthread_mutex_destroy(&ndb_index_stat_glob_mutex);
+    pthread_mutex_destroy(&ndb_index_stat_list_mutex);
+    pthread_mutex_destroy(&ndb_index_stat_stat_mutex);
+    pthread_cond_destroy(&ndb_index_stat_stat_cond);
+    goto ndbcluster_init_error;
+  }
+
+  /* Wait for the index statistics thread to start */
+  pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
+  while (ndb_index_stat_thread_running < 0)
+    pthread_cond_wait(&COND_ndb_index_stat_ready, &LOCK_ndb_index_stat_thread);
+  pthread_mutex_unlock(&LOCK_ndb_index_stat_thread);
+  
+  if (!ndb_index_stat_thread_running)
+  {
+    DBUG_PRINT("error", ("ndb index statistics thread exited prematurely"));
+    hash_free(&ndbcluster_open_tables);
+    pthread_mutex_destroy(&ndbcluster_mutex);
+    pthread_mutex_destroy(&LOCK_ndb_index_stat_thread);
+    pthread_cond_destroy(&COND_ndb_index_stat_thread);
+    pthread_cond_destroy(&COND_ndb_index_stat_ready);
+    pthread_mutex_destroy(&ndb_index_stat_glob_mutex);
+    pthread_mutex_destroy(&ndb_index_stat_list_mutex);
+    pthread_mutex_destroy(&ndb_index_stat_stat_mutex);
+    pthread_cond_destroy(&ndb_index_stat_stat_cond);
+    goto ndbcluster_init_error;
+  }
+
 #ifndef NDB_NO_WAIT_SETUP
   ndb_wait_setup_func= ndb_wait_setup_func_impl;
 #endif
@@ -11623,6 +11655,15 @@ static int ndbcluster_end(handlerton *ht
     DBUG_RETURN(0);
   ndbcluster_inited= 0;
 
+  /* wait for index stat thread to finish */
+  sql_print_information("Stopping Cluster Index Statistics thread");
+  pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
+  ndbcluster_terminating= 1;
+  pthread_cond_signal(&COND_ndb_index_stat_thread);
+  while (ndb_index_stat_thread_running > 0)
+    pthread_cond_wait(&COND_ndb_index_stat_ready, &LOCK_ndb_index_stat_thread);
+  pthread_mutex_unlock(&LOCK_ndb_index_stat_thread);
+
   /* wait for util and binlog thread to finish */
   ndbcluster_binlog_end(NULL);
 
@@ -11642,6 +11683,7 @@ static int ndbcluster_end(handlerton *ht
   }
   my_hash_free(&ndbcluster_open_tables);
 
+  ndb_index_stat_end();
   ndbcluster_disconnect();
 
   // cleanup ndb interface
@@ -11652,6 +11694,9 @@ static int ndbcluster_end(handlerton *ht
   pthread_cond_destroy(&COND_ndb_util_thread);
   pthread_cond_destroy(&COND_ndb_util_ready);
   pthread_cond_destroy(&COND_ndb_setup_complete);
+  pthread_mutex_destroy(&LOCK_ndb_index_stat_thread);
+  pthread_cond_destroy(&COND_ndb_index_stat_thread);
+  pthread_cond_destroy(&COND_ndb_index_stat_ready);
   ndbcluster_global_schema_lock_deinit();
   DBUG_RETURN(0);
 }
@@ -11761,6 +11806,12 @@ void ha_ndbcluster::set_tabname(const ch
 }
 
 
+/*
+  If there are no stored stats, should we do a tree-dive on all db
+  nodes.  The result is fairly good but does mean a round-trip.
+ */
+static const bool g_ndb_records_in_range_tree_dive= false;
+
 /* Determine roughly how many records are in the range specified */
 ha_rows 
 ha_ndbcluster::records_in_range(uint inx, key_range *min_key,
@@ -11786,92 +11837,81 @@ ha_ndbcluster::records_in_range(uint inx
         memcmp(min_key->key, max_key->key, key_length)==0)))
     DBUG_RETURN(1);
   
+  // XXX why this if
   if ((idx_type == PRIMARY_KEY_ORDERED_INDEX ||
        idx_type == UNIQUE_ORDERED_INDEX ||
-       idx_type == ORDERED_INDEX) &&
-      m_index[inx].index_stat != NULL) // --ndb-index-stat-enable=1
+       idx_type == ORDERED_INDEX))
   {
     THD *thd= current_thd;
-    NDB_INDEX_DATA& d=m_index[inx];
-    const NDBINDEX* index= d.index;
-    Ndb *ndb= get_ndb(thd);
-    NdbTransaction* active_trans= m_thd_ndb ? m_thd_ndb->trans : 0;
-    NdbTransaction* trans=NULL;
-    int res=0;
-    Uint64 rows;
+    const bool index_stat_enable= THDVAR(NULL, index_stat_enable) &&
+                                  THDVAR(thd, index_stat_enable);
 
-    do
+    if (index_stat_enable)
     {
-      // We must provide approx table rows
-      Uint64 table_rows=0;
-      if (stats.records != ~(ha_rows)0 && stats.records != 0)
+      ha_rows rows= HA_POS_ERROR;
+      int err= ndb_index_stat_get_rir(inx, min_key, max_key, &rows);
+      if (err == 0)
       {
-        table_rows = stats.records;
-        DBUG_PRINT("info", ("use info->records: %lu", (ulong) table_rows));
-      }
-      else
-      {
-        if (update_stats(thd, 1))
-          break;
-        table_rows= stats.records;
-        DBUG_PRINT("info", ("use db row_count: %lu", (ulong) table_rows));
-        if (table_rows == 0) {
-          // Problem if autocommit=0
-#ifdef ndb_get_table_statistics_uses_active_trans
-          rows=0;
-          break;
-#endif
-        }
+        /**
+         * optmizer thinks that all values < 2 are exact...but
+         * but we don't provide exact statistics
+         */
+        if (rows < 2)
+          rows = 2;
+        DBUG_RETURN(rows);
       }
+      /*fall through*/
+    }
 
-      /*
-        Query the index statistics for our range.
-      */
-      if ((trans=active_trans) == NULL || 
-	  trans->commitStatus() != NdbTransaction::Started)
+    if (g_ndb_records_in_range_tree_dive)
+    {
+      NDB_INDEX_DATA& d=m_index[inx];
+      const NDBINDEX* index= d.index;
+      Ndb *ndb= get_ndb(thd);
+      NdbTransaction* active_trans= m_thd_ndb ? m_thd_ndb->trans : 0;
+      NdbTransaction* trans=NULL;
+      int res=0;
+      Uint64 rows;
+
+      do
       {
-        DBUG_PRINT("info", ("no active trans"));
-        if (! (trans=ndb->startTransaction()))
-          ERR_BREAK(ndb->getNdbError(), res);
-      }
-      
-      /* Create an IndexBound struct for the keys */
-      NdbIndexScanOperation::IndexBound ib;
-      compute_index_bounds(ib,
-                           key_info,
-                           min_key, 
-                           max_key);
-
-      ib.range_no= 0;
-
-      // Decide if db should be contacted
-      int flags=0;
-      if (d.index_stat_query_count < d.index_stat_cache_entries ||
-          (d.index_stat_update_freq != 0 &&
-           d.index_stat_query_count % d.index_stat_update_freq == 0))
-      {
-        DBUG_PRINT("info", ("force stat from db"));
-        flags|=NdbIndexStat::RR_UseDb;
-      }
-      if (d.index_stat->records_in_range(index, 
-                                         trans, 
-                                         d.ndb_record_key,
-                                         m_ndb_record,
-                                         &ib, 
-                                         table_rows, 
-                                         &rows, 
-                                         flags) == -1)
-        ERR_BREAK(d.index_stat->getNdbError(), res);
-      d.index_stat_query_count++;
-    } while (0);
-
-    if (trans != active_trans && rows == 0)
-      rows = 1;
-    if (trans != active_trans && trans != NULL)
-      ndb->closeTransaction(trans);
-    if (res != 0)
-      DBUG_RETURN(HA_POS_ERROR);
-    DBUG_RETURN(rows);
+        if ((trans=active_trans) == NULL || 
+            trans->commitStatus() != NdbTransaction::Started)
+        {
+          DBUG_PRINT("info", ("no active trans"));
+          if (! (trans=ndb->startTransaction()))
+            ERR_BREAK(ndb->getNdbError(), res);
+        }
+        
+        /* Create an IndexBound struct for the keys */
+        NdbIndexScanOperation::IndexBound ib;
+        compute_index_bounds(ib,
+                             key_info,
+                             min_key, 
+                             max_key);
+
+        ib.range_no= 0;
+
+        NdbIndexStat is;
+        if (is.records_in_range(index, 
+                                trans, 
+                                d.ndb_record_key,
+                                m_ndb_record,
+                                &ib, 
+                                0, 
+                                &rows, 
+                                0) == -1)
+          ERR_BREAK(is.getNdbError(), res);
+      } while (0);
+
+      if (trans != active_trans && rows == 0)
+        rows = 1;
+      if (trans != active_trans && trans != NULL)
+        ndb->closeTransaction(trans);
+      if (res == 0)
+        DBUG_RETURN(rows);
+      /*fall through*/
+    }
   }
 
   /* Use simple heuristics to estimate fraction
@@ -12751,6 +12791,8 @@ void ndbcluster_real_free_share(NDB_SHAR
   if (opt_ndb_extra_logging > 9)
     sql_print_information ("ndbcluster_real_free_share: %s use_count: %u", (*share)->key, (*share)->use_count);
 
+  ndb_index_stat_free(*share);
+
   my_hash_delete(&ndbcluster_open_tables, (uchar*) *share);
   thr_lock_delete(&(*share)->lock);
   pthread_mutex_destroy(&(*share)->mutex);
@@ -16592,6 +16634,7 @@ SHOW_VAR ndb_status_variables_export[]=
   {"Ndb",          (char*) &ndb_status_injector_variables, SHOW_ARRAY},
   {"Ndb",          (char*) &ndb_status_slave_variables,    SHOW_ARRAY},
   {"Ndb",          (char*) &show_ndb_server_api_stats,     SHOW_FUNC},
+  {"Ndb_index_stat", (char*) &ndb_status_index_stat_variables, SHOW_ARRAY},
   {NullS, NullS, SHOW_LONG}
 };
 
@@ -16670,6 +16713,31 @@ static MYSQL_SYSVAR_UINT(
   0                                  /* block */
 );
 
+/* should be in index_stat.h */
+
+extern int
+ndb_index_stat_option_check(MYSQL_THD,
+                            struct st_mysql_sys_var *var,
+                            void *save,
+                            struct st_mysql_value *value);
+extern void
+ndb_index_stat_option_update(MYSQL_THD,
+                             struct st_mysql_sys_var *var,
+                             void *var_ptr,
+                             const void *save);
+
+extern char ndb_index_stat_option_buf[];
+
+static MYSQL_SYSVAR_STR(
+  index_stat_option,                /* name */
+  opt_ndb_index_stat_option,        /* var */
+  PLUGIN_VAR_RQCMDARG,
+  "Comma-separated tunable options for ndb index statistics",
+  ndb_index_stat_option_check,      /* check func. */
+  ndb_index_stat_option_update,     /* update func. */
+  ndb_index_stat_option_buf
+);
+
 
 ulong opt_ndb_report_thresh_binlog_epoch_slip;
 static MYSQL_SYSVAR_ULONG(
@@ -16863,6 +16931,7 @@ static struct st_mysql_sys_var* system_v
   MYSQL_SYSVAR(batch_size),
   MYSQL_SYSVAR(optimization_delay),
   MYSQL_SYSVAR(index_stat_enable),
+  MYSQL_SYSVAR(index_stat_option),
   MYSQL_SYSVAR(index_stat_cache_entries),
   MYSQL_SYSVAR(index_stat_update_freq),
   MYSQL_SYSVAR(table_no_logging),

=== modified file 'sql/ha_ndbcluster.h'
--- a/sql/ha_ndbcluster.h	2011-06-15 10:40:26 +0000
+++ b/sql/ha_ndbcluster.h	2011-06-17 12:41:11 +0000
@@ -77,12 +77,6 @@ typedef struct ndb_index_data {
   const NdbDictionary::Index *unique_index;
   unsigned char *unique_index_attrid_map;
   bool null_in_unique_index;
-  // In this version stats are not shared between threads
-  NdbIndexStat* index_stat;
-  uint index_stat_cache_entries;
-  // Simple counter mechanism to decide when to connect to db
-  uint index_stat_update_freq;
-  uint index_stat_query_count;
   /*
     In mysqld, keys and rows are stored differently (using KEY_PART_INFO for
     keys and Field for rows).
@@ -132,30 +126,98 @@ typedef enum {
   NSS_ALTERED 
 } NDB_SHARE_STATE;
 
-#ifdef HAVE_NDB_BINLOG
 enum enum_conflict_fn_type
 {
   CFT_NDB_UNDEF = 0
   ,CFT_NDB_MAX
   ,CFT_NDB_OLD
   ,CFT_NDB_MAX_DEL_WIN
+  ,CFT_NUMBER_OF_CFTS /* End marker */
+};
+
+#ifdef HAVE_NDB_BINLOG
+static const Uint32 MAX_CONFLICT_ARGS= 8;
+
+enum enum_conflict_fn_arg_type
+{
+  CFAT_END
+  ,CFAT_COLUMN_NAME
+};
+
+struct st_conflict_fn_arg
+{
+  enum_conflict_fn_arg_type type;
+  const char *ptr;
+  uint32 len;
+  uint32 fieldno; // CFAT_COLUMN_NAME
+};
+
+struct st_conflict_fn_arg_def
+{
+  enum enum_conflict_fn_arg_type arg_type;
+  bool optional;
+};
+
+/* What type of operation was issued */
+enum enum_conflicting_op_type
+{                /* NdbApi          */
+  WRITE_ROW,     /* insert (!write) */
+  UPDATE_ROW,    /* update          */
+  DELETE_ROW     /* delete          */
+};
+
+/*
+  prepare_detect_func
+
+  Type of function used to prepare for conflict detection on
+  an NdbApi operation
+*/
+typedef int (* prepare_detect_func) (struct st_ndbcluster_conflict_fn_share* cfn_share,
+                                     enum_conflicting_op_type op_type,
+                                     const uchar* old_data,
+                                     const uchar* new_data,
+                                     const MY_BITMAP* write_set,
+                                     NdbInterpretedCode* code);
+
+struct st_conflict_fn_def
+{
+  const char *name;
+  enum_conflict_fn_type type;
+  const st_conflict_fn_arg_def* arg_defs;
+  prepare_detect_func prep_func;
+};
+
+/* What sort of conflict was found */
+enum enum_conflict_cause
+{
+  ROW_ALREADY_EXISTS,
+  ROW_DOES_NOT_EXIST,
+  ROW_IN_CONFLICT
 };
 
 /* NdbOperation custom data which points out handler and record. */
 struct Ndb_exceptions_data {
   struct st_ndbcluster_share *share;
+  const NdbRecord* key_rec;
   const uchar* row;
+  enum_conflicting_op_type op_type;
+};
+
+enum enum_conflict_fn_flags
+{
+  CFF_NONE = 0
 };
 
 typedef struct st_ndbcluster_conflict_fn_share {
-  enum_conflict_fn_type m_resolve_cft;
+  const st_conflict_fn_def* m_conflict_fn;
 
   /* info about original table */
   uint8 m_pk_cols;
   uint8 m_resolve_column;
   uint8 m_resolve_size;
-  uint8 unused;
+  uint8 m_flags;
   uint16 m_offset[16];
+  uint16 m_resolve_offset;
 
   const NdbDictionary::Table *m_ex_tab;
   uint32 m_count;
@@ -189,6 +251,7 @@ typedef struct st_ndbcluster_share {
   char *table_name;
   Ndb::TupleIdRange tuple_id_range;
   struct Ndb_statistics stat;
+  struct Ndb_index_stat *index_stat_list;
   bool util_thread; // if opened by util thread
   uint32 connect_count;
   uint32 flags;
@@ -280,6 +343,26 @@ inline my_bool get_binlog_use_update(NDB
 { return (share->flags & NSF_BINLOG_USE_UPDATE) != 0; }
 
 /*
+  State associated with the Slave thread
+  (From the Ndb handler's point of view)
+*/
+struct st_ndb_slave_state
+{
+  /* Counter values for current slave transaction */
+  Uint32 current_conflict_defined_op_count;
+  Uint32 current_violation_count[CFT_NUMBER_OF_CFTS];
+
+  /* Cumulative counter values */
+  Uint64 total_violation_count[CFT_NUMBER_OF_CFTS];
+
+  /* Methods */
+  void atTransactionCommit();
+  void atTransactionAbort();
+
+  st_ndb_slave_state();
+};
+
+/*
   Place holder for ha_ndbcluster thread specific data
 */
 
@@ -353,9 +436,6 @@ class Thd_ndb
   uint m_batch_size;
 
   uint m_execute_count;
-  uint m_max_violation_count;
-  uint m_old_violation_count;
-  uint m_conflict_fn_usage_count;
 
   uint m_scan_count;
   uint m_pruned_scan_count;
@@ -403,6 +483,28 @@ class Thd_ndb
   void release_query_defs();
 };
 
+struct st_ndb_status {
+  st_ndb_status() { bzero(this, sizeof(struct st_ndb_status)); }
+  long cluster_node_id;
+  const char * connected_host;
+  long connected_port;
+  long number_of_replicas;
+  long number_of_data_nodes;
+  long number_of_ready_data_nodes;
+  long connect_count;
+  long execute_count;
+  long scan_count;
+  long pruned_scan_count;
+  long sorted_scan_count;
+  long pushed_queries_defined;
+  long pushed_queries_dropped;
+  long pushed_queries_executed;
+  long pushed_reads;
+  long transaction_no_hint_count[MAX_NDB_NODES];
+  long transaction_hint_count[MAX_NDB_NODES];
+  long long api_client_stats[Ndb::NumClientStatistics];
+};
+
 int ndbcluster_commit(handlerton *hton, THD *thd, bool all);
 class ha_ndbcluster: public handler
 {
@@ -421,6 +523,7 @@ class ha_ndbcluster: public handler
 
   int optimize(THD* thd, HA_CHECK_OPT* check_opt);
   int analyze(THD* thd, HA_CHECK_OPT* check_opt);
+  int analyze_index(THD* thd);
 
   int write_row(uchar *buf);
   int update_row(const uchar *old_data, uchar *new_data);
@@ -641,20 +744,12 @@ static void set_tabname(const char *path
 
 private:
 #ifdef HAVE_NDB_BINLOG
-  int delete_row_conflict_fn(enum_conflict_fn_type cft,
-                             const uchar *old_data,
-                             NdbInterpretedCode *);
-  int write_row_conflict_fn(enum_conflict_fn_type cft,
-                            uchar *data,
-                            NdbInterpretedCode *);
-  int update_row_conflict_fn(enum_conflict_fn_type cft,
-                             const uchar *old_data,
-                             uchar *new_data,
-                             NdbInterpretedCode *);
-  int row_conflict_fn_max(const uchar *new_data,
-                          NdbInterpretedCode *);
-  int row_conflict_fn_old(const uchar *old_data,
-                          NdbInterpretedCode *);
+  int prepare_conflict_detection(enum_conflicting_op_type op_type,
+                                 const NdbRecord* key_rec,
+                                 const uchar* old_data,
+                                 const uchar* new_data,
+                                 NdbInterpretedCode* code,
+                                 NdbOperation::OperationOptions* options);
 #endif
   void setup_key_ref_for_ndb_record(const NdbRecord **key_rec,
                                     const uchar **key_row,
@@ -818,6 +913,21 @@ private:
   void no_uncommitted_rows_update(int);
   void no_uncommitted_rows_reset(THD *);
 
+  /* Ordered index statistics v4 */
+  int ndb_index_stat_get_rir(uint inx,
+                             key_range *min_key,
+                             key_range *max_key,
+                             ha_rows *rows_out);
+  int ndb_index_stat_set_rpk(uint inx);
+  int ndb_index_stat_wait(Ndb_index_stat *st,
+                          uint sample_version);
+  int ndb_index_stat_query(uint inx,
+                           const key_range *min_key,
+                           const key_range *max_key,
+                           NdbIndexStat::Stat& stat);
+  int ndb_index_stat_analyze(Ndb *ndb,
+                             uint *inx_list,
+                             uint inx_count);
 
   NdbTransaction *start_transaction_part_id(uint32 part_id, int &error);
   inline NdbTransaction *get_transaction_part_id(uint32 part_id, int &error)
@@ -973,3 +1083,5 @@ static const int ndbcluster_hton_name_le
 extern int ndbcluster_terminating;
 extern int ndb_util_thread_running;
 extern pthread_cond_t COND_ndb_util_ready;
+extern int ndb_index_stat_thread_running;
+extern pthread_cond_t COND_ndb_index_stat_ready;

=== modified file 'sql/ha_ndbcluster_binlog.cc'
--- a/sql/ha_ndbcluster_binlog.cc	2011-03-22 08:32:32 +0000
+++ b/sql/ha_ndbcluster_binlog.cc	2011-06-16 18:16:01 +0000
@@ -831,6 +831,24 @@ int ndbcluster_binlog_end(THD *thd)
     pthread_mutex_unlock(&LOCK_ndb_util_thread);
   }
 
+  if (ndb_index_stat_thread_running > 0)
+  {
+    /*
+      Index stats thread blindly imitates util thread.  Following actually
+      fixes some "[Warning] Plugin 'ndbcluster' will be forced to shutdown".
+    */
+    sql_print_information("Stopping Cluster Index Stats thread");
+    pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
+    /* Ensure mutex are not freed if ndb_cluster_end is running at same time */
+    ndb_index_stat_thread_running++;
+    ndbcluster_terminating= 1;
+    pthread_cond_signal(&COND_ndb_index_stat_thread);
+    while (ndb_index_stat_thread_running > 1)
+      pthread_cond_wait(&COND_ndb_index_stat_ready, &LOCK_ndb_index_stat_thread);
+    ndb_index_stat_thread_running--;
+    pthread_mutex_unlock(&LOCK_ndb_index_stat_thread);
+  }
+
   if (ndbcluster_binlog_inited)
   {
     ndbcluster_binlog_inited= 0;
@@ -3741,18 +3759,13 @@ inline void slave_reset_conflict_fn(NDB_
   }
 }
 
-static int
-slave_set_resolve_fn(THD *thd, NDB_SHARE *share,
-                     const NDBTAB *ndbtab, uint field_index,
-                     enum_conflict_fn_type type, TABLE *table)
+static uint
+slave_check_resolve_col_type(const NDBTAB *ndbtab,
+                             uint field_index)
 {
-  DBUG_ENTER("slave_set_resolve_fn");
-
-  Thd_ndb *thd_ndb= get_thd_ndb(thd);
-  Ndb *ndb= thd_ndb->ndb;
-  NDBDICT *dict= ndb->getDictionary();
+  DBUG_ENTER("slave_check_resolve_col_type");
   const NDBCOL *c= ndbtab->getColumn(field_index);
-  uint sz;
+  uint sz= 0;
   switch (c->getType())
   {
   case  NDBCOL::Unsigned:
@@ -3768,11 +3781,24 @@ slave_set_resolve_fn(THD *thd, NDB_SHARE
   default:
     DBUG_PRINT("info", ("resolve column %u has wrong type",
                         field_index));
-    slave_reset_conflict_fn(share);
-    DBUG_RETURN(-1);
     break;
   }
+  DBUG_RETURN(sz);
+}
 
+static int
+slave_set_resolve_fn(THD *thd, NDB_SHARE *share,
+                     const NDBTAB *ndbtab, uint field_index,
+                     uint resolve_col_sz,
+                     const st_conflict_fn_def* conflict_fn,
+                     TABLE *table,
+                     uint8 flags)
+{
+  DBUG_ENTER("slave_set_resolve_fn");
+
+  Thd_ndb *thd_ndb= get_thd_ndb(thd);
+  Ndb *ndb= thd_ndb->ndb;
+  NDBDICT *dict= ndb->getDictionary();
   NDB_CONFLICT_FN_SHARE *cfn_share= share->m_cfn_share;
   if (cfn_share == NULL)
   {
@@ -3780,9 +3806,14 @@ slave_set_resolve_fn(THD *thd, NDB_SHARE
       alloc_root(&share->mem_root, sizeof(NDB_CONFLICT_FN_SHARE));
     slave_reset_conflict_fn(share);
   }
-  cfn_share->m_resolve_size= sz;
+  cfn_share->m_conflict_fn= conflict_fn;
+
+  /* Calculate resolve col stuff (if relevant) */
+  cfn_share->m_resolve_size= resolve_col_sz;
   cfn_share->m_resolve_column= field_index;
-  cfn_share->m_resolve_cft= type;
+  cfn_share->m_resolve_offset= (uint16)(table->field[field_index]->ptr -
+                                        table->record[0]);
+  cfn_share->m_flags = flags;
 
   {
     /* get exceptions table */
@@ -3841,7 +3872,10 @@ slave_set_resolve_fn(THD *thd, NDB_SHARE
           cfn_share->m_pk_cols= nkey;
           ndbtab_g.release();
           if (opt_ndb_extra_logging)
-            sql_print_information("NDB Slave: log exceptions to %s",
+            sql_print_information("NDB Slave: Table %s.%s logging exceptions to %s.%s",
+                                  table->s->db.str,
+                                  table->s->table_name.str,
+                                  table->s->db.str,
                                   ex_tab_name);
         }
         else
@@ -3858,76 +3892,307 @@ slave_set_resolve_fn(THD *thd, NDB_SHARE
   DBUG_RETURN(0);
 }
 
-enum enum_conflict_fn_arg_type
+/**
+  CFT_NDB_OLD
+
+  To perform conflict detection, an interpreted program is used to read
+  the timestamp stored locally and compare to what was on the master.
+  If timestamp is not equal, an error for this operation (9998) will be raised,
+  and new row will not be applied. The error codes for the operations will
+  be checked on return.  For this to work is is vital that the operation
+  is run with ignore error option.
+
+  As an independent feature, phase 2 also saves the
+  conflicts into the table's exceptions table.
+*/
+int
+row_conflict_fn_old(st_ndbcluster_conflict_fn_share* cfn_share,
+                    enum_conflicting_op_type op_type,
+                    const uchar* old_data,
+                    const uchar* new_data,
+                    const MY_BITMAP* write_set,
+                    NdbInterpretedCode* code)
 {
-  CFAT_END
-  ,CFAT_COLUMN_NAME
-};
-struct st_conflict_fn_arg
+  DBUG_ENTER("row_conflict_fn_old");
+  uint32 resolve_column= cfn_share->m_resolve_column;
+  uint32 resolve_size= cfn_share->m_resolve_size;
+  const uchar* field_ptr = old_data + cfn_share->m_resolve_offset;
+
+  assert((resolve_size == 4) || (resolve_size == 8));
+
+  if (unlikely(!bitmap_is_set(write_set, resolve_column)))
+  {
+    sql_print_information("NDB Slave: missing data for %s",
+                          cfn_share->m_conflict_fn->name);
+    DBUG_RETURN(1);
+  }
+
+  const uint label_0= 0;
+  const Uint32 RegOldValue= 1, RegCurrentValue= 2;
+  int r;
+
+  DBUG_PRINT("info",
+             ("Adding interpreted filter, existing value must eq event old value"));
+  /*
+   * read old value from record
+   */
+  union {
+    uint32 old_value_32;
+    uint64 old_value_64;
+  };
+  {
+    if (resolve_size == 4)
+    {
+      memcpy(&old_value_32, field_ptr, resolve_size);
+      DBUG_PRINT("info", ("  old_value_32: %u", old_value_32));
+    }
+    else
+    {
+      memcpy(&old_value_64, field_ptr, resolve_size);
+      DBUG_PRINT("info", ("  old_value_64: %llu",
+                          (unsigned long long) old_value_64));
+    }
+  }
+
+  /*
+   * Load registers RegOldValue and RegCurrentValue
+   */
+  if (resolve_size == 4)
+    r= code->load_const_u32(RegOldValue, old_value_32);
+  else
+    r= code->load_const_u64(RegOldValue, old_value_64);
+  DBUG_ASSERT(r == 0);
+  r= code->read_attr(RegCurrentValue, resolve_column);
+  DBUG_ASSERT(r == 0);
+  /*
+   * if RegOldValue == RegCurrentValue goto label_0
+   * else raise error for this row
+   */
+  r= code->branch_eq(RegOldValue, RegCurrentValue, label_0);
+  DBUG_ASSERT(r == 0);
+  r= code->interpret_exit_nok(error_conflict_fn_violation);
+  DBUG_ASSERT(r == 0);
+  r= code->def_label(label_0);
+  DBUG_ASSERT(r == 0);
+  r= code->interpret_exit_ok();
+  DBUG_ASSERT(r == 0);
+  r= code->finalise();
+  DBUG_ASSERT(r == 0);
+  DBUG_RETURN(r);
+}
+
+int
+row_conflict_fn_max_update_only(st_ndbcluster_conflict_fn_share* cfn_share,
+                                enum_conflicting_op_type op_type,
+                                const uchar* old_data,
+                                const uchar* new_data,
+                                const MY_BITMAP* write_set,
+                                NdbInterpretedCode* code)
 {
-  enum_conflict_fn_arg_type type;
-  const char *ptr;
-  uint32 len;
-  uint32 fieldno; // CFAT_COLUMN_NAME
+  DBUG_ENTER("row_conflict_fn_max_update_only");
+  uint32 resolve_column= cfn_share->m_resolve_column;
+  uint32 resolve_size= cfn_share->m_resolve_size;
+  const uchar* field_ptr = new_data + cfn_share->m_resolve_offset;
+
+  assert((resolve_size == 4) || (resolve_size == 8));
+
+  if (unlikely(!bitmap_is_set(write_set, resolve_column)))
+  {
+    sql_print_information("NDB Slave: missing data for %s",
+                          cfn_share->m_conflict_fn->name);
+    DBUG_RETURN(1);
+  }
+
+  const uint label_0= 0;
+  const Uint32 RegNewValue= 1, RegCurrentValue= 2;
+  int r;
+
+  DBUG_PRINT("info",
+             ("Adding interpreted filter, existing value must be lt event new"));
+  /*
+   * read new value from record
+   */
+  union {
+    uint32 new_value_32;
+    uint64 new_value_64;
+  };
+  {
+    if (resolve_size == 4)
+    {
+      memcpy(&new_value_32, field_ptr, resolve_size);
+      DBUG_PRINT("info", ("  new_value_32: %u", new_value_32));
+    }
+    else
+    {
+      memcpy(&new_value_64, field_ptr, resolve_size);
+      DBUG_PRINT("info", ("  new_value_64: %llu",
+                          (unsigned long long) new_value_64));
+    }
+  }
+  /*
+   * Load registers RegNewValue and RegCurrentValue
+   */
+  if (resolve_size == 4)
+    r= code->load_const_u32(RegNewValue, new_value_32);
+  else
+    r= code->load_const_u64(RegNewValue, new_value_64);
+  DBUG_ASSERT(r == 0);
+  r= code->read_attr(RegCurrentValue, resolve_column);
+  DBUG_ASSERT(r == 0);
+  /*
+   * if RegNewValue > RegCurrentValue goto label_0
+   * else raise error for this row
+   */
+  r= code->branch_gt(RegNewValue, RegCurrentValue, label_0);
+  DBUG_ASSERT(r == 0);
+  r= code->interpret_exit_nok(error_conflict_fn_violation);
+  DBUG_ASSERT(r == 0);
+  r= code->def_label(label_0);
+  DBUG_ASSERT(r == 0);
+  r= code->interpret_exit_ok();
+  DBUG_ASSERT(r == 0);
+  r= code->finalise();
+  DBUG_ASSERT(r == 0);
+  DBUG_RETURN(r);
+}
+
+/**
+  CFT_NDB_MAX
+
+  To perform conflict resolution, an interpreted program is used to read
+  the timestamp stored locally and compare to what is going to be applied.
+  If timestamp is lower, an error for this operation (9999) will be raised,
+  and new row will not be applied. The error codes for the operations will
+  be checked on return.  For this to work is is vital that the operation
+  is run with ignore error option.
+
+  Note that for delete, this algorithm reverts to the OLD algorithm.
+*/
+int
+row_conflict_fn_max(st_ndbcluster_conflict_fn_share* cfn_share,
+                    enum_conflicting_op_type op_type,
+                    const uchar* old_data,
+                    const uchar* new_data,
+                    const MY_BITMAP* write_set,
+                    NdbInterpretedCode* code)
+{
+  switch(op_type)
+  {
+  case WRITE_ROW:
+    abort();
+    return 1;
+  case UPDATE_ROW:
+    return row_conflict_fn_max_update_only(cfn_share,
+                                           op_type,
+                                           old_data,
+                                           new_data,
+                                           write_set,
+                                           code);
+  case DELETE_ROW:
+    /* Can't use max of new image, as there's no new image
+     * for DELETE
+     * Use OLD instead
+     */
+    return row_conflict_fn_old(cfn_share,
+                               op_type,
+                               old_data,
+                               new_data,
+                               write_set,
+                               code);
+  default:
+    abort();
+    return 1;
+  }
+}
+
+
+/**
+  CFT_NDB_MAX_DEL_WIN
+
+  To perform conflict resolution, an interpreted program is used to read
+  the timestamp stored locally and compare to what is going to be applied.
+  If timestamp is lower, an error for this operation (9999) will be raised,
+  and new row will not be applied. The error codes for the operations will
+  be checked on return.  For this to work is is vital that the operation
+  is run with ignore error option.
+
+  In this variant, replicated DELETEs alway succeed - no filter is added
+  to them.
+*/
+
+int
+row_conflict_fn_max_del_win(st_ndbcluster_conflict_fn_share* cfn_share,
+                            enum_conflicting_op_type op_type,
+                            const uchar* old_data,
+                            const uchar* new_data,
+                            const MY_BITMAP* write_set,
+                            NdbInterpretedCode* code)
+{
+  switch(op_type)
+  {
+  case WRITE_ROW:
+    abort();
+    return 1;
+  case UPDATE_ROW:
+    return row_conflict_fn_max_update_only(cfn_share,
+                                           op_type,
+                                           old_data,
+                                           new_data,
+                                           write_set,
+                                           code);
+  case DELETE_ROW:
+    /* This variant always lets a received DELETE_ROW
+     * succeed.
+     */
+    return 1;
+  default:
+    abort();
+    return 1;
+  }
 };
-struct st_conflict_fn_def
+
+static const st_conflict_fn_arg_def resolve_col_args[]=
 {
-  const char *name;
-  enum_conflict_fn_type type;
-  enum enum_conflict_fn_arg_type arg_type;
+  /* Arg type              Optional */
+  { CFAT_COLUMN_NAME,      false },
+  { CFAT_END,              false }
 };
-static struct st_conflict_fn_def conflict_fns[]=
+
+static const st_conflict_fn_def conflict_fns[]=
 {
-   { "NDB$MAX_DELETE_WIN", CFT_NDB_MAX_DEL_WIN, CFAT_COLUMN_NAME }
-  ,{ NULL,                 CFT_NDB_MAX_DEL_WIN, CFAT_END }
-  ,{ "NDB$MAX", CFT_NDB_MAX,   CFAT_COLUMN_NAME }
-  ,{ NULL,      CFT_NDB_MAX,   CFAT_END }
-  ,{ "NDB$OLD", CFT_NDB_OLD,   CFAT_COLUMN_NAME }
-  ,{ NULL,      CFT_NDB_OLD,   CFAT_END }
+  { "NDB$MAX_DELETE_WIN", CFT_NDB_MAX_DEL_WIN,
+    &resolve_col_args[0], row_conflict_fn_max_del_win },
+  { "NDB$MAX",            CFT_NDB_MAX,
+    &resolve_col_args[0], row_conflict_fn_max         },
+  { "NDB$OLD",            CFT_NDB_OLD,
+    &resolve_col_args[0], row_conflict_fn_old         },
 };
+
 static unsigned n_conflict_fns=
-sizeof(conflict_fns) / sizeof(struct st_conflict_fn_def);
+  sizeof(conflict_fns) / sizeof(struct st_conflict_fn_def);
 
-static int
-set_conflict_fn(THD *thd, NDB_SHARE *share,
-                const NDBTAB *ndbtab,
-                const NDBCOL *conflict_col,
-                char *conflict_fn,
-                char *msg, uint msg_len,
-                TABLE *table)
-{
-  DBUG_ENTER("set_conflict_fn");
-  uint len= 0;
-  switch (conflict_col->getArrayType())
-  {
-  case NDBCOL::ArrayTypeShortVar:
-    len= *(uchar*)conflict_fn;
-    conflict_fn++;
-    break;
-  case NDBCOL::ArrayTypeMediumVar:
-    len= uint2korr(conflict_fn);
-    conflict_fn+= 2;
-    break;
-  default:
-    break;
-  }
-  conflict_fn[len]= '\0';
-  const char *ptr= conflict_fn;
+
+int
+parse_conflict_fn_spec(const char* conflict_fn_spec,
+                       const st_conflict_fn_def** conflict_fn,
+                       st_conflict_fn_arg* args,
+                       Uint32* max_args,
+                       const TABLE* table,
+                       char *msg, uint msg_len)
+{
+  DBUG_ENTER("parse_conflict_fn_spec");
+
+  Uint32 no_args = 0;
+  const char *ptr= conflict_fn_spec;
   const char *error_str= "unknown conflict resolution function";
   /* remove whitespace */
   while (*ptr == ' ' && *ptr != '\0') ptr++;
 
-  const unsigned MAX_ARGS= 8;
-  unsigned no_args= 0;
-  struct st_conflict_fn_arg args[MAX_ARGS];
-
-  DBUG_PRINT("info", ("parsing %s", conflict_fn));
+  DBUG_PRINT("info", ("parsing %s", conflict_fn_spec));
 
   for (unsigned i= 0; i < n_conflict_fns; i++)
   {
-    struct st_conflict_fn_def &fn= conflict_fns[i];
-    if (fn.name == NULL)
-      continue;
+    const st_conflict_fn_def &fn= conflict_fns[i];
 
     uint len= strlen(fn.name);
     if (strncmp(ptr, fn.name, len))
@@ -3953,9 +4218,16 @@ set_conflict_fn(THD *thd, NDB_SHARE *sha
     /* find all arguments */
     for (;;)
     {
+      if (no_args >= *max_args)
+      {
+        error_str= "too many arguments";
+        DBUG_PRINT("info", ("parse error %s", error_str));
+        break;
+      }
+
       /* expected type */
       enum enum_conflict_fn_arg_type type=
-        conflict_fns[i+no_args].arg_type;
+        conflict_fns[i].arg_defs[no_args].arg_type;
 
       /* remove whitespace */
       while (*ptr == ' ' && *ptr != '\0') ptr++;
@@ -3968,16 +4240,30 @@ set_conflict_fn(THD *thd, NDB_SHARE *sha
       }
 
       /* arg */
+      /* Todo : Should support comma as an arg separator? */
       const char *start_arg= ptr;
       while (*ptr != ')' && *ptr != ' ' && *ptr != '\0') ptr++;
       const char *end_arg= ptr;
 
+      bool optional_arg = conflict_fns[i].arg_defs[no_args].optional;
       /* any arg given? */
       if (start_arg == end_arg)
       {
-        error_str= "missing function argument";
-        DBUG_PRINT("info", ("parse error %s", error_str));
-        break;
+        if (!optional_arg)
+        {
+          error_str= "missing function argument";
+          DBUG_PRINT("info", ("parse error %s", error_str));
+          break;
+        }
+        else
+        {
+          /* Arg was optional, and not present
+           * Must be at end of args, finish parsing
+           */
+          args[no_args].type= CFAT_END;
+          error_str= NULL;
+          break;
+        }
       }
 
       uint len= end_arg - start_arg;
@@ -3988,6 +4274,7 @@ set_conflict_fn(THD *thd, NDB_SHARE *sha
  
       DBUG_PRINT("info", ("found argument %s %u", start_arg, len));
 
+      bool arg_processing_error = false;
       switch (type)
       {
       case CFAT_COLUMN_NAME:
@@ -4012,6 +4299,8 @@ set_conflict_fn(THD *thd, NDB_SHARE *sha
         abort();
       }
 
+      if (arg_processing_error)
+        break;
       no_args++;
     }
 
@@ -4039,43 +4328,86 @@ set_conflict_fn(THD *thd, NDB_SHARE *sha
       break;
     }
 
-    /* setup the function */
-    switch (fn.type)
-    {
-    case CFT_NDB_MAX:
-    case CFT_NDB_OLD:
-    case CFT_NDB_MAX_DEL_WIN:
-      if (args[0].fieldno == (uint32)-1)
-        break;
-      if (slave_set_resolve_fn(thd, share, ndbtab, args[0].fieldno,
-                               fn.type, table))
-      {
-        /* wrong data type */
-        my_snprintf(msg, msg_len,
-                 "column '%s' has wrong datatype",
-                 table->s->field[args[0].fieldno]->field_name);
-        DBUG_PRINT("info", ("%s", msg));
-        DBUG_RETURN(-1);
-      }
-      if (opt_ndb_extra_logging)
-      {
-        sql_print_information("NDB Slave: conflict_fn %s on attribute %s",
-                              fn.name,
-                              table->s->field[args[0].fieldno]->field_name);
-      }
-      break;
-    case CFT_NDB_UNDEF:
-      abort();
-    }
+    /* Update ptrs to conflict fn + # of args */
+    *conflict_fn = &conflict_fns[i];
+    *max_args = no_args;
+
     DBUG_RETURN(0);
   }
   /* parse error */
   my_snprintf(msg, msg_len, "%s, %s at '%s'",
-           conflict_fn, error_str, ptr);
+           conflict_fn_spec, error_str, ptr);
   DBUG_PRINT("info", ("%s", msg));
   DBUG_RETURN(-1);
 }
 
+static int
+setup_conflict_fn(THD *thd, NDB_SHARE *share,
+                  const NDBTAB *ndbtab,
+                  char *msg, uint msg_len,
+                  TABLE *table,
+                  const st_conflict_fn_def* conflict_fn,
+                  const st_conflict_fn_arg* args,
+                  const Uint32 num_args)
+{
+  DBUG_ENTER("setup_conflict_fn");
+
+  /* setup the function */
+  switch (conflict_fn->type)
+  {
+  case CFT_NDB_MAX:
+  case CFT_NDB_OLD:
+  case CFT_NDB_MAX_DEL_WIN:
+  {
+    if (num_args != 1)
+    {
+      my_snprintf(msg, msg_len,
+                  "Incorrect arguments to conflict function");
+      DBUG_PRINT("info", ("%s", msg));
+      DBUG_RETURN(-1);
+    }
+
+    uint resolve_col_sz= 0;
+
+    if (0 == (resolve_col_sz =
+              slave_check_resolve_col_type(ndbtab, args[0].fieldno)))
+    {
+      /* wrong data type */
+      slave_reset_conflict_fn(share);
+      my_snprintf(msg, msg_len,
+                  "column '%s' has wrong datatype",
+                  table->s->field[args[0].fieldno]->field_name);
+      DBUG_PRINT("info", ("%s", msg));
+      DBUG_RETURN(-1);
+    }
+
+    if (slave_set_resolve_fn(thd, share, ndbtab,
+                             args[0].fieldno, resolve_col_sz,
+                             conflict_fn, table, CFF_NONE))
+    {
+      my_snprintf(msg, msg_len,
+                  "unable to setup conflict resolution using column '%s'",
+                  table->s->field[args[0].fieldno]->field_name);
+      DBUG_PRINT("info", ("%s", msg));
+      DBUG_RETURN(-1);
+    }
+    if (opt_ndb_extra_logging)
+    {
+       sql_print_information("NDB Slave: Table %s.%s using conflict_fn %s on attribute %s.",
+                             table->s->db.str,
+                             table->s->table_name.str,
+                             conflict_fn->name,
+                             table->s->field[args[0].fieldno]->field_name);
+    }
+    break;
+  }
+  case CFT_NUMBER_OF_CFTS:
+  case CFT_NDB_UNDEF:
+    abort();
+  }
+  DBUG_RETURN(0);
+}
+
 static const char *ndb_rep_db= NDB_REP_DB;
 static const char *ndb_replication_table= NDB_REPLICATION_TABLE;
 static const char *nrt_db= "db";
@@ -4083,45 +4415,30 @@ static const char *nrt_table_name= "tabl
 static const char *nrt_server_id= "server_id";
 static const char *nrt_binlog_type= "binlog_type";
 static const char *nrt_conflict_fn= "conflict_fn";
+
+/*
+   ndbcluster_read_replication_table
+
+   This function reads the information for the supplied table from
+   the mysql.ndb_replication table.
+   Where there is no information (or no table), defaults are
+   returned.
+*/
 int
-ndbcluster_read_binlog_replication(THD *thd, Ndb *ndb,
-                                   NDB_SHARE *share,
-                                   const NDBTAB *ndbtab,
-                                   uint server_id,
-                                   TABLE *table,
-                                   bool do_set_binlog_flags)
+ndbcluster_read_replication_table(THD *thd, Ndb *ndb,
+                                  const char* db,
+                                  const char* table_name,
+                                  uint server_id,
+                                  Uint32* binlog_flags,
+                                  char** conflict_fn_spec,
+                                  char* conflict_fn_buffer,
+                                  Uint32 conflict_fn_buffer_len)
 {
-  DBUG_ENTER("ndbcluster_read_binlog_replication");
-  const char *db= share->db;
-  const char *table_name= share->table_name;
+  DBUG_ENTER("ndbcluster_read_replication_table");
   NdbError ndberror;
   int error= 0;
   const char *error_str= "<none>";
 
-  /* Override for ndb_apply_status when logging */
-  if (opt_ndb_log_apply_status &&
-      do_set_binlog_flags)
-  {
-    if (strcmp(db, NDB_REP_DB) == 0 &&
-        strcmp(table_name, NDB_APPLY_TABLE) == 0)
-    {
-      /*
-        Ensure that we get all columns from ndb_apply_status updates
-        by forcing FULL event type
-        Also, ensure that ndb_apply_status events are always logged as 
-        WRITES.
-        This is needed so that received ndb_apply_status WRITES can be
-        cleanly propagated.
-      */
-      DBUG_PRINT("info", ("ndb_apply_status defaulting to FULL, USE_WRITE"));
-      sql_print_information("NDB : ndb-log-apply-status forcing "
-                            "%s.%s to FULL USE_WRITE",
-                            NDB_REP_DB, NDB_APPLY_TABLE);
-      set_binlog_flags(share, NBT_FULL);
-      DBUG_RETURN(0);
-    }
-  }
-
   ndb->setDatabaseName(ndb_rep_db);
   NDBDICT *dict= ndb->getDictionary();
   Ndb_table_guard ndbtab_g(dict, ndb_replication_table);
@@ -4131,8 +4448,8 @@ ndbcluster_read_binlog_replication(THD *
        dict->getNdbError().code == 4009))
   {
     DBUG_PRINT("info", ("No %s.%s table", ndb_rep_db, ndb_replication_table));
-    if (do_set_binlog_flags)
-      set_binlog_flags(share, NBT_DEFAULT);
+    *binlog_flags= NBT_DEFAULT;
+    *conflict_fn_spec= NULL;
     DBUG_RETURN(0);
   }
   const NDBCOL
@@ -4198,6 +4515,10 @@ ndbcluster_read_binlog_replication(THD *
     char *ndb_conflict_fn[2]= {ndb_conflict_fn_buf, ndb_conflict_fn_buf+sz};
     NdbOperation *op[2];
     uint32 i, id= 0;
+    /* Read generic row (server_id==0) and specific row (server_id == our id)
+     * from ndb_replication.
+     * Specific overrides generic, if present
+     */
     for (i= 0; i < 2; i++)
     {
       NdbOperation *_op;
@@ -4257,39 +4578,70 @@ ndbcluster_read_binlog_replication(THD *
     if (col_binlog_type_rec_attr[1] == NULL ||
         col_binlog_type_rec_attr[1]->isNULL())
     {
+      /* No specific value, use generic */
       col_binlog_type_rec_attr[1]= col_binlog_type_rec_attr[0];
       ndb_binlog_type[1]= ndb_binlog_type[0];
     }
     if (col_conflict_fn_rec_attr[1] == NULL ||
         col_conflict_fn_rec_attr[1]->isNULL())
     {
+      /* No specific value, use generic */
       col_conflict_fn_rec_attr[1]= col_conflict_fn_rec_attr[0];
       ndb_conflict_fn[1]= ndb_conflict_fn[0];
     }
 
-    if (do_set_binlog_flags)
+    if (col_binlog_type_rec_attr[1] == NULL ||
+        col_binlog_type_rec_attr[1]->isNULL())
     {
-      if (col_binlog_type_rec_attr[1] == NULL ||
-          col_binlog_type_rec_attr[1]->isNULL())
-        set_binlog_flags(share, NBT_DEFAULT);
-      else
-        set_binlog_flags(share, (enum Ndb_binlog_type) ndb_binlog_type[1]);
+      DBUG_PRINT("info", ("No binlog flag value, using default"));
+      /* No value */
+      *binlog_flags= NBT_DEFAULT;
+    }
+    else
+    {
+      DBUG_PRINT("info", ("Taking binlog flag value from the table"));
+      *binlog_flags= (enum Ndb_binlog_type) ndb_binlog_type[1];
+    }
+
+    if (col_conflict_fn_rec_attr[1] == NULL ||
+        col_conflict_fn_rec_attr[1]->isNULL())
+    {
+      /* No conflict function */
+      *conflict_fn_spec = NULL;
     }
-    if (table)
+    else
     {
-      if (col_conflict_fn_rec_attr[1] == NULL ||
-          col_conflict_fn_rec_attr[1]->isNULL())
-        slave_reset_conflict_fn(share); /* no conflict_fn */
-      else if (set_conflict_fn(thd, share, ndbtab,
-                               col_conflict_fn, ndb_conflict_fn[1],
-                               tmp_buf, sizeof(tmp_buf), table))
+      const char* conflict_fn = ndb_conflict_fn[1];
+      uint len= 0;
+      switch (col_conflict_fn->getArrayType())
+      {
+      case NDBCOL::ArrayTypeShortVar:
+        len= *(uchar*)conflict_fn;
+        conflict_fn++;
+        break;
+      case NDBCOL::ArrayTypeMediumVar:
+        len= uint2korr(conflict_fn);
+        conflict_fn+= 2;
+        break;
+      default:
+        abort();
+      }
+      if ((len + 1) > conflict_fn_buffer_len)
       {
-        error_str= tmp_buf;
-        error= 1;
         ndb->closeTransaction(trans);
+        error= -2;
+        error_str= "Conflict function specification too long.";
         goto err;
       }
+      memcpy(conflict_fn_buffer, conflict_fn, len);
+      conflict_fn_buffer[len] = '\0';
+      *conflict_fn_spec = conflict_fn_buffer;
     }
+
+    DBUG_PRINT("info", ("Retrieved Binlog flags : %u and function spec : %s",
+                        *binlog_flags, (*conflict_fn_spec != NULL ?*conflict_fn_spec:
+                                       "NULL")));
+
     ndb->closeTransaction(trans);
 
     DBUG_RETURN(0);
@@ -4298,14 +4650,7 @@ ndbcluster_read_binlog_replication(THD *
 err:
   DBUG_PRINT("info", ("error %d, error_str %s, ndberror.code %u",
                       error, error_str, ndberror.code));
-  if (error > 0)
-  {
-    push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
-                        ER_CONFLICT_FN_PARSE_ERROR,
-                        ER(ER_CONFLICT_FN_PARSE_ERROR),
-                        error_str);
-  }
-  else if (error < 0)
+  if (error < 0)
   {
     char msg[FN_REFLEN];
     switch (error)
@@ -4337,12 +4682,192 @@ err:
                         ER(ER_ILLEGAL_HA_CREATE_OPTION),
                         ndbcluster_hton_name, msg);  
   }
-  if (do_set_binlog_flags)
-    set_binlog_flags(share, NBT_DEFAULT);
+  *binlog_flags= NBT_DEFAULT;
+  *conflict_fn_spec= NULL;
+
   if (ndberror.code && opt_ndb_extra_logging)
     print_warning_list("NDB", thd_warn_list(thd));
   DBUG_RETURN(ndberror.code);
 }
+
+/*
+  ndbcluster_get_binlog_replication_info
+
+  This function retrieves the data for the given table
+  from the ndb_replication table.
+
+  If the table is not found, or the table does not exist,
+  then defaults are returned.
+*/
+int
+ndbcluster_get_binlog_replication_info(THD *thd, Ndb *ndb,
+                                       const char* db,
+                                       const char* table_name,
+                                       uint server_id,
+                                       const TABLE *table,
+                                       Uint32* binlog_flags,
+                                       const st_conflict_fn_def** conflict_fn,
+                                       st_conflict_fn_arg* args,
+                                       Uint32* num_args)
+{
+  DBUG_ENTER("ndbcluster_get_binlog_replication_info");
+
+  /* Override for ndb_apply_status when logging */
+  if (opt_ndb_log_apply_status)
+  {
+    if (strcmp(db, NDB_REP_DB) == 0 &&
+        strcmp(table_name, NDB_APPLY_TABLE) == 0)
+    {
+      /*
+        Ensure that we get all columns from ndb_apply_status updates
+        by forcing FULL event type
+        Also, ensure that ndb_apply_status events are always logged as
+        WRITES.
+      */
+      DBUG_PRINT("info", ("ndb_apply_status defaulting to FULL, USE_WRITE"));
+      sql_print_information("NDB : ndb-log-apply-status forcing "
+                            "%s.%s to FULL USE_WRITE",
+                            NDB_REP_DB, NDB_APPLY_TABLE);
+      *binlog_flags = NBT_FULL;
+      *conflict_fn = NULL;
+      *num_args = 0;
+      DBUG_RETURN(0);
+    }
+  }
+
+  const Uint32 MAX_CONFLICT_FN_SPEC_LEN = 256;
+  char conflict_fn_buffer[MAX_CONFLICT_FN_SPEC_LEN];
+  char* conflict_fn_spec;
+
+  if (ndbcluster_read_replication_table(thd,
+                                        ndb,
+                                        db,
+                                        table_name,
+                                        server_id,
+                                        binlog_flags,
+                                        &conflict_fn_spec,
+                                        conflict_fn_buffer,
+                                        MAX_CONFLICT_FN_SPEC_LEN) != 0)
+  {
+    DBUG_RETURN(ER_NDB_REPLICATION_SCHEMA_ERROR);
+  }
+
+  if (table != NULL)
+  {
+    if (conflict_fn_spec != NULL)
+    {
+      char tmp_buf[FN_REFLEN];
+
+      if (parse_conflict_fn_spec(conflict_fn_spec,
+                                 conflict_fn,
+                                 args,
+                                 num_args,
+                                 table,
+                                 tmp_buf,
+                                 sizeof(tmp_buf)) != 0)
+      {
+        push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+                            ER_CONFLICT_FN_PARSE_ERROR,
+                            ER(ER_CONFLICT_FN_PARSE_ERROR),
+                            tmp_buf);
+        DBUG_RETURN(ER_CONFLICT_FN_PARSE_ERROR);
+      }
+    }
+    else
+    {
+      /* No conflict function specified */
+      conflict_fn= NULL;
+      num_args= 0;
+    }
+  }
+
+  DBUG_RETURN(0);
+}
+
+int
+ndbcluster_apply_binlog_replication_info(THD *thd,
+                                         NDB_SHARE *share,
+                                         const NDBTAB* ndbtab,
+                                         TABLE* table,
+                                         const st_conflict_fn_def* conflict_fn,
+                                         const st_conflict_fn_arg* args,
+                                         Uint32 num_args,
+                                         bool do_set_binlog_flags,
+                                         Uint32 binlog_flags)
+{
+  DBUG_ENTER("ndbcluster_apply_binlog_replication_info");
+  char tmp_buf[FN_REFLEN];
+
+  if (do_set_binlog_flags)
+  {
+    DBUG_PRINT("info", ("Setting binlog flags to %u", binlog_flags));
+    set_binlog_flags(share, (enum Ndb_binlog_type)binlog_flags);
+  }
+
+  if (conflict_fn != NULL)
+  {
+    if (setup_conflict_fn(thd, share,
+                          ndbtab,
+                          tmp_buf, sizeof(tmp_buf),
+                          table,
+                          conflict_fn,
+                          args,
+                          num_args) != 0)
+    {
+      push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
+                          ER_CONFLICT_FN_PARSE_ERROR,
+                          ER(ER_CONFLICT_FN_PARSE_ERROR),
+                          tmp_buf);
+      DBUG_RETURN(-1);
+    }
+  }
+  else
+  {
+    /* No conflict function specified */
+    slave_reset_conflict_fn(share);
+  }
+
+  DBUG_RETURN(0);
+}
+
+int
+ndbcluster_read_binlog_replication(THD *thd, Ndb *ndb,
+                                   NDB_SHARE *share,
+                                   const NDBTAB *ndbtab,
+                                   uint server_id,
+                                   TABLE *table,
+                                   bool do_set_binlog_flags)
+{
+  DBUG_ENTER("ndbcluster_read_binlog_replication");
+  Uint32 binlog_flags;
+  const st_conflict_fn_def* conflict_fn= NULL;
+  st_conflict_fn_arg args[MAX_CONFLICT_ARGS];
+  Uint32 num_args = MAX_CONFLICT_ARGS;
+
+  if ((ndbcluster_get_binlog_replication_info(thd, ndb,
+                                              share->db,
+                                              share->table_name,
+                                              server_id,
+                                              table,
+                                              &binlog_flags,
+                                              &conflict_fn,
+                                              args,
+                                              &num_args) != 0) ||
+      (ndbcluster_apply_binlog_replication_info(thd,
+                                                share,
+                                                ndbtab,
+                                                table,
+                                                conflict_fn,
+                                                args,
+                                                num_args,
+                                                do_set_binlog_flags,
+                                                binlog_flags) != 0))
+  {
+    DBUG_RETURN(-1);
+  }
+
+  DBUG_RETURN(0);
+}
 #endif /* HAVE_NDB_BINLOG */
 
 bool
@@ -5966,6 +6491,13 @@ ndb_binlog_thread_func(void *arg)
   uint incident_id= 0;
   Binlog_thread_state do_ndbcluster_binlog_close_connection;
 
+  /**
+   * If we get error after having reported incident
+   *   but before binlog started...we do "Restarting Cluster Binlog"
+   *   in that case, don't report incident again
+   */
+  bool do_incident = true;
+
 #ifdef RUN_NDB_BINLOG_TIMER
   Timer main_timer;
 #endif
@@ -6106,7 +6638,7 @@ restart_cluster_failure:
   /*
     Main NDB Injector loop
   */
-  while (ndb_binlog_running)
+  while (do_incident && ndb_binlog_running)
   {
     /*
       check if it is the first log, if so we do not insert a GAP event
@@ -6138,6 +6670,7 @@ restart_cluster_failure:
     int ret = inj->record_incident(thd, INCIDENT_LOST_EVENTS,
                                    msg[incident_id]);
     assert(ret == 0);
+    do_incident = false; // Don't report incident again, unless we get started
     break;
   }
   incident_id= 1;
@@ -6263,6 +6796,7 @@ restart_cluster_failure:
     static char db[]= "";
     thd->db= db;
   }
+  do_incident = true; // If we get disconnected again...do incident report
   do_ndbcluster_binlog_close_connection= BCCC_running;
   for ( ; !((ndbcluster_binlog_terminating ||
              do_ndbcluster_binlog_close_connection) &&

=== modified file 'sql/ha_ndbcluster_binlog.h'
--- a/sql/ha_ndbcluster_binlog.h	2011-02-18 13:55:27 +0000
+++ b/sql/ha_ndbcluster_binlog.h	2011-06-16 18:16:01 +0000
@@ -95,8 +95,7 @@ static const char *ha_ndb_ext=".ndb";
 #define NDB_EXCEPTIONS_TABLE_SUFFIX "$EX"
 #define NDB_EXCEPTIONS_TABLE_SUFFIX_LOWER "$ex"
 
-const uint error_conflict_fn_old_violation= 9998;
-const uint error_conflict_fn_max_violation= 9999;
+const uint error_conflict_fn_violation= 9999;
 #endif /* HAVE_NDB_BINLOG */
 
 
@@ -195,6 +194,8 @@ void ndbcluster_global_schema_lock_deini
 extern unsigned char g_node_id_map[max_ndb_nodes];
 extern pthread_mutex_t LOCK_ndb_util_thread;
 extern pthread_cond_t COND_ndb_util_thread;
+extern pthread_mutex_t LOCK_ndb_index_stat_thread;
+extern pthread_cond_t COND_ndb_index_stat_thread;
 extern pthread_mutex_t ndbcluster_mutex;
 extern HASH ndbcluster_open_tables;
 
@@ -237,6 +238,26 @@ void ndb_rep_event_name(String *event_na
                         const char *db, const char *tbl, my_bool full);
 #ifdef HAVE_NDB_BINLOG
 int
+ndbcluster_get_binlog_replication_info(THD *thd, Ndb *ndb,
+                                       const char* db,
+                                       const char* table_name,
+                                       uint server_id,
+                                       const TABLE *table,
+                                       Uint32* binlog_flags,
+                                       const st_conflict_fn_def** conflict_fn,
+                                       st_conflict_fn_arg* args,
+                                       Uint32* num_args);
+int
+ndbcluster_apply_binlog_replication_info(THD *thd,
+                                         NDB_SHARE *share,
+                                         const NDBTAB* ndbtab,
+                                         TABLE* table,
+                                         const st_conflict_fn_def* conflict_fn,
+                                         const st_conflict_fn_arg* args,
+                                         Uint32 num_args,
+                                         bool do_set_binlog_flags,
+                                         Uint32 binlog_flags);
+int
 ndbcluster_read_binlog_replication(THD *thd, Ndb *ndb,
                                    NDB_SHARE *share,
                                    const NDBTAB *ndbtab,

=== modified file 'storage/ndb/CMakeLists.txt'
--- a/storage/ndb/CMakeLists.txt	2011-05-20 05:54:20 +0000
+++ b/storage/ndb/CMakeLists.txt	2011-06-17 12:41:11 +0000
@@ -147,6 +147,7 @@ SET(NDBCLUSTER_SOURCES
   ../../sql/ha_ndbcluster_push.cc
   ../../sql/ha_ndbcluster_connection.cc
   ../../sql/ha_ndbcluster_binlog.cc
+  ../../sql/ha_ndb_index_stat.cc
   ../../sql/ha_ndbinfo.cc)
 INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/storage/ndb/include)
 

=== modified file 'storage/ndb/include/kernel/ndb_limits.h'
--- a/storage/ndb/include/kernel/ndb_limits.h	2011-06-07 12:19:47 +0000
+++ b/storage/ndb/include/kernel/ndb_limits.h	2011-06-17 12:41:11 +0000
@@ -226,9 +226,13 @@
  * counted as 1 word.  Values currently contain RIR (one word) and RPK
  * (one word for each key level).  The SAMPLEs table STAT_VALUE column
  * is longer to allow future changes.
+ *
+ * Stats tables are "lifted" to mysql level so for max key size use
+ * MAX_KEY_LENGTH/4 instead of the bigger MAX_KEY_SIZE_IN_WORDS.  The
+ * definition is not available by default, use 3072 directly now.
  */
 #define MAX_INDEX_STAT_KEY_COUNT    MAX_ATTRIBUTES_IN_INDEX
-#define MAX_INDEX_STAT_KEY_SIZE     (MAX_KEY_SIZE_IN_WORDS - 3 - 1)
+#define MAX_INDEX_STAT_KEY_SIZE     ((3072/4) - 3 - 1)
 #define MAX_INDEX_STAT_VALUE_COUNT  (1 + MAX_INDEX_STAT_KEY_COUNT)
 #define MAX_INDEX_STAT_VALUE_SIZE   MAX_INDEX_STAT_VALUE_COUNT
 #define MAX_INDEX_STAT_VALUE_CSIZE  512 /* Longvarbinary(2048) */

=== modified file 'storage/ndb/include/ndb_constants.h'
--- a/storage/ndb/include/ndb_constants.h	2011-05-31 08:28:58 +0000
+++ b/storage/ndb/include/ndb_constants.h	2011-06-12 16:54:32 +0000
@@ -118,10 +118,10 @@
 #define NDB_INDEX_STAT_DB     "mysql"
 #define NDB_INDEX_STAT_SCHEMA "def"
 
-#define NDB_INDEX_STAT_HEAD_TABLE    "NDB$IS_HEAD"
-#define NDB_INDEX_STAT_SAMPLE_TABLE  "NDB$IS_SAMPLE"
-#define NDB_INDEX_STAT_SAMPLE_INDEX1 "NDB$IS_SAMPLE_X1"
+#define NDB_INDEX_STAT_HEAD_TABLE    "ndb_index_stat_head"
+#define NDB_INDEX_STAT_SAMPLE_TABLE  "ndb_index_stat_sample"
+#define NDB_INDEX_STAT_SAMPLE_INDEX1 "ndb_index_stat_sample_x1"
 
-#define NDB_INDEX_STAT_PREFIX        "NDB$IS"
+#define NDB_INDEX_STAT_PREFIX        "ndb_index_stat"
 
 #endif

=== modified file 'storage/ndb/src/kernel/blocks/trix/Trix.cpp'
--- a/storage/ndb/src/kernel/blocks/trix/Trix.cpp	2011-06-06 12:18:27 +0000
+++ b/storage/ndb/src/kernel/blocks/trix/Trix.cpp	2011-06-12 16:54:32 +0000
@@ -1694,50 +1694,50 @@ Trix::execINDEX_STAT_IMPL_REQ(Signal* si
 
 const Trix::SysColumn
 Trix::g_statMetaHead_column[] = {
-  { 0, "INDEX_ID",
+  { 0, "index_id",
     true
   },
-  { 1, "INDEX_VERSION",
+  { 1, "index_version",
     true
   },
-  { 2, "TABLE_ID",
+  { 2, "table_id",
     false
   },
-  { 3, "FRAG_COUNT",
+  { 3, "frag_count",
     false
   },
-  { 4, "VALUE_FORMAT",
+  { 4, "value_format",
     false
   },
-  { 5, "SAMPLE_VERSION",
+  { 5, "sample_version",
     false
   },
-  { 6, "LOAD_TIME",
+  { 6, "load_time",
     false
   },
-  { 7, "SAMPLE_COUNT",
+  { 7, "sample_count",
     false
   },
-  { 8, "KEY_BYTES",
+  { 8, "key_bytes",
     false
   }
 };
 
 const Trix::SysColumn
 Trix::g_statMetaSample_column[] = {
-  { 0, "INDEX_ID",
+  { 0, "index_id",
     true
   },
-  { 1, "INDEX_VERSION",
+  { 1, "index_version",
     true
   },
-  { 2, "SAMPLE_VERSION",
+  { 2, "sample_version",
     true
   },
-  { 3, "STAT_KEY",
+  { 3, "stat_key",
     true
   },
-  { 4, "STAT_VALUE",
+  { 4, "stat_value",
     false
   }
 };

=== modified file 'storage/ndb/src/ndbapi/CMakeLists.txt'
--- a/storage/ndb/src/ndbapi/CMakeLists.txt	2011-06-07 12:19:47 +0000
+++ b/storage/ndb/src/ndbapi/CMakeLists.txt	2011-06-17 12:41:11 +0000
@@ -21,7 +21,8 @@ ADD_LIBRARY(ndbapi STATIC
             NdbEventOperation.cpp
             NdbEventOperationImpl.cpp
             NdbIndexStat.cpp
-	    NdbIndexStatImpl.cpp
+            NdbIndexStatImpl.cpp
+            NdbIndexStatFrmData.cpp
             NdbInterpretedCode.cpp
             TransporterFacade.cpp
             ClusterMgr.cpp

=== modified file 'storage/ndb/src/ndbapi/Makefile.am'
--- a/storage/ndb/src/ndbapi/Makefile.am	2011-06-07 12:19:47 +0000
+++ b/storage/ndb/src/ndbapi/Makefile.am	2011-06-17 12:41:11 +0000
@@ -59,6 +59,7 @@ libndbapi_la_SOURCES = \
 	NdbBlob.cpp \
 	NdbIndexStat.cpp \
 	NdbIndexStatImpl.cpp \
+	NdbIndexStatFrmData.cpp \
         SignalSender.cpp \
         ObjectMap.cpp \
 	NdbInterpretedCode.cpp \

=== added file 'storage/ndb/src/ndbapi/NdbIndexStatFrmData.cpp'
--- a/storage/ndb/src/ndbapi/NdbIndexStatFrmData.cpp	1970-01-01 00:00:00 +0000
+++ b/storage/ndb/src/ndbapi/NdbIndexStatFrmData.cpp	2011-06-12 16:54:32 +0000
@@ -0,0 +1,175 @@
+/*
+   Copyright (c) 2011, 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
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+*/
+
+#include <ndb_global.h>
+#include "NdbIndexStatImpl.hpp"
+
+#if ndb_index_stat_systables_sql
+
+use mysql;
+
+create table ndb_index_stat_head (
+  index_id int unsigned not null,
+  index_version int unsigned not null,
+  table_id int unsigned not null,
+  frag_count int unsigned not null,
+  value_format int unsigned not null,
+  sample_version int unsigned not null,
+  load_time int unsigned not null,
+  sample_count int unsigned not null,
+  key_bytes int unsigned not null,
+  primary key using hash (
+    index_id, index_version)
+) engine=ndb;
+
+create table ndb_index_stat_sample (
+  index_id int unsigned not null,
+  index_version int unsigned not null,
+  sample_version int unsigned not null,
+  stat_key varbinary(3056) not null, -- ((3072/4) - 3 - 1) * 4
+  stat_value varbinary(2048) not null, -- 512 * 4
+  primary key using hash (
+    index_id, index_version, sample_version, stat_key),
+  index ndb_index_stat_sample_x1 (
+    index_id, index_version, sample_version)
+) engine=ndb;
+
+#endif
+
+// rest dumped from *.frm by tools/ndb_dump_frm_data
+
+/*
+  name: ndb_index_stat_head
+  orig: 8918
+  pack: 402
+*/
+
+const uint g_ndb_index_stat_head_frm_len = 402;
+
+const uint8 g_ndb_index_stat_head_frm_data[402] =
+{
+  0x01,0x00,0x00,0x00,0xd6,0x22,0x00,0x00,
+  0x86,0x01,0x00,0x00,0x78,0x9c,0xed,0xda,
+  0xbf,0x4e,0xc2,0x40,0x1c,0x07,0xf0,0x6f,
+  0xcb,0xdf,0x1e,0x50,0x08,0x21,0x0c,0x0c,
+  0xa6,0x31,0x21,0x11,0x17,0x9d,0x9d,0xc4,
+  0xc4,0x81,0x18,0x95,0x10,0x16,0x5c,0x9a,
+  0x02,0x87,0xa9,0x02,0x35,0xb4,0x10,0xd9,
+  0x78,0x27,0x1e,0xc1,0x77,0xf0,0x29,0x7c,
+  0x06,0xcf,0x2b,0x50,0x3c,0x36,0x16,0x83,
+  0x9a,0xdf,0x67,0xba,0xfb,0xf6,0x2e,0xf9,
+  0xe5,0xbb,0xb5,0xe9,0xa7,0x66,0x98,0x31,
+  0xa0,0xa0,0x01,0xe7,0x80,0xab,0x55,0xb1,
+  0xa5,0x9f,0x22,0x0d,0x24,0xc2,0x65,0x3a,
+  0xca,0x5c,0x79,0xee,0xe3,0x0d,0xb8,0x58,
+  0xed,0x4c,0xe0,0xec,0x0c,0xb0,0x40,0x08,
+  0x21,0x84,0x10,0x42,0x08,0x21,0x84,0x90,
+  0xdf,0x4c,0xd3,0x01,0x86,0xf0,0x0d,0x5f,
+  0x8f,0xc9,0xdd,0x42,0x6e,0x2b,0x97,0x71,
+  0xe8,0x8b,0xe4,0x7a,0x21,0x9a,0xad,0xc6,
+  0x6d,0xbd,0xd5,0x11,0x87,0x1e,0xf4,0xaf,
+  0xdb,0xb3,0x40,0x86,0x71,0xbf,0xdb,0x1b,
+  0x4e,0xfd,0x80,0x4f,0x4a,0x72,0x6f,0x35,
+  0xeb,0xad,0x76,0xa3,0xdd,0xb8,0xbf,0xb3,
+  0xae,0x3a,0xd6,0xcd,0x75,0xc7,0x3a,0xa9,
+  0x41,0x2b,0xfe,0xe8,0xa8,0x84,0x10,0x42,
+  0x08,0x21,0x84,0x10,0x42,0xfe,0x9f,0x77,
+  0x1d,0x85,0x43,0xcf,0x70,0x48,0x1a,0x0c,
+  0x2c,0xf1,0x20,0x57,0x55,0x3c,0x6d,0xd3,
+  0x26,0xca,0x9b,0xd5,0x12,0xcc,0xd0,0x4b,
+  0x35,0x6b,0x4f,0x88,0xc3,0x70,0xc7,0x7d,
+  0xfe,0x6a,0xbb,0x7d,0x24,0x60,0xae,0xd7,
+  0x33,0x3e,0xf1,0x5d,0x6f,0x8c,0x24,0x8c,
+  0xc0,0xe9,0x0e,0x79,0xf8,0x30,0x85,0xcc,
+  0x60,0xe2,0x3c,0xda,0x3d,0x6f,0x3a,0x0e,
+  0x90,0x46,0x6e,0xe6,0x0c,0xa7,0xdc,0x1e,
+  0x78,0x93,0x91,0x13,0xc8,0xa1,0xf2,0xbe,
+  0x33,0x7a,0x91,0x47,0xa3,0xbb,0x0c,0x6c,
+  0xe8,0x39,0x7d,0x3b,0x70,0x47,0x1c,0x19,
+  0xe4,0x36,0x8f,0xd7,0xd7,0xb3,0x60,0xcf,
+  0x7c,0x6e,0x77,0xe7,0x01,0xf7,0x11,0x37,
+  0x18,0xc3,0xea,0x3b,0x8e,0x9c,0x3f,0x16,
+  0xfe,0xc2,0x61,0xca,0x20,0xa9,0x04,0xc9,
+  0xf0,0x04,0x53,0x82,0x54,0x46,0x06,0xa6,
+  0x12,0xa4,0x73,0x32,0x28,0x2a,0x81,0x91,
+  0x97,0x41,0x59,0x09,0x98,0x84,0x8a,0x12,
+  0x64,0xc2,0x2b,0x47,0x4a,0x90,0x0d,0x4f,
+  0x1c,0x2b,0x81,0x88,0x9a,0x11,0x3b,0xb5,
+  0x88,0xa8,0x13,0xf1,0x5d,0x88,0x50,0xdb,
+  0x10,0xbb,0x55,0x88,0x6d,0x0f,0x42,0x2d,
+  0x41,0x6c,0x1b,0x10,0xf8,0x02,0xe7,0x16,
+  0x7d,0xd4
+};
+
+/*
+  name: ndb_index_stat_sample
+  orig: 12842
+  pack: 371
+*/
+
+const uint g_ndb_index_stat_sample_frm_len = 371;
+
+const uint8 g_ndb_index_stat_sample_frm_data[371] =
+{
+  0x01,0x00,0x00,0x00,0x2a,0x32,0x00,0x00,
+  0x67,0x01,0x00,0x00,0x78,0x9c,0xed,0xda,
+  0x3b,0x4f,0xc2,0x60,0x18,0x05,0xe0,0xd3,
+  0x1b,0xf4,0xa2,0x85,0x81,0x38,0x38,0x35,
+  0x71,0x01,0x17,0x74,0x71,0x32,0x11,0x8c,
+  0x9a,0x10,0xa3,0x12,0xc2,0xc2,0xd4,0x80,
+  0xed,0x40,0xc4,0x6a,0xb8,0x05,0xb6,0xfe,
+  0x36,0xfd,0x3b,0x0e,0xfc,0x00,0xe3,0x67,
+  0x69,0xa9,0xc0,0x62,0xdc,0x8a,0xc9,0x79,
+  0xa6,0xb7,0xa7,0x5f,0x9a,0xf3,0x6d,0x1d,
+  0xde,0x2f,0xc9,0xb4,0x15,0xa0,0x28,0x01,
+  0x35,0xe0,0x4d,0x46,0x09,0x29,0x79,0x06,
+  0x03,0xd0,0x96,0xa3,0x9e,0x66,0xd1,0x01,
+  0x7c,0xbc,0x03,0x67,0xf1,0x93,0x0d,0x54,
+  0xab,0xc0,0x09,0x88,0x88,0x88,0x88,0x88,
+  0x88,0x88,0x68,0x97,0xc9,0x79,0xe0,0x08,
+  0x65,0x7c,0x5a,0xaa,0x02,0x48,0xa1,0x04,
+  0x1c,0xd6,0x54,0xc8,0xa1,0x96,0x0c,0x4a,
+  0x68,0x24,0x83,0x1a,0xee,0x47,0xef,0x6b,
+  0x0b,0x4b,0xc2,0x1e,0xa2,0xa3,0xbf,0x9d,
+  0x15,0xcd,0x56,0xe3,0xae,0xde,0xea,0x88,
+  0xc0,0xeb,0xb9,0xfd,0xc0,0xf3,0x67,0xee,
+  0x68,0xdc,0x1d,0xbb,0xa3,0xee,0xf3,0xeb,
+  0xc0,0x77,0x67,0xa7,0x22,0xeb,0x6b,0x13,
+  0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
+  0x11,0xd1,0x3f,0x60,0x22,0xf0,0x7a,0x8f,
+  0x83,0xc9,0x68,0xec,0x0f,0x97,0xeb,0x6b,
+  0x4e,0xb3,0xde,0x6a,0x37,0xda,0x8d,0x87,
+  0x7b,0xe7,0xb2,0xe3,0xdc,0x5e,0x77,0x9c,
+  0x72,0x05,0x92,0x9d,0x75,0x4d,0x22,0x22,
+  0x22,0xa2,0x9d,0x70,0x2c,0xa3,0x98,0x75,
+  0x87,0x2c,0x49,0xd0,0x30,0x87,0xbd,0xfc,
+  0x6d,0x2c,0x9d,0xff,0xa4,0x4d,0x1c,0xac,
+  0xa6,0x39,0x72,0x9a,0x5c,0xaa,0x38,0x7f,
+  0x04,0x15,0x46,0xb2,0xf0,0xd0,0xf7,0xa2,
+  0x2f,0xdb,0xc9,0x3c,0xf5,0x87,0xa3,0xfe,
+  0x4b,0x80,0x1c,0x0a,0xab,0x15,0x88,0x34,
+  0xc9,0xc3,0x88,0x37,0x23,0x9e,0xfc,0x39,
+  0x74,0x58,0xf1,0x3c,0xed,0x0e,0x26,0x3e,
+  0x54,0xc3,0x34,0x11,0x6f,0x58,0x44,0x2d,
+  0x14,0x1d,0xd0,0xec,0x28,0xd0,0x36,0x82,
+  0x5c,0x21,0x0a,0x8c,0x8d,0x20,0x6f,0xdc,
+  0x2c,0xac,0x78,0x4b,0x23,0x0a,0x0a,0x17,
+  0x80,0x6e,0x5d,0x41,0x17,0xd6,0x3a,0x10,
+  0x69,0x37,0xb1,0x55,0x4c,0x6c,0xb7,0x12,
+  0x69,0x25,0xb1,0xee,0x23,0xf0,0x0d,0x53,
+  0x4c,0x66,0xbc
+};

=== modified file 'storage/ndb/src/ndbapi/NdbIndexStatImpl.cpp'
--- a/storage/ndb/src/ndbapi/NdbIndexStatImpl.cpp	2011-06-07 13:28:40 +0000
+++ b/storage/ndb/src/ndbapi/NdbIndexStatImpl.cpp	2011-06-16 18:16:01 +0000
@@ -101,6 +101,10 @@ NdbIndexStatImpl::Sys::~Sys()
 void
 NdbIndexStatImpl::sys_release(Sys& sys)
 {
+  // close schema trans if any exists
+  NdbDictionary::Dictionary* const dic = sys.m_dic;
+  (void)dic->endSchemaTrans(NdbDictionary::Dictionary::SchemaTransAbort);
+
   if (sys.m_headtable != 0)
   {
     sys.m_dic->removeTableGlobal(*sys.m_headtable, false);
@@ -123,59 +127,67 @@ NdbIndexStatImpl::make_headtable(NdbDict
 {
   tab.setName(g_headtable_name);
   tab.setLogging(true);
+  int ret;
+  ret = tab.setFrm(g_ndb_index_stat_head_frm_data,
+                   g_ndb_index_stat_head_frm_len);
+  if (ret != 0)
+  {
+    setError(ret, __LINE__);
+    return -1;
+  }
   // key must be first
   {
-    NdbDictionary::Column col("INDEX_ID");
+    NdbDictionary::Column col("index_id");
     col.setType(NdbDictionary::Column::Unsigned);
     col.setPrimaryKey(true);
     tab.addColumn(col);
   }
   {
-    NdbDictionary::Column col("INDEX_VERSION");
+    NdbDictionary::Column col("index_version");
     col.setType(NdbDictionary::Column::Unsigned);
     col.setPrimaryKey(true);
     tab.addColumn(col);
   }
   // table
   {
-    NdbDictionary::Column col("TABLE_ID");
+    NdbDictionary::Column col("table_id");
     col.setType(NdbDictionary::Column::Unsigned);
     col.setNullable(false);
     tab.addColumn(col);
   }
   {
-    NdbDictionary::Column col("FRAG_COUNT");
+    NdbDictionary::Column col("frag_count");
     col.setType(NdbDictionary::Column::Unsigned);
     col.setNullable(false);
     tab.addColumn(col);
   }
   // current sample
   {
-    NdbDictionary::Column col("VALUE_FORMAT");
+    NdbDictionary::Column col("value_format");
     col.setType(NdbDictionary::Column::Unsigned);
     col.setNullable(false);
     tab.addColumn(col);
   }
   {
-    NdbDictionary::Column col("SAMPLE_VERSION");
+    NdbDictionary::Column col("sample_version");
     col.setType(NdbDictionary::Column::Unsigned);
     col.setNullable(false);
     tab.addColumn(col);
   }
   {
-    NdbDictionary::Column col("LOAD_TIME");
+    NdbDictionary::Column col("load_time");
     col.setType(NdbDictionary::Column::Unsigned);
     col.setNullable(false);
     tab.addColumn(col);
   }
   {
-    NdbDictionary::Column col("SAMPLE_COUNT");
+    NdbDictionary::Column col("sample_count");
     col.setType(NdbDictionary::Column::Unsigned);
     col.setNullable(false);
     tab.addColumn(col);
   }
   {
-    NdbDictionary::Column col("KEY_BYTES");
+    NdbDictionary::Column col("key_bytes");
     col.setType(NdbDictionary::Column::Unsigned);
     col.setNullable(false);
     tab.addColumn(col);
@@ -193,27 +205,35 @@ NdbIndexStatImpl::make_sampletable(NdbDi
 {
   tab.setName(g_sampletable_name);
   tab.setLogging(true);
+  int ret;
+  ret = tab.setFrm(g_ndb_index_stat_sample_frm_data,
+                   g_ndb_index_stat_sample_frm_len);
+  if (ret != 0)
+  {
+    setError(ret, __LINE__);
+    return -1;
+  }
   // key must be first
   {
-    NdbDictionary::Column col("INDEX_ID");
+    NdbDictionary::Column col("index_id");
     col.setType(NdbDictionary::Column::Unsigned);
     col.setPrimaryKey(true);
     tab.addColumn(col);
   }
   {
-    NdbDictionary::Column col("INDEX_VERSION");
+    NdbDictionary::Column col("index_version");
     col.setType(NdbDictionary::Column::Unsigned);
     col.setPrimaryKey(true);
     tab.addColumn(col);
   }
   {
-    NdbDictionary::Column col("SAMPLE_VERSION");
+    NdbDictionary::Column col("sample_version");
     col.setType(NdbDictionary::Column::Unsigned);
     col.setPrimaryKey(true);
     tab.addColumn(col);
   }
   {
-    NdbDictionary::Column col("STAT_KEY");
+    NdbDictionary::Column col("stat_key");
     col.setType(NdbDictionary::Column::Longvarbinary);
     col.setPrimaryKey(true);
     col.setLength(MaxKeyBytes);
@@ -221,7 +241,7 @@ NdbIndexStatImpl::make_sampletable(NdbDi
   }
   // value
   {
-    NdbDictionary::Column col("STAT_VALUE");
+    NdbDictionary::Column col("stat_value");
     col.setType(NdbDictionary::Column::Longvarbinary);
     col.setNullable(false);
     col.setLength(MaxValueCBytes);
@@ -242,9 +262,9 @@ NdbIndexStatImpl::make_sampleindex1(NdbD
   ind.setName(g_sampleindex1_name);
   ind.setType(NdbDictionary::Index::OrderedIndex);
   ind.setLogging(false);
-  ind.addColumnName("INDEX_ID");
-  ind.addColumnName("INDEX_VERSION");
-  ind.addColumnName("SAMPLE_VERSION");
+  ind.addColumnName("index_id");
+  ind.addColumnName("index_version");
+  ind.addColumnName("sample_version");
   return 0;
 }
 
@@ -382,7 +402,13 @@ NdbIndexStatImpl::create_systables(Ndb*
     return -1;
   }
 
-  NdbDictionary::Dictionary* const dic = ndb->getDictionary();
+  NdbDictionary::Dictionary* const dic = sys.m_dic;
+
+  if (dic->beginSchemaTrans() == -1)
+  {
+    setError(dic->getNdbError().code, __LINE__);
+    return -1;
+  }
 
   {
     NdbDictionary::Table tab;
@@ -406,6 +432,19 @@ NdbIndexStatImpl::create_systables(Ndb*
     NdbDictionary::Table tab;
     if (make_sampletable(tab) == -1)
       return -1;
+
+#ifdef VM_TRACE
+    // test of schema trans
+    {
+      const char* p = NdbEnv_GetEnv("NDB_INDEX_STAT_ABORT_SYS_CREATE", (char*)0, 0);
+      if (p != 0 && strchr("1Y", p[0]) != 0)
+      {
+        setError(9999, __LINE__);
+        return -1;
+      }
+    }
+#endif
+
     if (dic->createTable(tab) == -1)
     {
       setError(dic->getNdbError().code, __LINE__);
@@ -438,6 +477,12 @@ NdbIndexStatImpl::create_systables(Ndb*
     }
   }
 
+  if (dic->endSchemaTrans() == -1)
+  {
+    setError(dic->getNdbError().code, __LINE__);
+    return -1;
+  }
+
   return 0;
 }
 
@@ -450,7 +495,13 @@ NdbIndexStatImpl::drop_systables(Ndb* nd
       m_error.code != BadSysTables)
     return -1;
 
-  NdbDictionary::Dictionary* const dic = ndb->getDictionary();
+  NdbDictionary::Dictionary* const dic = sys.m_dic;
+
+  if (dic->beginSchemaTrans() == -1)
+  {
+    setError(dic->getNdbError().code, __LINE__);
+    return -1;
+  }
 
   if (sys.m_headtable != 0)
   {
@@ -463,12 +514,31 @@ NdbIndexStatImpl::drop_systables(Ndb* nd
 
   if (sys.m_sampletable != 0)
   {
+
+#ifdef VM_TRACE
+    // test of schema trans
+    {
+      const char* p = NdbEnv_GetEnv("NDB_INDEX_STAT_ABORT_SYS_DROP", (char*)0, 0);
+      if (p != 0 && strchr("1Y", p[0]) != 0)
+      {
+        setError(9999, __LINE__);
+        return -1;
+      }
+    }
+#endif
+
     if (dic->dropTableGlobal(*sys.m_sampletable) == -1)
     {
       setError(dic->getNdbError().code, __LINE__);
       return -1;
     }
   }
+
+  if (dic->endSchemaTrans() == -1)
+  {
+    setError(dic->getNdbError().code, __LINE__);
+    return -1;
+  }
     
   return 0;
 }
@@ -816,12 +886,12 @@ NdbIndexStatImpl::sys_head_setkey(Con& c
 {
   Head& head = con.m_head;
   NdbOperation* op = con.m_op;
-  if (op->equal("INDEX_ID", (char*)&head.m_indexId) == -1)
+  if (op->equal("index_id", (char*)&head.m_indexId) == -1)
   {
     setError(con, __LINE__);
     return -1;
   }
-  if (op->equal("INDEX_VERSION", (char*)&head.m_indexVersion) == -1)
+  if (op->equal("index_version", (char*)&head.m_indexVersion) == -1)
   {
     setError(con, __LINE__);
     return -1;
@@ -834,37 +904,37 @@ NdbIndexStatImpl::sys_head_getvalue(Con&
 {
   Head& head = con.m_head;
   NdbOperation* op = con.m_op;
-  if (op->getValue("TABLE_ID", (char*)&head.m_tableId) == 0)
+  if (op->getValue("table_id", (char*)&head.m_tableId) == 0)
   {
     setError(con, __LINE__);
     return -1;
   }
-  if (op->getValue("FRAG_COUNT", (char*)&head.m_fragCount) == 0)
+  if (op->getValue("frag_count", (char*)&head.m_fragCount) == 0)
   {
     setError(con, __LINE__);
     return -1;
   }
-  if (op->getValue("VALUE_FORMAT", (char*)&head.m_valueFormat) == 0)
+  if (op->getValue("value_format", (char*)&head.m_valueFormat) == 0)
   {
     setError(con, __LINE__);
     return -1;
   }
-  if (op->getValue("SAMPLE_VERSION", (char*)&head.m_sampleVersion) == 0)
+  if (op->getValue("sample_version", (char*)&head.m_sampleVersion) == 0)
   {
     setError(con, __LINE__);
     return -1;
   }
-  if (op->getValue("LOAD_TIME", (char*)&head.m_loadTime) == 0)
+  if (op->getValue("load_time", (char*)&head.m_loadTime) == 0)
   {
     setError(con, __LINE__);
     return -1;
   }
-  if (op->getValue("SAMPLE_COUNT", (char*)&head.m_sampleCount) == 0)
+  if (op->getValue("sample_count", (char*)&head.m_sampleCount) == 0)
   {
     setError(con, __LINE__);
     return -1;
   }
-  if (op->getValue("KEY_BYTES", (char*)&head.m_keyBytes) == 0)
+  if (op->getValue("key_bytes", (char*)&head.m_keyBytes) == 0)
   {
     setError(con, __LINE__);
     return -1;
@@ -877,22 +947,22 @@ NdbIndexStatImpl::sys_sample_setkey(Con&
 {
   Head& head = con.m_head;
   NdbIndexScanOperation* op = con.m_scanop;
-  if (op->equal("INDEX_ID", (char*)&head.m_indexId) == -1)
+  if (op->equal("index_id", (char*)&head.m_indexId) == -1)
   {
     setError(con, __LINE__);
     return -1;
   }
-  if (op->equal("INDEX_VERSION", (char*)&head.m_indexVersion) == -1)
+  if (op->equal("index_version", (char*)&head.m_indexVersion) == -1)
   {
     setError(con, __LINE__);
     return -1;
   }
-  if (op->equal("SAMPLE_VERSION", (char*)&head.m_sampleVersion) == -1)
+  if (op->equal("sample_version", (char*)&head.m_sampleVersion) == -1)
   {
     setError(con, __LINE__);
     return -1;
   }
-  if (op->equal("STAT_KEY", (char*)m_keyData.get_full_buf()) == -1)
+  if (op->equal("stat_key", (char*)m_keyData.get_full_buf()) == -1)
   {
     setError(con, __LINE__);
     return -1;
@@ -904,12 +974,12 @@ int
 NdbIndexStatImpl::sys_sample_getvalue(Con& con)
 {
   NdbIndexScanOperation* op = con.m_scanop;
-  if (op->getValue("STAT_KEY", (char*)m_keyData.get_full_buf()) == 0)
+  if (op->getValue("stat_key", (char*)m_keyData.get_full_buf()) == 0)
   {
     setError(con, __LINE__);
     return -1;
   }
-  if (op->getValue("STAT_VALUE", (char*)m_valueData.get_full_buf()) == 0)
+  if (op->getValue("stat_value", (char*)m_valueData.get_full_buf()) == 0)
   {
     setError(con, __LINE__);
     return -1;
@@ -925,19 +995,19 @@ NdbIndexStatImpl::sys_sample_setbound(Co
   const NdbIndexScanOperation::BoundType eq_bound =
     NdbIndexScanOperation::BoundEQ;
 
-  if (op->setBound("INDEX_ID", eq_bound, &head.m_indexId) == -1)
+  if (op->setBound("index_id", eq_bound, &head.m_indexId) == -1)
   {
     setError(con, __LINE__);
     return -1;
   }
-  if (op->setBound("INDEX_VERSION", eq_bound, &head.m_indexVersion) == -1)
+  if (op->setBound("index_version", eq_bound, &head.m_indexVersion) == -1)
   {
     setError(con, __LINE__);
     return -1;
   }
   if (sv_bound != -1)
   {
-    if (op->setBound("SAMPLE_VERSION", sv_bound, &head.m_sampleVersion) == -1)
+    if (op->setBound("sample_version", sv_bound, &head.m_sampleVersion) == -1)
     {
       setError(con, __LINE__);
       return -1;

=== modified file 'storage/ndb/src/ndbapi/NdbIndexStatImpl.hpp'
--- a/storage/ndb/src/ndbapi/NdbIndexStatImpl.hpp	2011-06-07 10:03:02 +0000
+++ b/storage/ndb/src/ndbapi/NdbIndexStatImpl.hpp	2011-06-12 16:54:32 +0000
@@ -30,6 +30,11 @@ class NdbIndexScanOperation;
 class NdbRecAttr;
 class NdbOperation;
 
+extern const uint g_ndb_index_stat_head_frm_len;
+extern const uint8 g_ndb_index_stat_head_frm_data[];
+extern const uint g_ndb_index_stat_sample_frm_len;
+extern const uint8 g_ndb_index_stat_sample_frm_data[];
+
 class NdbIndexStatImpl : public NdbIndexStat {
 public:
   friend class NdbIndexStat;

=== modified file 'storage/ndb/tools/CMakeLists.txt'
--- a/storage/ndb/tools/CMakeLists.txt	2011-05-24 11:51:39 +0000
+++ b/storage/ndb/tools/CMakeLists.txt	2011-06-14 10:42:04 +0000
@@ -82,6 +82,14 @@ ADD_CUSTOM_COMMAND(OUTPUT ${PROJECT_SOUR
 ADD_CUSTOM_TARGET(ndbinfo_sql_run ALL
 				  DEPENDS ${PROJECT_SOURCE_DIR}/storage/ndb/tools/ndbinfo.sql)
 
+MYSQL_ADD_EXECUTABLE(ndb_index_stat
+  ndb_index_stat.cpp
+  COMPONENT ClusterTools)
+TARGET_LINK_LIBRARIES(ndb_index_stat ndbNDBT ndbgeneral)
+
+ADD_EXECUTABLE(ndb_dump_frm_data
+  ndb_dump_frm_data.cpp)
+TARGET_LINK_LIBRARIES(ndb_dump_frm_data ndbNDBT ndbgeneral)
 
 IF (MYSQL_VERSION_ID LESS "50501")
   # Don't build or install this program anymore in 5.5+

=== modified file 'storage/ndb/tools/Makefile.am'
--- a/storage/ndb/tools/Makefile.am	2011-06-06 12:18:27 +0000
+++ b/storage/ndb/tools/Makefile.am	2011-06-12 16:54:32 +0000
@@ -17,7 +17,7 @@ EXTRA_DIST = CMakeLists.txt ndbinfo.sql
 
 BUILT_SOURCES = ndbinfo.sql
 
-noinst_PROGRAMS = 	ndbinfo_sql
+noinst_PROGRAMS = 	ndbinfo_sql ndb_dump_frm_data
 dist_bin_SCRIPTS =	ndb_size.pl ndb_error_reporter
 dist_pkgdata_DATA = ndbinfo.sql
 
@@ -78,6 +78,7 @@ ndbinfo.sql: $(ndbinfo_sql_SOURCES)
 	$(MV) $@-t $@
 
 ndb_index_stat_SOURCES = ndb_index_stat.cpp $(tools_common_sources)
+ndb_dump_frm_data_SOURCES = ndb_dump_frm_data.cpp
 
 include $(top_srcdir)/storage/ndb/config/common.mk.am
 include $(top_srcdir)/storage/ndb/config/type_ndbapitools.mk.am
@@ -95,4 +96,5 @@ ndb_restore_LDFLAGS = @ndb_bin_am_ldflag
 ndb_config_LDFLAGS = @ndb_bin_am_ldflags@
 ndbinfo_sql_LDFLAGS = @ndb_bin_am_ldflags@
 ndb_index_stat_LDFLAGS = @ndb_bin_am_ldflags@
+ndb_dump_frm_data_LDFLAGS = @ndb_bin_am_ldflags@
 

=== added file 'storage/ndb/tools/ndb_dump_frm_data.cpp'
--- a/storage/ndb/tools/ndb_dump_frm_data.cpp	1970-01-01 00:00:00 +0000
+++ b/storage/ndb/tools/ndb_dump_frm_data.cpp	2011-06-14 10:42:04 +0000
@@ -0,0 +1,176 @@
+/* Copyright (C) 2003 MySQL AB
+
+   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
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
+
+#include <ndb_global.h>
+#include <ndb_opts.h>
+
+#include <NdbOut.hpp>
+#include <NdbApi.hpp>
+#include <NDBT.hpp>
+
+// UNUSED static int oi = 1000;
+static struct my_option
+my_long_options[] =
+{
+  { "help", '?',
+    "Display this help and exit.",
+    0, 0, 0,
+    GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 },
+  { 0, 0,
+    0,
+    0, 0, 0,
+    GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }
+};
+
+const char*
+load_default_groups[]= { 0 };
+
+static void
+short_usage_sub(void)
+{
+  ndb_short_usage_sub("*.frm ...");
+}
+
+static void
+usage()
+{
+  printf("%s: pack and dump *.frm as C arrays\n", my_progname);
+  ndb_usage(short_usage_sub, load_default_groups, my_long_options);
+}
+
+static void
+dodump(const char* name, const uchar* frm_data, uint frm_len)
+{
+  printf("const uint g_%s_frm_len = %u;\n\n", name, frm_len);
+  printf("const uint8 g_%s_frm_data[%u] =\n{\n", name, frm_len);
+  uint n = 0;
+  while (n < frm_len) {
+    if (n % 8 == 0) {
+      if (n != 0) {
+        printf("\n");
+      }
+      printf("  ");
+    }
+    printf("0x%02x", frm_data[n]);
+    if (n + 1 < frm_len) {
+            printf(",");
+    }
+    n++;
+  }
+  if (n % 8 != 0) {
+    printf("\n");
+  }
+  printf("};\n");
+}
+
+static int
+dofile(const char* file)
+{
+  struct stat st;
+  size_t size = 0;
+  uchar* data = 0;
+  int fd = -1;
+  uchar* pack_data = 0;
+  size_t pack_len = 0;
+  char* namebuf = 0;
+  int ret = -1;
+  do
+  {
+    if (stat(file, &st) == -1)
+    {
+      fprintf(stderr, "%s: stat: %s\n", file, strerror(errno));
+      break;
+    }
+    size = st.st_size;
+    if ((data = (uchar*)malloc(size)) == 0)
+    {
+      fprintf(stderr, "%s: malloc %u: %s\n", file, (uint)size, strerror(errno));
+      break;
+    }
+    if ((fd = open(file, O_RDONLY)) == -1)
+    {
+      fprintf(stderr, "%s: open: %s\n", file, strerror(errno));
+      break;
+    }
+    ssize_t size2;
+    if ((size2 = read(fd, data, size)) == -1)
+    {
+      fprintf(stderr, "%s: read: %s\n", file, strerror(errno));
+      break;
+    }
+    if ((size_t)size2 != size)
+    {
+      fprintf(stderr, "%s: short read: %u != %u\n", file, (uint)size2, (uint)size);
+      break;
+    }
+    int error;
+    if ((error = packfrm(data, size, &pack_data, &pack_len)) != 0)
+    {
+      fprintf(stderr, "%s: packfrm: error %d\n", file, error);
+      break;
+    }
+    namebuf = strdup(file);
+    if (namebuf == 0)
+    {
+      fprintf(stderr, "%s: strdup: %s\n", file, strerror(errno));
+      break;
+    }
+    char* name = namebuf;
+    if (strchr(name, '/') != 0)
+      name = strrchr(name, '/') + 1;
+    char* dot;
+    if ((dot = strchr(name, '.')) != 0)
+      *dot = 0;
+    printf("\n/*\n");
+    printf("  name: %s\n", name);
+    printf("  orig: %u\n", (uint)size);
+    printf("  pack: %u\n", (uint)pack_len);
+    printf("*/\n\n");
+    dodump(name, pack_data, pack_len);
+    ret = 0;
+  }
+  while (0);
+  if (namebuf != 0)
+    free(namebuf);
+  if (pack_data != 0)
+    my_free(pack_data, MYF(0));
+  if (fd != -1)
+    (void)close(fd);
+  if (data != 0)
+    free(data);
+  return ret;
+}
+
+int
+main(int argc, char** argv)
+{
+  my_progname = "ndb_pack_frm";
+  int ret;
+
+  ndb_init();
+  ndb_opt_set_usage_funcs(short_usage_sub, usage);
+  ret = handle_options(&argc, &argv, my_long_options, ndb_std_get_one_option);
+ if (ret != 0)
+    return NDBT_WRONGARGS;
+
+  for (int i = 0; i < argc; i++)
+  {
+    ret = dofile(argv[i]);
+    if (ret != 0)
+      return NDBT_FAILED;
+  }
+
+  return NDBT_OK;
+}

=== modified file 'storage/ndb/tools/ndb_index_stat.cpp'
--- a/storage/ndb/tools/ndb_index_stat.cpp	2011-06-06 12:18:47 +0000
+++ b/storage/ndb/tools/ndb_index_stat.cpp	2011-06-14 10:42:04 +0000
@@ -20,6 +20,7 @@
 #include <NdbApi.hpp>
 #include <NDBT.hpp>
 #include <NdbIndexStatImpl.hpp>
+#include <ndb_rand.h>
 
 // stats options
 static const char* _dbname = 0;
@@ -93,7 +94,7 @@ doconnect()
 
     g_dic = g_ndb->getDictionary();
 
-    g_ndb_sys = new Ndb(g_ncc, "mysql");
+    g_ndb_sys = new Ndb(g_ncc, NDB_INDEX_STAT_DB);
     CHK2(g_ndb_sys->init() == 0, g_ndb_sys->getNdbError());
     CHK2(g_ndb_sys->waitUntilReady(30) == 0, g_ndb_sys->getNdbError());
 
@@ -207,18 +208,18 @@ doquery()
         NdbIndexStat::Bound& b = (i == 0 ? b_lo : b_hi);
 
         bool strict = false;
-        if (random() % 3 != 0)
+        if (ndb_rand() % 3 != 0)
         {
-          if (random() % 3 != 0)
+          if (ndb_rand() % 3 != 0)
           {
-            Uint32 x = random();
+            Uint32 x = ndb_rand();
             CHK2(g_is->add_bound(b, &x) == 0, g_is->getNdbError());
           }
           else
           {
             CHK2(g_is->add_bound_null(b) == 0, g_is->getNdbError());
           }
-          bool strict = (random() % 2 == 0);
+          bool strict = (ndb_rand() % 2 == 0);
           g_is->set_bound_strict(b, strict);
         }
       }
@@ -251,7 +252,7 @@ dostats(int i)
     if (_delete)
     {
       g_info << g_indname << ": delete stats" << endl;
-      if (random() % 2 == 0)
+      if (ndb_rand() % 2 == 0)
       {
         CHK2(g_dic->deleteIndexStat(*g_ind, *g_tab) == 0, g_dic->getNdbError());
       }
@@ -264,7 +265,7 @@ dostats(int i)
     if (_update)
     {
       g_info << g_indname << ": update stats" << endl;
-      if (random() % 2 == 0)
+      if (ndb_rand() % 2 == 0)
       {
         CHK2(g_dic->updateIndexStat(*g_ind, *g_tab) == 0, g_dic->getNdbError());
       }
@@ -615,8 +616,6 @@ main(int argc, char** argv)
   my_progname = "ndb_index_stat";
   int ret;
 
-  srandom((unsigned)time(0));
-
   ndb_init();
   ndb_opt_set_usage_funcs(short_usage_sub, usage);
   ret = handle_options(&argc, &argv, my_long_options, ndb_std_get_one_option);
@@ -625,6 +624,10 @@ main(int argc, char** argv)
 
   setOutputLevel(_verbose ? 2 : 0);
 
+  unsigned seed = (unsigned)time(0);
+  g_info << "random seed " << seed << endl;
+  ndb_srand(seed);
+
   ret = doall();
   if (ret == -1)
     return NDBT_ProgramExit(NDBT_FAILED);

No bundle (reason: revision is a merge (you can force generation of a bundle with env var BZR_FORCE_BUNDLE=1)).
Thread
bzr commit into mysql-5.1-telco-7.0-spj-scan-vs-scan branch (jonas:3508) jonas oreland19 Jun