List:Commits« Previous MessageNext Message »
From:Jonas Oreland Date:August 17 2011 9:10am
Subject:bzr push into mysql-5.1-telco-7.0 branch (jonas.oreland:4460 to 4461)
View as plain text  
 4461 Jonas Oreland	2011-08-17 [merge]
      ndb - merge wl4124-new2

    removed:
      mysql-test/suite/ndb/r/ndb_statistics.result
      mysql-test/suite/ndb/t/ndb_statistics.test
    added:
      mysql-test/suite/ndb/r/ndb_index_stat.result
      mysql-test/suite/ndb/r/ndb_statistics0.result
      mysql-test/suite/ndb/r/ndb_statistics1.result
      mysql-test/suite/ndb/t/ndb_index_stat.test
      mysql-test/suite/ndb/t/ndb_index_stat_enable.inc
      mysql-test/suite/ndb/t/ndb_statistics.inc
      mysql-test/suite/ndb/t/ndb_statistics0.test
      mysql-test/suite/ndb/t/ndb_statistics1.test
    modified:
      mysql-test/suite/ndb/r/ndb_restore_misc.result
      mysql-test/suite/ndb/t/ndb_restore_misc.test
      sql/ha_ndb_index_stat.cc
      sql/ha_ndb_index_stat.h
      sql/ha_ndbcluster.cc
      sql/ha_ndbcluster.h
      storage/ndb/include/ndb_constants.h
      storage/ndb/include/ndbapi/NdbIndexStat.hpp
      storage/ndb/include/util/NdbPack.hpp
      storage/ndb/src/common/util/NdbPack.cpp
      storage/ndb/src/ndbapi/NdbIndexStat.cpp
      storage/ndb/src/ndbapi/NdbIndexStatImpl.cpp
      storage/ndb/src/ndbapi/NdbIndexStatImpl.hpp
      storage/ndb/test/ndbapi/testIndexStat.cpp
      storage/ndb/tools/ndb_index_stat.cpp
 4460 Ole John Aske	2011-08-17
      SPJ Refactoring of class OrderedFragSet:
      
      ::add() and ::reorganize() 99% duplicated its logic. Removed most of ::add() code
      (except the 'add' part itself) and then let it call ::reorganize() to maintain
      sorted order and handle empty fragments. 
      
      Simplify ::reorganize() logic by taking advantage of that 'm_activeFragCount'
      is known to be '>0' (added assert for this)

    modified:
      storage/ndb/src/ndbapi/NdbQueryOperation.cpp
      storage/ndb/src/ndbapi/NdbQueryOperationImpl.hpp
=== added file 'mysql-test/suite/ndb/r/ndb_index_stat.result'
--- a/mysql-test/suite/ndb/r/ndb_index_stat.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/ndb/r/ndb_index_stat.result	2011-07-23 14:38:08 +0000
@@ -0,0 +1,498 @@
+DROP TABLE IF EXISTS t1, t2;
+set @is_enable_default = @@global.ndb_index_stat_enable;
+set @is_enable = 1;
+set @is_enable = NULL;
+# is_enable_on=1 is_enable_off=0
+# ndb_index_stat_enable - before
+show global variables like 'ndb_index_stat_enable';
+Variable_name	Value
+ndb_index_stat_enable	OFF
+show local variables like 'ndb_index_stat_enable';
+Variable_name	Value
+ndb_index_stat_enable	OFF
+set @@global.ndb_index_stat_enable = 1;
+set @@local.ndb_index_stat_enable = 1;
+# ndb_index_stat_enable - after
+show global variables like 'ndb_index_stat_enable';
+Variable_name	Value
+ndb_index_stat_enable	ON
+show local variables like 'ndb_index_stat_enable';
+Variable_name	Value
+ndb_index_stat_enable	ON
+show global variables like 'ndb_index_stat_option';
+Variable_name	Value
+ndb_index_stat_option	loop_checkon=1000ms,loop_idle=1000ms,loop_busy=100ms,update_batch=1,read_batch=4,idle_batch=32,check_batch=32,check_delay=1m,delete_batch=8,clean_delay=0,error_batch=4,error_delay=1m,evict_batch=8,evict_delay=1m,cache_limit=32M,cache_lowpct=90
+set @save_option = @@global.ndb_index_stat_option;
+set @@global.ndb_index_stat_option = 'loop_idle=3333,cache_limit=44M';
+set @@global.ndb_index_stat_option = 'cache_lowpct=85,evict_delay=55';
+set @@global.ndb_index_stat_option = 'check_delay=234s';
+show global variables like 'ndb_index_stat_option';
+Variable_name	Value
+ndb_index_stat_option	loop_checkon=1000ms,loop_idle=3333ms,loop_busy=100ms,update_batch=1,read_batch=4,idle_batch=32,check_batch=32,check_delay=234s,delete_batch=8,clean_delay=0,error_batch=4,error_delay=1m,evict_batch=8,evict_delay=55s,cache_limit=44M,cache_lowpct=85
+set @@global.ndb_index_stat_option = @save_option;
+show global variables like 'ndb_index_stat_option';
+Variable_name	Value
+ndb_index_stat_option	loop_checkon=1000ms,loop_idle=1000ms,loop_busy=100ms,update_batch=1,read_batch=4,idle_batch=32,check_batch=32,check_delay=1m,delete_batch=8,clean_delay=0,error_batch=4,error_delay=1m,evict_batch=8,evict_delay=1m,cache_limit=32M,cache_lowpct=90
+create table t1 (
+a1 int unsigned not null,
+b1 int unsigned not null,
+c1 int unsigned not null,
+primary key (a1),
+index b1x (b1),
+index c1x (c1)
+) engine=ndb;
+create table t2 (
+a2 int unsigned not null,
+b2 int unsigned not null,
+c2 int unsigned not null,
+primary key (a2),
+index b2x (b2),
+index c2x (c2)
+) engine=ndb;
+analyze table t1, t2;
+Table	Op	Msg_type	Msg_text
+test.t1	analyze	status	OK
+test.t2	analyze	status	OK
+# must use b1x
+explain select * from t1
+where b1 = 5 and c1 = 5;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	ref	b1x,c1x	b1x	4	const	#	Using where with pushed condition
+# must use c2x
+explain select * from t2
+where b2 = 5 and c2 = 5;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t2	ref	b2x,c2x	c2x	4	const	#	Using where with pushed condition
+# must use b1x, c2x
+explain select * from t1, t2
+where c1 = c2 and b1 = 5 and b2 = 5;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	ref	b1x,c1x	b1x	4	const	#	
+1	SIMPLE	t2	ref	b2x,c2x	c2x	4	test.t1.c1	#	Using where with pushed condition
+# must use c2x, b1x
+explain select * from t1, t2
+where b1 = b2 and c1 = 5 and c2 = 5;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t2	ref	b2x,c2x	c2x	4	const	#	
+1	SIMPLE	t1	ref	b1x,c1x	b1x	4	test.t2.b2	#	Using where with pushed condition
+# must use t1, c2x
+explain select * from t1, t2
+where c1 = c2;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	ALL	c1x	NULL	NULL	NULL	#	
+1	SIMPLE	t2	ref	c2x	c2x	4	test.t1.c1	#	
+# must use t2, b1x
+explain select * from t1, t2
+where b1 = b2;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t2	ALL	b2x	NULL	NULL	NULL	#	
+1	SIMPLE	t1	ref	b1x	b1x	4	test.t2.b2	#	
+# should NOT say: Using index for group-by
+explain select distinct (a1) from t1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	ALL	NULL	NULL	NULL	NULL	#	
+# must say: Using index for group by
+explain select distinct (b1) from t1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	NULL	b1x	4	NULL	#	
+# must say: Using index for group by
+explain select distinct (c1) from t1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	index	NULL	c1x	4	NULL	#	
+drop table t1, t2;
+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');
+analyze table t1;
+Table	Op	Msg_type	Msg_text
+test.t1	analyze	status	OK
+set @@local.ndb_index_stat_enable = 0;
+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
+set @@local.ndb_index_stat_enable = 1;
+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
+set @@local.ndb_index_stat_enable = 0;
+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
+set @@local.ndb_index_stat_enable = 1;
+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;
+create table t1 (a int, b int, primary key using hash (a), index x1 (b))
+engine=ndb;
+insert into t1 values (1,11),(2,22),(3,33);
+select * from t1 order by a;
+a	b
+1	11
+2	22
+3	33
+select * from t1 order by a;
+a	b
+1	11
+2	22
+3	33
+analyze table t1;
+Table	Op	Msg_type	Msg_text
+test.t1	analyze	status	OK
+drop table t1;
+create table t1 (
+pk int not null,
+a tinyint not null,
+b tinyint unsigned not null,
+c smallint not null,
+d smallint unsigned not null,
+e mediumint not null,
+f mediumint unsigned not null,
+g int not null,
+h int unsigned not null,
+i bigint not null,
+j bigint unsigned not null,
+k float not null,
+l double not null,
+m decimal not null,
+n decimal unsigned not null,
+primary key using hash (pk),
+index (a),
+index (b),
+index (c),
+index (d),
+index (e),
+index (f),
+index (g),
+index (h),
+index (i),
+index (j),
+index (k),
+index (l),
+index (m),
+index (n)
+) engine=ndb;
+insert into t1 values
+(1,11,11,11,11,11,11,11,11,11,11,11,11,11,11),
+(2,22,22,22,22,22,22,22,22,22,22,22,22,22,22),
+(3,33,33,33,33,33,33,33,33,33,33,33,33,33,33);
+select count(*) from t1 where a > 22;
+count(*)
+1
+select count(*) from t1 where b > 22;
+count(*)
+1
+select count(*) from t1 where c > 22;
+count(*)
+1
+select count(*) from t1 where d > 22;
+count(*)
+1
+select count(*) from t1 where e > 22;
+count(*)
+1
+select count(*) from t1 where f > 22;
+count(*)
+1
+select count(*) from t1 where g > 22;
+count(*)
+1
+select count(*) from t1 where h > 22;
+count(*)
+1
+select count(*) from t1 where i > 22;
+count(*)
+1
+select count(*) from t1 where j > 22;
+count(*)
+1
+select count(*) from t1 where k > 22;
+count(*)
+1
+select count(*) from t1 where l > 22;
+count(*)
+1
+select count(*) from t1 where m > 22;
+count(*)
+1
+select count(*) from t1 where n > 22;
+count(*)
+1
+analyze table t1;
+Table	Op	Msg_type	Msg_text
+test.t1	analyze	status	OK
+select count(*) from t1 where a > 22;
+count(*)
+1
+select count(*) from t1 where b > 22;
+count(*)
+1
+select count(*) from t1 where c > 22;
+count(*)
+1
+select count(*) from t1 where d > 22;
+count(*)
+1
+select count(*) from t1 where e > 22;
+count(*)
+1
+select count(*) from t1 where f > 22;
+count(*)
+1
+select count(*) from t1 where g > 22;
+count(*)
+1
+select count(*) from t1 where h > 22;
+count(*)
+1
+select count(*) from t1 where i > 22;
+count(*)
+1
+select count(*) from t1 where j > 22;
+count(*)
+1
+select count(*) from t1 where k > 22;
+count(*)
+1
+select count(*) from t1 where l > 22;
+count(*)
+1
+select count(*) from t1 where m > 22;
+count(*)
+1
+select count(*) from t1 where n > 22;
+count(*)
+1
+drop table t1;
+create table t1 (
+pk int not null,
+a datetime not null,
+b date not null,
+c year not null,
+d time not null,
+e timestamp not null,
+primary key using hash (pk),
+index (a),
+index (b),
+index (c),
+index (d),
+index (e)
+) engine=ndb;
+insert into t1 values
+(1,'1971-01-01 01:01:01','1971-01-01','1971','01:01:01','1971-01-01 01:01:01'),
+(2,'1972-02-02 02:02:02','1972-02-02','1972','02:02:02','1972-02-02 02:02:02'),
+(3,'1973-03-03 03:03:03','1973-03-03','1973','03:03:03','1973-03-03 03:03:03');
+select count(*) from t1 where a > '1972-02-02 02:02:02';
+count(*)
+1
+select count(*) from t1 where b > '1972-02-02';
+count(*)
+1
+select count(*) from t1 where c > '1972';
+count(*)
+1
+select count(*) from t1 where d > '02:02:02';
+count(*)
+1
+select count(*) from t1 where e > '1972-02-02 02:02:02';
+count(*)
+1
+analyze table t1;
+Table	Op	Msg_type	Msg_text
+test.t1	analyze	status	OK
+select count(*) from t1 where a > '1972-02-02 02:02:02';
+count(*)
+1
+select count(*) from t1 where b > '1972-02-02';
+count(*)
+1
+select count(*) from t1 where c > '1972';
+count(*)
+1
+select count(*) from t1 where d > '02:02:02';
+count(*)
+1
+select count(*) from t1 where e > '1972-02-02 02:02:02';
+count(*)
+1
+drop table t1;
+create table t1 (
+pk int not null,
+a char(10) not null,
+b varchar(10) not null,
+c varchar(1000) not null,
+d binary(10) not null,
+e varbinary(10) not null,
+f varbinary(1000) not null,
+primary key using hash (pk),
+index (a),
+index (b),
+index (c),
+index (d),
+index (e),
+index (f)
+) engine=ndb;
+insert into t1 values
+('1','111','111','111','111','111','111'),
+('2','222','222','222','222','222','222'),
+('3','333','333','333','333','333','333');
+select count(*) from t1 where a > '222';
+count(*)
+1
+select count(*) from t1 where b > '222';
+count(*)
+1
+select count(*) from t1 where c > '222';
+count(*)
+1
+select count(*) from t1 where d > '222';
+count(*)
+2
+select count(*) from t1 where e > '222';
+count(*)
+1
+select count(*) from t1 where f > '222';
+count(*)
+1
+analyze table t1;
+Table	Op	Msg_type	Msg_text
+test.t1	analyze	status	OK
+select count(*) from t1 where a > '222';
+count(*)
+1
+select count(*) from t1 where b > '222';
+count(*)
+1
+select count(*) from t1 where c > '222';
+count(*)
+1
+select count(*) from t1 where d > '222';
+count(*)
+2
+select count(*) from t1 where e > '222';
+count(*)
+1
+select count(*) from t1 where f > '222';
+count(*)
+1
+drop table t1;
+set @is_enable = @is_enable_default;
+set @is_enable = NULL;
+# is_enable_on=0 is_enable_off=1
+# ndb_index_stat_enable - before
+show global variables like 'ndb_index_stat_enable';
+Variable_name	Value
+ndb_index_stat_enable	ON
+show local variables like 'ndb_index_stat_enable';
+Variable_name	Value
+ndb_index_stat_enable	ON
+set @@local.ndb_index_stat_enable = 0;
+set @@global.ndb_index_stat_enable = 0;
+drop table mysql.ndb_index_stat_sample;
+drop table mysql.ndb_index_stat_head;
+# ndb_index_stat_enable - after
+show global variables like 'ndb_index_stat_enable';
+Variable_name	Value
+ndb_index_stat_enable	OFF
+show local variables like 'ndb_index_stat_enable';
+Variable_name	Value
+ndb_index_stat_enable	OFF

=== modified file 'mysql-test/suite/ndb/r/ndb_restore_misc.result'
--- a/mysql-test/suite/ndb/r/ndb_restore_misc.result	2011-05-31 08:28:58 +0000
+++ b/mysql-test/suite/ndb/r/ndb_restore_misc.result	2011-07-19 10:54:29 +0000
@@ -623,8 +623,12 @@ name VARCHAR(255)
 
 ndb_show_tables completed.....
 
+select id into @tmp1 from ndb_show_tables_results
+where name like '%ndb_index_stat_sample_x1%';
 select * from ndb_show_tables_results
 where type like '%Index%'
+and name not like '%ndb_index_stat_sample_x1%'
+and name not like concat('%NDB$INDEX_',@tmp1,'_CUSTOM%')
 order by 1,2,3,4,5,6,7;
 id	type	state	logging	_database	_schema	name
 drop table ndb_show_tables_results;

=== removed file 'mysql-test/suite/ndb/r/ndb_statistics.result'
--- a/mysql-test/suite/ndb/r/ndb_statistics.result	2011-02-28 10:42:04 +0000
+++ b/mysql-test/suite/ndb/r/ndb_statistics.result	1970-01-01 00:00:00 +0000
@@ -1,161 +0,0 @@
-drop table if exists t1, t2, t3, t4;
-CREATE TABLE t10(
-K INT NOT NULL AUTO_INCREMENT,
-I INT, J INT,
-PRIMARY KEY(K),
-KEY(I,J),
-UNIQUE KEY(J,K)
-) ENGINE=ndbcluster;
-INSERT INTO t10(I,J) VALUES (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9),(0,0);
-CREATE TABLE t100 LIKE t10;
-INSERT INTO t100(I,J)
-SELECT X.J, X.J+(10*Y.J) FROM t10 AS X,t10 AS Y;
-CREATE TABLE t10000 LIKE t10;
-INSERT INTO t10000(I,J)
-SELECT X.J, X.J+(100*Y.J) FROM t100 AS X,t100 AS Y
-WHERE X.J<50;
-INSERT INTO t10000(I,J)
-SELECT X.J, X.J+(100*Y.J) FROM t100 AS X,t100 AS Y
-WHERE X.J>=50;
-ANALYZE TABLE t10,t100,t10000;
-Table	Op	Msg_type	Msg_text
-test.t10	analyze	status	OK
-test.t100	analyze	status	OK
-test.t10000	analyze	status	OK
-SELECT COUNT(*) FROM t10;
-COUNT(*)
-10
-SELECT COUNT(*) FROM t100;
-COUNT(*)
-100
-SELECT COUNT(*) FROM t10000;
-COUNT(*)
-10000
-EXPLAIN
-SELECT * FROM t10000 WHERE k = 42;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t10000	const	PRIMARY	PRIMARY	4	const	1	
-EXPLAIN
-SELECT * FROM t10000 WHERE k >= 42 and k < 10000;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t10000	range	PRIMARY	PRIMARY	4	NULL	500	Using where with pushed condition
-EXPLAIN
-SELECT * FROM t10000 WHERE k BETWEEN 42 AND 10000;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t10000	range	PRIMARY	PRIMARY	4	NULL	500	Using where with pushed condition
-EXPLAIN
-SELECT * FROM t10000 WHERE k < 42;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t10000	range	PRIMARY	PRIMARY	4	NULL	1000	Using where with pushed condition
-EXPLAIN
-SELECT * FROM t10000 WHERE k > 42;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t10000	range	PRIMARY	PRIMARY	4	NULL	1000	Using where with pushed condition
-EXPLAIN
-SELECT * FROM t10000 AS X JOIN t10000 AS Y
-ON Y.I=X.I AND Y.J = X.I;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	X	ALL	I	NULL	NULL	NULL	10000	
-1	SIMPLE	Y	ref	J,I	I	10	test.X.I,test.X.I	11	Using where
-EXPLAIN
-SELECT * FROM t100 WHERE k < 42;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t100	range	PRIMARY	PRIMARY	4	NULL	10	Using where with pushed condition
-EXPLAIN
-SELECT * FROM t100 WHERE k > 42;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t100	range	PRIMARY	PRIMARY	4	NULL	10	Using where with pushed condition
-EXPLAIN
-SELECT * FROM t10000 WHERE k < 42;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t10000	range	PRIMARY	PRIMARY	4	NULL	1000	Using where with pushed condition
-EXPLAIN
-SELECT * FROM t10000 WHERE k > 42;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t10000	range	PRIMARY	PRIMARY	4	NULL	1000	Using where with pushed condition
-EXPLAIN
-SELECT * FROM t100 WHERE k BETWEEN 42 AND 10000;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t100	range	PRIMARY	PRIMARY	4	NULL	5	Using where with pushed condition
-EXPLAIN
-SELECT * FROM t10000 WHERE k BETWEEN 42 AND 10000;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t10000	range	PRIMARY	PRIMARY	4	NULL	500	Using where with pushed condition
-EXPLAIN
-SELECT * FROM t10000 WHERE I = 0;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t10000	ref	I	I	5	const	200	Using where with pushed condition
-EXPLAIN
-SELECT * FROM t10000 WHERE J = 0;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t10000	ref	J	J	5	const	100	Using where with pushed condition
-EXPLAIN
-SELECT * FROM t10000 WHERE I = 0 AND J = 0;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t10000	ref	J,I	I	10	const,const	4	Using where with pushed condition
-EXPLAIN
-SELECT * FROM t10000 WHERE I = 0;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t10000	ref	I	I	5	const	200	Using where with pushed condition
-EXPLAIN
-SELECT * FROM t10000 WHERE I = 0 AND J > 1;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t10000	range	J,I	I	10	NULL	100	Using where with pushed condition
-EXPLAIN
-SELECT * FROM t10000 WHERE I = 0 AND J < 1;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t10000	range	J,I	I	10	NULL	50	Using where with pushed condition
-EXPLAIN
-SELECT * FROM t10000 WHERE I = 0 AND J BETWEEN 1 AND 10;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t10000	range	J,I	I	10	NULL	50	Using where with pushed condition
-EXPLAIN
-SELECT * FROM t10000 WHERE I = 0 AND J = 1;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t10000	ref	J,I	I	10	const,const	4	Using where with pushed condition
-EXPLAIN
-SELECT * FROM t10000 WHERE J = 0;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t10000	ref	J	J	5	const	100	Using where with pushed condition
-EXPLAIN
-SELECT * FROM t10000 WHERE J = 0 AND K > 1;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t10000	range	PRIMARY,J	J	9	NULL	50	Using where with pushed condition
-EXPLAIN
-SELECT * FROM t10000 WHERE J = 0 AND K < 1;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t10000	range	PRIMARY,J	J	9	NULL	50	Using where with pushed condition
-EXPLAIN
-SELECT * FROM t10000 WHERE J = 0 AND K BETWEEN 1 AND 10;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t10000	range	PRIMARY,J	J	9	NULL	25	Using where with pushed condition
-EXPLAIN
-SELECT * FROM t10000 WHERE J = 0 AND K = 1;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
-EXPLAIN
-SELECT * FROM t10000 WHERE I = 0 AND J <> 1;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t10000	range	J,I	I	10	NULL	150	Using where with pushed condition
-EXPLAIN
-SELECT * FROM t10000 WHERE I <> 0 AND J = 1;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t10000	ref	J,I	J	5	const	100	Using where with pushed condition
-EXPLAIN
-SELECT * FROM t10000 WHERE I <> 0 AND J <> 1;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t10000	range	J,I	J	5	NULL	1500	Using where with pushed condition
-EXPLAIN
-SELECT * FROM t10000 WHERE J <> 1 AND I = 0;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t10000	range	J,I	I	10	NULL	150	Using where with pushed condition
-EXPLAIN
-SELECT * FROM t10000 WHERE J = 1 AND I <> 0;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t10000	ref	J,I	J	5	const	100	Using where with pushed condition
-EXPLAIN
-SELECT * FROM t10000 WHERE J <> 1 AND I <> 0;
-id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t10000	range	J,I	J	5	NULL	1500	Using where with pushed condition
-DROP TABLE t10,t100,t10000;
-End of 5.1 tests

=== added file 'mysql-test/suite/ndb/r/ndb_statistics0.result'
--- a/mysql-test/suite/ndb/r/ndb_statistics0.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/ndb/r/ndb_statistics0.result	2011-07-02 07:05:32 +0000
@@ -0,0 +1,197 @@
+set @is_enable_default = @@global.ndb_index_stat_enable;
+set @is_enable = 0;
+set @is_enable = NULL;
+# is_enable_on=0 is_enable_off=0
+# ndb_index_stat_enable - before
+show global variables like 'ndb_index_stat_enable';
+Variable_name	Value
+ndb_index_stat_enable	OFF
+show local variables like 'ndb_index_stat_enable';
+Variable_name	Value
+ndb_index_stat_enable	OFF
+# ndb_index_stat_enable - after
+show global variables like 'ndb_index_stat_enable';
+Variable_name	Value
+ndb_index_stat_enable	OFF
+show local variables like 'ndb_index_stat_enable';
+Variable_name	Value
+ndb_index_stat_enable	OFF
+drop table if exists t1, t2, t3, t4;
+CREATE TABLE t10(
+K INT NOT NULL AUTO_INCREMENT,
+I INT, J INT,
+PRIMARY KEY(K),
+KEY(I,J),
+UNIQUE KEY(J,K)
+) ENGINE=ndbcluster
+partition by key (K) partitions 1;
+INSERT INTO t10(I,J) VALUES (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9),(0,0);
+CREATE TABLE t100 LIKE t10;
+INSERT INTO t100(I,J)
+SELECT X.J, X.J+(10*Y.J) FROM t10 AS X,t10 AS Y;
+CREATE TABLE t10000 LIKE t10;
+INSERT INTO t10000(I,J)
+SELECT X.J, X.J+(100*Y.J) FROM t100 AS X,t100 AS Y
+WHERE X.J<50;
+INSERT INTO t10000(I,J)
+SELECT X.J, X.J+(100*Y.J) FROM t100 AS X,t100 AS Y
+WHERE X.J>=50;
+ANALYZE TABLE t10,t100,t10000;
+Table	Op	Msg_type	Msg_text
+test.t10	analyze	status	OK
+test.t100	analyze	status	OK
+test.t10000	analyze	status	OK
+SELECT COUNT(*) FROM t10;
+COUNT(*)
+10
+SELECT COUNT(*) FROM t100;
+COUNT(*)
+100
+SELECT COUNT(*) FROM t10000;
+COUNT(*)
+10000
+EXPLAIN
+SELECT * FROM t10000 WHERE k = 42;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	const	PRIMARY	PRIMARY	4	const	1	
+EXPLAIN
+SELECT * FROM t10000 WHERE k >= 42 and k < 10000;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	PRIMARY	PRIMARY	4	NULL	500	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE k BETWEEN 42 AND 10000;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	PRIMARY	PRIMARY	4	NULL	500	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE k < 42;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	PRIMARY	PRIMARY	4	NULL	1000	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE k > 42;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	PRIMARY	PRIMARY	4	NULL	1000	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 AS X JOIN t10000 AS Y
+ON Y.I=X.I AND Y.J = X.I;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	X	ALL	I	NULL	NULL	NULL	10000	
+1	SIMPLE	Y	ref	J,I	I	10	test.X.I,test.X.I	11	Using where
+EXPLAIN
+SELECT * FROM t100 WHERE k < 42;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t100	range	PRIMARY	PRIMARY	4	NULL	10	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t100 WHERE k > 42;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t100	range	PRIMARY	PRIMARY	4	NULL	10	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE k < 42;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	PRIMARY	PRIMARY	4	NULL	1000	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE k > 42;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	PRIMARY	PRIMARY	4	NULL	1000	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t100 WHERE k BETWEEN 42 AND 10000;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t100	range	PRIMARY	PRIMARY	4	NULL	5	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE k BETWEEN 42 AND 10000;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	PRIMARY	PRIMARY	4	NULL	500	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE I = 0;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	ref	I	I	5	const	200	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE J = 0;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	ref	J	J	5	const	100	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE I = 0 AND J = 0;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	ref	J,I	I	10	const,const	4	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE I = 0;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	ref	I	I	5	const	200	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE I = 0 AND J > 1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	J,I	I	10	NULL	100	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE I = 0 AND J < 1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	J,I	I	10	NULL	50	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE I = 0 AND J BETWEEN 1 AND 10;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	J,I	I	10	NULL	50	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE I = 0 AND J = 1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	ref	J,I	I	10	const,const	4	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE J = 0;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	ref	J	J	5	const	100	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE J = 0 AND K > 1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	PRIMARY,J	J	9	NULL	50	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE J = 0 AND K < 1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	PRIMARY,J	J	9	NULL	50	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE J = 0 AND K BETWEEN 1 AND 10;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	PRIMARY,J	J	9	NULL	25	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE J = 0 AND K = 1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
+EXPLAIN
+SELECT * FROM t10000 WHERE I = 0 AND J <> 1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	J,I	I	10	NULL	150	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE I <> 0 AND J = 1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	ref	J,I	J	5	const	100	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE I <> 0 AND J <> 1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	J,I	J	5	NULL	1500	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE J <> 1 AND I = 0;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	J,I	I	10	NULL	150	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE J = 1 AND I <> 0;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	ref	J,I	J	5	const	100	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE J <> 1 AND I <> 0;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	J,I	J	5	NULL	1500	Using where with pushed condition
+DROP TABLE t10,t100,t10000;
+End of 5.1 tests
+set @is_enable = @is_enable_default;
+set @is_enable = NULL;
+# is_enable_on=0 is_enable_off=0
+# ndb_index_stat_enable - before
+show global variables like 'ndb_index_stat_enable';
+Variable_name	Value
+ndb_index_stat_enable	OFF
+show local variables like 'ndb_index_stat_enable';
+Variable_name	Value
+ndb_index_stat_enable	OFF
+# ndb_index_stat_enable - after
+show global variables like 'ndb_index_stat_enable';
+Variable_name	Value
+ndb_index_stat_enable	OFF
+show local variables like 'ndb_index_stat_enable';
+Variable_name	Value
+ndb_index_stat_enable	OFF

=== added file 'mysql-test/suite/ndb/r/ndb_statistics1.result'
--- a/mysql-test/suite/ndb/r/ndb_statistics1.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/ndb/r/ndb_statistics1.result	2011-07-25 17:11:41 +0000
@@ -0,0 +1,203 @@
+set @is_enable_default = @@global.ndb_index_stat_enable;
+set @is_enable = 1;
+set @is_enable = NULL;
+# is_enable_on=1 is_enable_off=0
+# ndb_index_stat_enable - before
+show global variables like 'ndb_index_stat_enable';
+Variable_name	Value
+ndb_index_stat_enable	OFF
+show local variables like 'ndb_index_stat_enable';
+Variable_name	Value
+ndb_index_stat_enable	OFF
+set @@global.ndb_index_stat_enable = 1;
+set @@local.ndb_index_stat_enable = 1;
+# ndb_index_stat_enable - after
+show global variables like 'ndb_index_stat_enable';
+Variable_name	Value
+ndb_index_stat_enable	ON
+show local variables like 'ndb_index_stat_enable';
+Variable_name	Value
+ndb_index_stat_enable	ON
+drop table if exists t1, t2, t3, t4;
+CREATE TABLE t10(
+K INT NOT NULL AUTO_INCREMENT,
+I INT, J INT,
+PRIMARY KEY(K),
+KEY(I,J),
+UNIQUE KEY(J,K)
+) ENGINE=ndbcluster
+partition by key (K) partitions 1;
+INSERT INTO t10(I,J) VALUES (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9),(0,0);
+CREATE TABLE t100 LIKE t10;
+INSERT INTO t100(I,J)
+SELECT X.J, X.J+(10*Y.J) FROM t10 AS X,t10 AS Y;
+CREATE TABLE t10000 LIKE t10;
+INSERT INTO t10000(I,J)
+SELECT X.J, X.J+(100*Y.J) FROM t100 AS X,t100 AS Y
+WHERE X.J<50;
+INSERT INTO t10000(I,J)
+SELECT X.J, X.J+(100*Y.J) FROM t100 AS X,t100 AS Y
+WHERE X.J>=50;
+ANALYZE TABLE t10,t100,t10000;
+Table	Op	Msg_type	Msg_text
+test.t10	analyze	status	OK
+test.t100	analyze	status	OK
+test.t10000	analyze	status	OK
+SELECT COUNT(*) FROM t10;
+COUNT(*)
+10
+SELECT COUNT(*) FROM t100;
+COUNT(*)
+100
+SELECT COUNT(*) FROM t10000;
+COUNT(*)
+10000
+EXPLAIN
+SELECT * FROM t10000 WHERE k = 42;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	const	PRIMARY	PRIMARY	4	const	1	
+EXPLAIN
+SELECT * FROM t10000 WHERE k >= 42 and k < 10000;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	PRIMARY	PRIMARY	4	NULL	2	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE k BETWEEN 42 AND 10000;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	PRIMARY	PRIMARY	4	NULL	2	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE k < 42;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	PRIMARY	PRIMARY	4	NULL	42	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE k > 42;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	PRIMARY	PRIMARY	4	NULL	9958	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 AS X JOIN t10000 AS Y
+ON Y.I=X.I AND Y.J = X.I;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	X	ALL	I	NULL	NULL	NULL	10000	
+1	SIMPLE	Y	ref	J,I	I	10	test.X.I,test.X.I	1	Using where
+EXPLAIN
+SELECT * FROM t100 WHERE k < 42;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t100	range	PRIMARY	PRIMARY	4	NULL	42	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t100 WHERE k > 42;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t100	range	PRIMARY	PRIMARY	4	NULL	58	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE k < 42;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	PRIMARY	PRIMARY	4	NULL	42	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE k > 42;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	PRIMARY	PRIMARY	4	NULL	9958	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t100 WHERE k BETWEEN 42 AND 10000;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t100	range	PRIMARY	PRIMARY	4	NULL	2	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE k BETWEEN 42 AND 10000;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	PRIMARY	PRIMARY	4	NULL	2	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE I = 0;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	ref	I	I	5	const	100	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE J = 0;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	ref	J	J	5	const	2	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE I = 0 AND J = 0;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	ref	J,I	J	5	const	2	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE I = 0;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	ref	I	I	5	const	100	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE I = 0 AND J > 1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	J,I	I	10	NULL	99	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE I = 0 AND J < 1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	J,I	J	5	NULL	2	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE I = 0 AND J BETWEEN 1 AND 10;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	J,I	J	5	NULL	2	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE I = 0 AND J = 1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	ref	J,I	J	5	const	2	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE J = 0;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	ref	J	J	5	const	2	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE J = 0 AND K > 1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	PRIMARY,J	J	9	NULL	2	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE J = 0 AND K < 1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	PRIMARY,J	PRIMARY	4	NULL	2	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE J = 0 AND K BETWEEN 1 AND 10;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	PRIMARY,J	PRIMARY	4	NULL	2	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE J = 0 AND K = 1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	NULL	NULL	NULL	NULL	NULL	NULL	NULL	Impossible WHERE noticed after reading const tables
+EXPLAIN
+SELECT * FROM t10000 WHERE I = 0 AND J <> 1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	J,I	I	10	NULL	101	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE I <> 0 AND J = 1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	ref	J,I	J	5	const	2	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE I <> 0 AND J <> 1;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	J,I	I	5	NULL	9902	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE J <> 1 AND I = 0;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	J,I	I	10	NULL	101	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE J = 1 AND I <> 0;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	ref	J,I	J	5	const	2	Using where with pushed condition
+EXPLAIN
+SELECT * FROM t10000 WHERE J <> 1 AND I <> 0;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t10000	range	J,I	I	5	NULL	9902	Using where with pushed condition
+DROP TABLE t10,t100,t10000;
+End of 5.1 tests
+set @is_enable = @is_enable_default;
+set @is_enable = NULL;
+# is_enable_on=0 is_enable_off=1
+# ndb_index_stat_enable - before
+show global variables like 'ndb_index_stat_enable';
+Variable_name	Value
+ndb_index_stat_enable	ON
+show local variables like 'ndb_index_stat_enable';
+Variable_name	Value
+ndb_index_stat_enable	ON
+set @@local.ndb_index_stat_enable = 0;
+set @@global.ndb_index_stat_enable = 0;
+drop table mysql.ndb_index_stat_sample;
+drop table mysql.ndb_index_stat_head;
+# ndb_index_stat_enable - after
+show global variables like 'ndb_index_stat_enable';
+Variable_name	Value
+ndb_index_stat_enable	OFF
+show local variables like 'ndb_index_stat_enable';
+Variable_name	Value
+ndb_index_stat_enable	OFF

=== added file 'mysql-test/suite/ndb/t/ndb_index_stat.test'
--- a/mysql-test/suite/ndb/t/ndb_index_stat.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/ndb/t/ndb_index_stat.test	2011-07-23 14:35:37 +0000
@@ -0,0 +1,313 @@
+-- source include/have_ndb.inc
+
+# Notes on index stats in *.test.
+#
+# Most tables here have few table rows.  Index stats are not very
+# useful in such cases but the optimizer seems to use them anyway.
+# One reason may be that nested joins is only join method.
+#
+# In real production index stats are computed daily or weekly.
+# But tests here must compute them at once if "explain" is used.
+# Thus: insert (or other dml) - analyze table - explain.
+#
+# Index stats are approximate since only one replica was scanned
+# and values are interpolated from samples.  MTR however should be
+# deterministic.  If not use --replace column 9 # (rows).
+
+--disable_warnings
+DROP TABLE IF EXISTS t1, t2;
+--enable_warnings
+
+set @is_enable_default = @@global.ndb_index_stat_enable;
+
+set @is_enable = 1;
+source ndb_index_stat_enable.inc;
+
+# test changing suboptions
+show global variables like 'ndb_index_stat_option';
+set @save_option = @@global.ndb_index_stat_option;
+# some options
+set @@global.ndb_index_stat_option = 'loop_idle=3333,cache_limit=44M';
+set @@global.ndb_index_stat_option = 'cache_lowpct=85,evict_delay=55';
+set @@global.ndb_index_stat_option = 'check_delay=234s';
+show global variables like 'ndb_index_stat_option';
+set @@global.ndb_index_stat_option = @save_option;
+show global variables like 'ndb_index_stat_option';
+
+# TEST: main
+create table t1 (
+  a1 int unsigned not null,
+  b1 int unsigned not null,
+  c1 int unsigned not null,
+  primary key (a1),
+  index b1x (b1),
+  index c1x (c1)
+) engine=ndb;
+
+create table t2 (
+  a2 int unsigned not null,
+  b2 int unsigned not null,
+  c2 int unsigned not null,
+  primary key (a2),
+  index b2x (b2),
+  index c2x (c2)
+) engine=ndb;
+
+# enough rows to make index stats more approximate
+
+--disable_query_log
+let $i = 1000;
+while ($i)
+{
+  dec $i;
+  eval insert into t1 values ($i, $i % 100, $i % 10);
+}
+let $i = 1000;
+while ($i)
+{
+  dec $i;
+  eval insert into t2 values ($i, $i % 10, $i % 100);
+}
+--enable_query_log
+analyze table t1, t2;
+
+# TEST: key equal constant
+
+--echo # must use b1x
+--replace_column 9 #
+explain select * from t1
+  where b1 = 5 and c1 = 5;
+
+--echo # must use c2x
+--replace_column 9 #
+explain select * from t2
+  where b2 = 5 and c2 = 5;
+
+# TEST: keys equal constant in join
+
+--echo # must use b1x, c2x
+--replace_column 9 #
+explain select * from t1, t2
+  where c1 = c2 and b1 = 5 and b2 = 5;
+
+--echo # must use c2x, b1x
+--replace_column 9 #
+explain select * from t1, t2
+  where b1 = b2 and c1 = 5 and c2 = 5;
+
+# TEST: join via keys of different selectivity
+
+--echo # must use t1, c2x
+--replace_column 9 #
+explain select * from t1, t2
+  where c1 = c2;
+--echo # must use t2, b1x
+--replace_column 9 #
+explain select * from t1, t2
+  where b1 = b2;
+
+# TEST: bug#44760 quick distinct
+# QUICK_GROUP_MIN_MAX_SELECT says "Using index for group-by".
+# Should happen only for low cardinality index.
+# wl4124_todo: result is wrong until HA_KEYREAD_ONLY is set
+
+--echo # should NOT say: Using index for group-by
+--replace_column 9 #
+explain select distinct (a1) from t1;
+
+--echo # must say: Using index for group by
+--replace_column 9 #
+explain select distinct (b1) from t1;
+
+--echo # must say: Using index for group by
+--replace_column 9 #
+explain select distinct (c1) from t1;
+
+# TEST: end
+drop table t1, t2;
+
+# turn index stats OFF in client (falls back on other methods)
+# code snippet moved from ndb_index_ordered.test
+
+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');
+analyze table t1;
+let $is_loop = 4;
+while ($is_loop)
+{
+  # 4-OFF 3-ON 2-OFF 1-ON
+  let $is_enable = `select ($is_loop=3 or $is_loop=1)`;
+  dec $is_loop;
+  eval set @@local.ndb_index_stat_enable = $is_enable;
+
+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;
+
+# bug#XXXXX
+# autocreate=false,enable=1 is now acceptable
+# following gives warning while "no stats" is counted as error
+create table t1 (a int, b int, primary key using hash (a), index x1 (b))
+engine=ndb;
+insert into t1 values (1,11),(2,22),(3,33);
+# make_join_statistics() -> info() -> ndb_index_stat_set_rpk()
+# error 4715 - no stats
+select * from t1 order by a;
+# error 9003 suppressed - previous recent error
+select * from t1 order by a;
+# analyze clears previous error at once
+analyze table t1;
+drop table t1;
+
+# bug#XXXXX
+# wrong byte size from some types to NdbPack
+# before error fixes causes stats to be ignored silently (error 4716)
+# best seen with debug and export NDB_PACK_ABORT_ON_ERROR=1
+# affected types: mediumint datetime date time timestamp
+
+create table t1 (
+  pk int not null,
+  a tinyint not null,
+  b tinyint unsigned not null,
+  c smallint not null,
+  d smallint unsigned not null,
+  e mediumint not null,
+  f mediumint unsigned not null,
+  g int not null,
+  h int unsigned not null,
+  i bigint not null,
+  j bigint unsigned not null,
+  k float not null,
+  l double not null,
+  m decimal not null,
+  n decimal unsigned not null,
+  primary key using hash (pk),
+  index (a),
+  index (b),
+  index (c),
+  index (d),
+  index (e),
+  index (f),
+  index (g),
+  index (h),
+  index (i),
+  index (j),
+  index (k),
+  index (l),
+  index (m),
+  index (n)
+) engine=ndb;
+insert into t1 values
+(1,11,11,11,11,11,11,11,11,11,11,11,11,11,11),
+(2,22,22,22,22,22,22,22,22,22,22,22,22,22,22),
+(3,33,33,33,33,33,33,33,33,33,33,33,33,33,33);
+let $i = 2;
+while ($i)
+{
+  dec $i;
+  if (!$i)
+  {
+    eval analyze table t1;
+  }
+  eval select count(*) from t1 where a > 22;
+  eval select count(*) from t1 where b > 22;
+  eval select count(*) from t1 where c > 22;
+  eval select count(*) from t1 where d > 22;
+  eval select count(*) from t1 where e > 22;
+  eval select count(*) from t1 where f > 22;
+  eval select count(*) from t1 where g > 22;
+  eval select count(*) from t1 where h > 22;
+  eval select count(*) from t1 where i > 22;
+  eval select count(*) from t1 where j > 22;
+  eval select count(*) from t1 where k > 22;
+  eval select count(*) from t1 where l > 22;
+  eval select count(*) from t1 where m > 22;
+  eval select count(*) from t1 where n > 22;
+}
+drop table t1;
+
+create table t1 (
+  pk int not null,
+  a datetime not null,
+  b date not null,
+  c year not null,
+  d time not null,
+  e timestamp not null,
+  primary key using hash (pk),
+  index (a),
+  index (b),
+  index (c),
+  index (d),
+  index (e)
+) engine=ndb;
+insert into t1 values
+(1,'1971-01-01 01:01:01','1971-01-01','1971','01:01:01','1971-01-01 01:01:01'),
+(2,'1972-02-02 02:02:02','1972-02-02','1972','02:02:02','1972-02-02 02:02:02'),
+(3,'1973-03-03 03:03:03','1973-03-03','1973','03:03:03','1973-03-03 03:03:03');
+let $i = 2;
+while ($i)
+{
+  dec $i;
+  if (!$i)
+  {
+    eval analyze table t1;
+  }
+  eval select count(*) from t1 where a > '1972-02-02 02:02:02';
+  eval select count(*) from t1 where b > '1972-02-02';
+  eval select count(*) from t1 where c > '1972';
+  eval select count(*) from t1 where d > '02:02:02';
+  eval select count(*) from t1 where e > '1972-02-02 02:02:02';
+}
+drop table t1;
+
+create table t1 (
+  pk int not null,
+  a char(10) not null,
+  b varchar(10) not null,
+  c varchar(1000) not null,
+  d binary(10) not null,
+  e varbinary(10) not null,
+  f varbinary(1000) not null,
+  primary key using hash (pk),
+  index (a),
+  index (b),
+  index (c),
+  index (d),
+  index (e),
+  index (f)
+) engine=ndb;
+insert into t1 values
+('1','111','111','111','111','111','111'),
+('2','222','222','222','222','222','222'),
+('3','333','333','333','333','333','333');
+let $i = 2;
+while ($i)
+{
+  dec $i;
+  if (!$i)
+  {
+    eval analyze table t1;
+  }
+  eval select count(*) from t1 where a > '222';
+  eval select count(*) from t1 where b > '222';
+  eval select count(*) from t1 where c > '222';
+  eval select count(*) from t1 where d > '222';
+  eval select count(*) from t1 where e > '222';
+  eval select count(*) from t1 where f > '222';
+}
+drop table t1;
+
+set @is_enable = @is_enable_default;
+source ndb_index_stat_enable.inc;

=== added file 'mysql-test/suite/ndb/t/ndb_index_stat_enable.inc'
--- a/mysql-test/suite/ndb/t/ndb_index_stat_enable.inc	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/ndb/t/ndb_index_stat_enable.inc	2011-07-02 07:05:32 +0000
@@ -0,0 +1,39 @@
+# turn ndb_index_stat_enable ON or OFF
+# caller sets @is_enable 0/1
+# based on global variable, local follows global
+# do nothing if value is already correct
+# setting OFF drops stats tables to avoid MTR diff
+
+let is_enable_on = `select @is_enable and not @@global.ndb_index_stat_enable`;
+let is_enable_off = `select not @is_enable and @@global.ndb_index_stat_enable`;
+set @is_enable = NULL;
+
+--echo # is_enable_on=$is_enable_on is_enable_off=$is_enable_off
+
+--echo # ndb_index_stat_enable - before
+show global variables like 'ndb_index_stat_enable';
+show local variables like 'ndb_index_stat_enable';
+
+if ($is_enable_on)
+{
+  # first global
+  eval set @@global.ndb_index_stat_enable = 1;
+  eval set @@local.ndb_index_stat_enable = 1;
+
+  # stats thread creates stats tables
+}
+
+if ($is_enable_off)
+{
+  # first local
+  eval set @@local.ndb_index_stat_enable = 0;
+  eval set @@global.ndb_index_stat_enable = 0;
+
+  # stats thread does not (and must not) drop stats tables
+  eval drop table mysql.ndb_index_stat_sample;
+  eval drop table mysql.ndb_index_stat_head;
+}
+
+--echo # ndb_index_stat_enable - after
+show global variables like 'ndb_index_stat_enable';
+show local variables like 'ndb_index_stat_enable';

=== modified file 'mysql-test/suite/ndb/t/ndb_restore_misc.test'
--- a/mysql-test/suite/ndb/t/ndb_restore_misc.test	2011-05-31 08:28:58 +0000
+++ b/mysql-test/suite/ndb/t/ndb_restore_misc.test	2011-07-19 10:54:29 +0000
@@ -546,8 +546,15 @@ CREATE TEMPORARY TABLE IF NOT EXISTS ndb
   name VARCHAR(255)
 );
 --source ndb_show_tables_result.inc
+# the db fields include single quotes...
+--disable_warnings
+select id into @tmp1 from ndb_show_tables_results
+where name like '%ndb_index_stat_sample_x1%';
+--enable_warnings
 select * from ndb_show_tables_results
 where type like '%Index%'
+and name not like '%ndb_index_stat_sample_x1%'
+and name not like concat('%NDB$INDEX_',@tmp1,'_CUSTOM%')
 order by 1,2,3,4,5,6,7;
 drop table ndb_show_tables_results;
 

=== added file 'mysql-test/suite/ndb/t/ndb_statistics.inc'
--- a/mysql-test/suite/ndb/t/ndb_statistics.inc	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/ndb/t/ndb_statistics.inc	2011-07-02 07:05:32 +0000
@@ -0,0 +1,145 @@
+-- source include/have_ndb.inc
+
+--disable_warnings
+drop table if exists t1, t2, t3, t4;
+--enable_warnings
+
+CREATE TABLE t10(
+  K INT NOT NULL AUTO_INCREMENT,
+  I INT, J INT,
+  PRIMARY KEY(K),
+  KEY(I,J),
+  UNIQUE KEY(J,K)
+) ENGINE=ndbcluster
+  partition by key (K) partitions 1;
+
+INSERT INTO t10(I,J) VALUES (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9),(0,0);
+
+CREATE TABLE t100 LIKE t10;
+INSERT INTO t100(I,J)
+  SELECT X.J, X.J+(10*Y.J) FROM t10 AS X,t10 AS Y;
+
+CREATE TABLE t10000 LIKE t10;
+
+# Insert into t10000 in two chunks to not
+#  exhaust MaxNoOfConcurrentOperations
+INSERT INTO t10000(I,J)
+  SELECT X.J, X.J+(100*Y.J) FROM t100 AS X,t100 AS Y
+  WHERE X.J<50;
+INSERT INTO t10000(I,J)
+  SELECT X.J, X.J+(100*Y.J) FROM t100 AS X,t100 AS Y
+  WHERE X.J>=50;
+
+ANALYZE TABLE t10,t100,t10000;
+
+SELECT COUNT(*) FROM t10;
+SELECT COUNT(*) FROM t100;
+SELECT COUNT(*) FROM t10000;
+
+#
+# Bug #59517: Incorrect detection of single row access in
+#             ha_ndbcluster::records_in_range()
+
+# Expect a single row (or const) when PK is excact specified
+EXPLAIN
+SELECT * FROM t10000 WHERE k = 42;
+
+# All queries below should *not* return a single row
+EXPLAIN
+SELECT * FROM t10000 WHERE k >= 42 and k < 10000;
+EXPLAIN
+SELECT * FROM t10000 WHERE k BETWEEN 42 AND 10000;
+EXPLAIN
+SELECT * FROM t10000 WHERE k < 42;
+EXPLAIN
+SELECT * FROM t10000 WHERE k > 42;
+
+#
+# Bug #59519 ::set_rec_per_key() assumes ORDER_INDEX to be unique
+#
+
+# 'REF' join of 'Y' should match >1 rows
+EXPLAIN
+SELECT * FROM t10000 AS X JOIN t10000 AS Y
+  ON Y.I=X.I AND Y.J = X.I;
+
+#
+# Bug #11804277: INCORRECT INDEX MAY BE SELECTED DUE TO INSUFFICIENT 
+#                STATISTICS FROM CLUSTER
+#
+
+# Open bounded range should return 10% of #rows in table
+EXPLAIN
+SELECT * FROM t100 WHERE k < 42;
+EXPLAIN
+SELECT * FROM t100 WHERE k > 42;
+EXPLAIN
+SELECT * FROM t10000 WHERE k < 42;
+EXPLAIN
+SELECT * FROM t10000 WHERE k > 42;
+
+#Closed bounded range should return 5% of #rows in table
+EXPLAIN
+SELECT * FROM t100 WHERE k BETWEEN 42 AND 10000;
+EXPLAIN
+SELECT * FROM t10000 WHERE k BETWEEN 42 AND 10000;
+
+#EQ-range selectivity depends on
+#  - key length specified
+#  - #rows in table.
+#  - unique/non-unique index
+#  - min 2% selectivity
+#
+#  Possibly combined with open/closed ranges as
+#  above which further improves selectivity
+#
+EXPLAIN
+SELECT * FROM t10000 WHERE I = 0;
+EXPLAIN
+SELECT * FROM t10000 WHERE J = 0;
+
+EXPLAIN
+SELECT * FROM t10000 WHERE I = 0 AND J = 0;
+
+EXPLAIN
+SELECT * FROM t10000 WHERE I = 0;
+EXPLAIN
+SELECT * FROM t10000 WHERE I = 0 AND J > 1;
+EXPLAIN
+SELECT * FROM t10000 WHERE I = 0 AND J < 1;
+EXPLAIN
+SELECT * FROM t10000 WHERE I = 0 AND J BETWEEN 1 AND 10;
+EXPLAIN
+SELECT * FROM t10000 WHERE I = 0 AND J = 1;
+
+EXPLAIN
+SELECT * FROM t10000 WHERE J = 0;
+EXPLAIN
+SELECT * FROM t10000 WHERE J = 0 AND K > 1;
+EXPLAIN
+SELECT * FROM t10000 WHERE J = 0 AND K < 1;
+EXPLAIN
+SELECT * FROM t10000 WHERE J = 0 AND K BETWEEN 1 AND 10;
+EXPLAIN
+SELECT * FROM t10000 WHERE J = 0 AND K = 1;
+
+## Verify selection of 'best' index
+## (The one of index I/J being EQ)
+EXPLAIN
+SELECT * FROM t10000 WHERE I = 0 AND J <> 1;
+EXPLAIN
+SELECT * FROM t10000 WHERE I <> 0 AND J = 1;
+EXPLAIN
+SELECT * FROM t10000 WHERE I <> 0 AND J <> 1;
+
+EXPLAIN
+SELECT * FROM t10000 WHERE J <> 1 AND I = 0;
+EXPLAIN
+SELECT * FROM t10000 WHERE J = 1 AND I <> 0;
+EXPLAIN
+SELECT * FROM t10000 WHERE J <> 1 AND I <> 0;
+
+
+DROP TABLE t10,t100,t10000;
+
+--echo End of 5.1 tests

=== removed file 'mysql-test/suite/ndb/t/ndb_statistics.test'
--- a/mysql-test/suite/ndb/t/ndb_statistics.test	2011-02-28 10:42:04 +0000
+++ b/mysql-test/suite/ndb/t/ndb_statistics.test	1970-01-01 00:00:00 +0000
@@ -1,144 +0,0 @@
--- source include/have_ndb.inc
-
---disable_warnings
-drop table if exists t1, t2, t3, t4;
---enable_warnings
-
-CREATE TABLE t10(
-  K INT NOT NULL AUTO_INCREMENT,
-  I INT, J INT,
-  PRIMARY KEY(K),
-  KEY(I,J),
-  UNIQUE KEY(J,K)
-) ENGINE=ndbcluster;
-
-INSERT INTO t10(I,J) VALUES (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8),(9,9),(0,0);
-
-CREATE TABLE t100 LIKE t10;
-INSERT INTO t100(I,J)
-  SELECT X.J, X.J+(10*Y.J) FROM t10 AS X,t10 AS Y;
-
-CREATE TABLE t10000 LIKE t10;
-
-# Insert into t10000 in two chunks to not
-#  exhaust MaxNoOfConcurrentOperations
-INSERT INTO t10000(I,J)
-  SELECT X.J, X.J+(100*Y.J) FROM t100 AS X,t100 AS Y
-  WHERE X.J<50;
-INSERT INTO t10000(I,J)
-  SELECT X.J, X.J+(100*Y.J) FROM t100 AS X,t100 AS Y
-  WHERE X.J>=50;
-
-ANALYZE TABLE t10,t100,t10000;
-
-SELECT COUNT(*) FROM t10;
-SELECT COUNT(*) FROM t100;
-SELECT COUNT(*) FROM t10000;
-
-#
-# Bug #59517: Incorrect detection of single row access in
-#             ha_ndbcluster::records_in_range()
-
-# Expect a single row (or const) when PK is excact specified
-EXPLAIN
-SELECT * FROM t10000 WHERE k = 42;
-
-# All queries below should *not* return a single row
-EXPLAIN
-SELECT * FROM t10000 WHERE k >= 42 and k < 10000;
-EXPLAIN
-SELECT * FROM t10000 WHERE k BETWEEN 42 AND 10000;
-EXPLAIN
-SELECT * FROM t10000 WHERE k < 42;
-EXPLAIN
-SELECT * FROM t10000 WHERE k > 42;
-
-#
-# Bug #59519 ::set_rec_per_key() assumes ORDER_INDEX to be unique
-#
-
-# 'REF' join of 'Y' should match >1 rows
-EXPLAIN
-SELECT * FROM t10000 AS X JOIN t10000 AS Y
-  ON Y.I=X.I AND Y.J = X.I;
-
-#
-# Bug #11804277: INCORRECT INDEX MAY BE SELECTED DUE TO INSUFFICIENT 
-#                STATISTICS FROM CLUSTER
-#
-
-# Open bounded range should return 10% of #rows in table
-EXPLAIN
-SELECT * FROM t100 WHERE k < 42;
-EXPLAIN
-SELECT * FROM t100 WHERE k > 42;
-EXPLAIN
-SELECT * FROM t10000 WHERE k < 42;
-EXPLAIN
-SELECT * FROM t10000 WHERE k > 42;
-
-#Closed bounded range should return 5% of #rows in table
-EXPLAIN
-SELECT * FROM t100 WHERE k BETWEEN 42 AND 10000;
-EXPLAIN
-SELECT * FROM t10000 WHERE k BETWEEN 42 AND 10000;
-
-#EQ-range selectivity depends on
-#  - key length specified
-#  - #rows in table.
-#  - unique/non-unique index
-#  - min 2% selectivity
-#
-#  Possibly combined with open/closed ranges as
-#  above which further improves selectivity
-#
-EXPLAIN
-SELECT * FROM t10000 WHERE I = 0;
-EXPLAIN
-SELECT * FROM t10000 WHERE J = 0;
-
-EXPLAIN
-SELECT * FROM t10000 WHERE I = 0 AND J = 0;
-
-EXPLAIN
-SELECT * FROM t10000 WHERE I = 0;
-EXPLAIN
-SELECT * FROM t10000 WHERE I = 0 AND J > 1;
-EXPLAIN
-SELECT * FROM t10000 WHERE I = 0 AND J < 1;
-EXPLAIN
-SELECT * FROM t10000 WHERE I = 0 AND J BETWEEN 1 AND 10;
-EXPLAIN
-SELECT * FROM t10000 WHERE I = 0 AND J = 1;
-
-EXPLAIN
-SELECT * FROM t10000 WHERE J = 0;
-EXPLAIN
-SELECT * FROM t10000 WHERE J = 0 AND K > 1;
-EXPLAIN
-SELECT * FROM t10000 WHERE J = 0 AND K < 1;
-EXPLAIN
-SELECT * FROM t10000 WHERE J = 0 AND K BETWEEN 1 AND 10;
-EXPLAIN
-SELECT * FROM t10000 WHERE J = 0 AND K = 1;
-
-## Verify selection of 'best' index
-## (The one of index I/J being EQ)
-EXPLAIN
-SELECT * FROM t10000 WHERE I = 0 AND J <> 1;
-EXPLAIN
-SELECT * FROM t10000 WHERE I <> 0 AND J = 1;
-EXPLAIN
-SELECT * FROM t10000 WHERE I <> 0 AND J <> 1;
-
-EXPLAIN
-SELECT * FROM t10000 WHERE J <> 1 AND I = 0;
-EXPLAIN
-SELECT * FROM t10000 WHERE J = 1 AND I <> 0;
-EXPLAIN
-SELECT * FROM t10000 WHERE J <> 1 AND I <> 0;
-
-
-DROP TABLE t10,t100,t10000;
-
---echo End of 5.1 tests

=== added file 'mysql-test/suite/ndb/t/ndb_statistics0.test'
--- a/mysql-test/suite/ndb/t/ndb_statistics0.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/ndb/t/ndb_statistics0.test	2011-07-02 07:05:32 +0000
@@ -0,0 +1,11 @@
+# index stats OFF
+
+set @is_enable_default = @@global.ndb_index_stat_enable;
+
+set @is_enable = 0;
+source ndb_index_stat_enable.inc;
+
+--source ndb_statistics.inc
+
+set @is_enable = @is_enable_default;
+source ndb_index_stat_enable.inc;

=== added file 'mysql-test/suite/ndb/t/ndb_statistics1.test'
--- a/mysql-test/suite/ndb/t/ndb_statistics1.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/ndb/t/ndb_statistics1.test	2011-07-02 07:05:32 +0000
@@ -0,0 +1,11 @@
+# index stats ON
+
+set @is_enable_default = @@global.ndb_index_stat_enable;
+
+set @is_enable = 1;
+source ndb_index_stat_enable.inc;
+
+--source ndb_statistics.inc
+
+set @is_enable = @is_enable_default;
+source ndb_index_stat_enable.inc;

=== modified file 'sql/ha_ndb_index_stat.cc'
--- a/sql/ha_ndb_index_stat.cc	2011-07-01 09:00:54 +0000
+++ b/sql/ha_ndb_index_stat.cc	2011-08-16 11:53:13 +0000
@@ -62,6 +62,7 @@ struct Ndb_index_stat {
   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 */
+  bool no_stats;        /* have detected that no stats exist */
   NdbIndexStat::Error error;
   time_t error_time;
   int error_count;
@@ -498,40 +499,33 @@ struct Ndb_index_stat_glob {
   uint total_count;
   uint force_update;
   uint wait_update;
+  uint no_stats;
   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),
+    no_stats(0),
     cache_query_bytes(0),
-    cache_clean_bytes(0),
-    is_locked(false)
+    cache_clean_bytes(0)
   {
   }
   void set_list_count()
   {
+    total_count= 0;
     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;
+      total_count++;
     }
   }
-  void lock()
+  void set_status_variables()
   {
-    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);
   }
 };
 
@@ -554,6 +548,7 @@ Ndb_index_stat::Ndb_index_stat()
   check_time= 0;
   cache_clean= false;
   force_update= 0;
+  no_stats= false;
   error_time= 0;
   error_count= 0;
   share_next= 0;
@@ -567,7 +562,6 @@ Ndb_index_stat::Ndb_index_stat()
 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)
@@ -581,13 +575,18 @@ ndb_index_stat_error(Ndb_index_stat *st,
   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));
 }
 
+void
+ndb_index_stat_clear_error(Ndb_index_stat *st)
+{
+  st->error.code= 0;
+  st->error.status= NdbError::Success;
+}
+
 /* Lists across shares */
 
 Ndb_index_stat_list::Ndb_index_stat_list(int the_lt, const char* the_name)
@@ -611,9 +610,8 @@ Ndb_index_stat_list ndb_index_stat_list[
 };
 
 void
-ndb_index_stat_list_add(Ndb_index_stat* st, int lt, int place= +1)
+ndb_index_stat_list_add(Ndb_index_stat* st, int lt)
 {
-  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);
@@ -627,13 +625,6 @@ ndb_index_stat_list_add(Ndb_index_stat*
     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);
@@ -642,9 +633,6 @@ ndb_index_stat_list_add(Ndb_index_stat*
     list.tail= st;
   }
   list.count++;
-  glob.lock();
-  glob.total_count++;
-  glob.unlock();
 
   st->lt= lt;
 }
@@ -652,7 +640,6 @@ ndb_index_stat_list_add(Ndb_index_stat*
 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);
@@ -669,10 +656,6 @@ ndb_index_stat_list_remove(Ndb_index_sta
     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;
@@ -686,66 +669,85 @@ ndb_index_stat_list_remove(Ndb_index_sta
 }
 
 void
-ndb_index_stat_list_move(Ndb_index_stat *st, int lt, int place= +1)
+ndb_index_stat_list_move(Ndb_index_stat *st, int lt)
 {
   assert(st != 0);
   ndb_index_stat_list_remove(st);
-  ndb_index_stat_list_add(st, lt, place);
+  ndb_index_stat_list_add(st, lt);
 }
 
-/* Move entry in / out error list */
+/* Stats entry changes (must hold stat_mutex) */
 
 void
-ndb_index_stat_list_to_error(Ndb_index_stat *st)
+ndb_index_stat_force_update(Ndb_index_stat *st, bool onoff)
 {
   Ndb_index_stat_glob &glob= ndb_index_stat_glob;
-
-  assert(st != 0);
-  const int lt= st->lt; NDB_IGNORE_VALUE(lt);
-  assert(1 <= lt && lt < Ndb_index_stat::LT_Count);
-  assert(lt != Ndb_index_stat::LT_Error);
-
-  if (st->force_update != 0)
+  if (onoff)
   {
-    glob.lock();
+    /* One more request */
+    glob.force_update++;
+    st->force_update++;
+  }
+  else
+  {
+    /* All done */
     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)
+ndb_index_stat_no_stats(Ndb_index_stat *st, bool flag)
 {
-  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;
+  Ndb_index_stat_glob &glob= ndb_index_stat_glob;
+  if (st->no_stats != flag)
+  {
+    if (flag)
+    {
+      glob.no_stats++;
+      st->no_stats= true;
+    }
+    else
+    {
+      assert(glob.no_stats >= 1);
+      glob.no_stats-= 1;
+      st->no_stats= false;
+    }
+  }
 }
 
 /* Find or add entry under the share */
 
 Ndb_index_stat*
-ndb_index_stat_alloc()
+ndb_index_stat_alloc(const NDBINDEX *index,
+                     const NDBTAB *table,
+                     int &err_out)
 {
+  err_out= 0;
   Ndb_index_stat *st= new Ndb_index_stat;
   NdbIndexStat *is= new NdbIndexStat;
   if (st != 0 && is != 0)
   {
     st->is= is;
-    return 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 (is->set_index(*index, *table) == 0)
+      return st;
+    ndb_index_stat_error(st, "set_index", __LINE__);
+    err_out= st->error.code;
+  }
+  else
+  {
+    err_out= NdbIndexStat::NoMemError;
   }
-  delete is;
-  delete st;
+  if (is != 0)
+    delete is;
+  if (st != 0)
+    delete st;
   return 0;
 }
 
@@ -773,70 +775,65 @@ ndb_index_stat_find_share(NDB_SHARE *sha
 }
 
 /* Subroutine, have lock */
-Ndb_index_stat*
+void
 ndb_index_stat_add_share(NDB_SHARE *share,
-                         const NDBINDEX *index,
-                         const NDBTAB *table,
+                         Ndb_index_stat *st,
                          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;
+  st->share= share;
+  if (st_last == 0)
+    share->index_stat_list= st;
+  else
+    st_last->share_next= st;
 }
 
 Ndb_index_stat*
 ndb_index_stat_get_share(NDB_SHARE *share,
                          const NDBINDEX *index,
                          const NDBTAB *table,
+                         int &err_out,
                          bool allow_add,
                          bool force_update)
 {
   pthread_mutex_lock(&share->mutex);
   pthread_mutex_lock(&ndb_index_stat_list_mutex);
+  pthread_mutex_lock(&ndb_index_stat_stat_mutex);
   time_t now= ndb_index_stat_time();
+  err_out= 0;
 
   struct Ndb_index_stat *st= 0;
   struct Ndb_index_stat *st_last= 0;
-  if (ndb_index_stat_allow())
+  do
   {
-    st= ndb_index_stat_find_share(share, index, st_last);
-    if (st == 0 && allow_add)
+    if (unlikely(!ndb_index_stat_allow()))
     {
-      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);
+      err_out= Ndb_index_stat_error_NOT_ALLOW;
+      break;
     }
-    if (st != 0)
+    st= ndb_index_stat_find_share(share, index, st_last);
+    if (st == 0)
     {
-      if (force_update != 0)
+      if (!allow_add)
+      {
+        err_out= Ndb_index_stat_error_NOT_FOUND;
+        break;
+      }
+      st= ndb_index_stat_alloc(index, table, err_out);
+      if (st == 0)
       {
-        st->force_update++;
-        Ndb_index_stat_glob &glob= ndb_index_stat_glob;
-        glob.lock();
-        glob.force_update++;
-        glob.unlock();
+        assert(err_out != 0);
+        break;
       }
-      st->access_time= now;
+      ndb_index_stat_add_share(share, st, st_last);
+      ndb_index_stat_list_add(st, Ndb_index_stat::LT_New);
     }
+    if (force_update)
+      ndb_index_stat_force_update(st, true);
+    st->access_time= now;
   }
+  while (0);
 
+  pthread_mutex_unlock(&ndb_index_stat_stat_mutex);
   pthread_mutex_unlock(&ndb_index_stat_list_mutex);
   pthread_mutex_unlock(&share->mutex);
   return st;
@@ -893,6 +890,39 @@ ndb_index_stat_free(NDB_SHARE *share)
   pthread_mutex_unlock(&ndb_index_stat_list_mutex);
 }
 
+/* Find entry across shares */
+/* wl4124_todo mutex overkill, hash table, can we find table share */
+Ndb_index_stat*
+ndb_index_stat_find_entry(int index_id, int index_version, int table_id)
+{
+  DBUG_ENTER("ndb_index_stat_find_entry");
+  pthread_mutex_lock(&ndbcluster_mutex);
+  pthread_mutex_lock(&ndb_index_stat_list_mutex);
+  DBUG_PRINT("index_stat", ("find index:%d version:%d table:%d",
+                            index_id, index_version, table_id));
+
+  int lt;
+  for (lt=1; lt < Ndb_index_stat::LT_Count; lt++)
+  {
+    Ndb_index_stat *st=ndb_index_stat_list[lt].head;
+    while (st != 0)
+    {
+      if (st->index_id == index_id &&
+          st->index_version == index_version)
+      {
+        pthread_mutex_unlock(&ndb_index_stat_list_mutex);
+        pthread_mutex_unlock(&ndbcluster_mutex);
+        DBUG_RETURN(st);
+      }
+      st= st->list_next;
+    }
+  }
+
+  pthread_mutex_unlock(&ndb_index_stat_list_mutex);
+  pthread_mutex_unlock(&ndbcluster_mutex);
+  DBUG_RETURN(0);
+}
+
 /* Statistics thread sub-routines */
 
 void
@@ -909,12 +939,11 @@ ndb_index_stat_cache_move(Ndb_index_stat
   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();
+  glob.set_status_variables();
 }
 
 void
@@ -928,20 +957,21 @@ ndb_index_stat_cache_clean(Ndb_index_sta
   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();
+  glob.set_status_variables();
 }
 
 /* Misc in/out parameters for process steps */
 struct Ndb_index_stat_proc {
+  NdbIndexStat* is_util; // For metadata and polling
   Ndb *ndb;
   time_t now;
   int lt;
   bool busy;
   bool end;
   Ndb_index_stat_proc() :
+    is_util(0),
     ndb(0),
     now(0),
     lt(0),
@@ -1021,8 +1051,25 @@ ndb_index_stat_proc_read(Ndb_index_stat_
   NdbIndexStat::Head head;
   if (st->is->read_stat(pr.ndb) == -1)
   {
+    pthread_mutex_lock(&ndb_index_stat_stat_mutex);
     ndb_index_stat_error(st, "read_stat", __LINE__);
-    pr.lt= Ndb_index_stat::LT_Error;
+    const uint force_update= st->force_update;
+    ndb_index_stat_force_update(st, false);
+
+    /* no stats is not unexpected error, unless analyze was done */
+    if (st->is->getNdbError().code == NdbIndexStat::NoIndexStats &&
+        force_update == 0)
+    {
+      ndb_index_stat_no_stats(st, true);
+      pr.lt= Ndb_index_stat::LT_Idle;
+    }
+    else
+    {
+      pr.lt= Ndb_index_stat::LT_Error;
+    }
+
+    pthread_cond_broadcast(&ndb_index_stat_stat_cond);
+    pthread_mutex_unlock(&ndb_index_stat_stat_mutex);
     return;
   }
 
@@ -1033,15 +1080,8 @@ ndb_index_stat_proc_read(Ndb_index_stat_
   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_force_update(st, false);
+  ndb_index_stat_no_stats(st, false);
 
   ndb_index_stat_cache_move(st);
   st->cache_clean= false;
@@ -1073,6 +1113,7 @@ ndb_index_stat_proc_read(Ndb_index_stat_
     pr.busy= true;
 }
 
+// wl4124_todo detect force_update faster
 void
 ndb_index_stat_proc_idle(Ndb_index_stat_proc &pr, Ndb_index_stat *st)
 {
@@ -1124,11 +1165,9 @@ ndb_index_stat_proc_idle(Ndb_index_stat_
     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++;
-    }
+    // rotates list if entry remains LT_Idle
+    ndb_index_stat_list_move(st, pr.lt);
+    cnt++;
   }
   if (cnt == batch)
     pr.busy= true;
@@ -1143,7 +1182,16 @@ ndb_index_stat_proc_check(Ndb_index_stat
   if (st->is->read_head(pr.ndb) == -1)
   {
     ndb_index_stat_error(st, "read_head", __LINE__);
-    pr.lt= Ndb_index_stat::LT_Error;
+    /* no stats is not unexpected error */
+    if (st->is->getNdbError().code == NdbIndexStat::NoIndexStats)
+    {
+      ndb_index_stat_no_stats(st, true);
+      pr.lt= Ndb_index_stat::LT_Idle;
+    }
+    else
+    {
+      pr.lt= Ndb_index_stat::LT_Error;
+    }
     return;
   }
   st->is->get_head(head);
@@ -1213,9 +1261,7 @@ 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)
@@ -1330,11 +1376,15 @@ ndb_index_stat_proc_error(Ndb_index_stat
   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 (error_wait <= 0 ||
+      /* Analyze issued after previous error */
+      st->force_update)
+  {
+    DBUG_PRINT("index_stat", ("st %s error wait:%ds error count:%u"
+                              " force update:%u",
+                              st->id, (int)error_wait, st->error_count,
+                              st->force_update));
+    ndb_index_stat_clear_error(st);
     if (st->force_update)
       pr.lt= Ndb_index_stat::LT_Update;
     else
@@ -1361,16 +1411,82 @@ ndb_index_stat_proc_error(Ndb_index_stat
     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++;
-    }
+    ndb_index_stat_list_move(st, pr.lt);
+    cnt++;
   }
   if (cnt == batch)
     pr.busy= true;
 }
 
+void
+ndb_index_stat_proc_event(Ndb_index_stat_proc &pr, Ndb_index_stat *st)
+{
+  /*
+    Put on Check list if idle.
+    We get event also for our own analyze but this should not matter.
+   */
+  pr.lt= st->lt;
+  if (st->lt == Ndb_index_stat::LT_Idle ||
+      st->lt == Ndb_index_stat::LT_Error)
+    pr.lt= Ndb_index_stat::LT_Check;
+}
+
+void
+ndb_index_stat_proc_event(Ndb_index_stat_proc &pr)
+{
+  NdbIndexStat *is= pr.is_util;
+  Ndb *ndb= pr.ndb;
+  int ret;
+  ret= is->poll_listener(ndb, 0);
+  DBUG_PRINT("index_stat", ("poll_listener ret: %d", ret));
+  if (ret == -1)
+  {
+    // wl4124_todo report error
+    DBUG_ASSERT(false);
+    return;
+  }
+  if (ret == 0)
+    return;
+
+  while (1)
+  {
+    ret= is->next_listener(ndb);
+    DBUG_PRINT("index_stat", ("next_listener ret: %d", ret));
+    if (ret == -1)
+    {
+      // wl4124_todo report error
+      DBUG_ASSERT(false);
+      return;
+    }
+    if (ret == 0)
+      break;
+
+    NdbIndexStat::Head head;
+    is->get_head(head);
+    DBUG_PRINT("index_stat", ("next_listener eventType: %d indexId: %u",
+                              head.m_eventType, head.m_indexId));
+
+    Ndb_index_stat *st= ndb_index_stat_find_entry(head.m_indexId,
+                                                  head.m_indexVersion,
+                                                  head.m_tableId);
+    /*
+      Another process can update stats for an index which is not found
+      in this mysqld.  Ignore it.
+     */
+    if (st != 0)
+    {
+      DBUG_PRINT("index_stat", ("st %s proc %s", st->id, "Event"));
+      ndb_index_stat_proc_event(pr, st);
+      if (pr.lt != st->lt)
+        ndb_index_stat_list_move(st, pr.lt);
+    }
+    else
+    {
+      DBUG_PRINT("index_stat", ("entry not found in this mysqld"));
+    }
+  }
+}
+
 #ifndef DBUG_OFF
 void
 ndb_index_stat_report(const Ndb_index_stat_glob& old_glob)
@@ -1427,15 +1543,11 @@ ndb_index_stat_report(const Ndb_index_st
 
   /* 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));
-    }
+    uint no_stats= new_glob.no_stats;
+    DBUG_PRINT("index_stat", ("wait update:%u force update:%u no stats:%u",
+                              wait_update, force_update, no_stats));
   }
 }
 #endif
@@ -1458,6 +1570,7 @@ ndb_index_stat_proc(Ndb_index_stat_proc
   ndb_index_stat_proc_evict(pr);
   ndb_index_stat_proc_delete(pr);
   ndb_index_stat_proc_error(pr);
+  ndb_index_stat_proc_event(pr);
 
 #ifndef DBUG_OFF
   ndb_index_stat_report(old_glob);
@@ -1503,11 +1616,14 @@ ndb_index_stat_end()
 
 /* Index stats thread */
 
-static int
-ndb_index_stat_check_or_create_systables(NdbIndexStat* is, Ndb* ndb)
+int
+ndb_index_stat_check_or_create_systables(Ndb_index_stat_proc &pr)
 {
   DBUG_ENTER("ndb_index_stat_check_or_create_systables");
 
+  NdbIndexStat *is= pr.is_util;
+  Ndb *ndb= pr.ndb;
+
   if (is->check_systables(ndb) == 0)
   {
     DBUG_PRINT("index_stat", ("using existing index stats tables"));
@@ -1520,7 +1636,8 @@ ndb_index_stat_check_or_create_systables
     DBUG_RETURN(0);
   }
 
-  if (is->getNdbError().code == 721)
+  if (is->getNdbError().code == 721 ||
+      is->getNdbError().code == 4244)
   {
     // race between mysqlds, maybe
     DBUG_PRINT("index_stat", ("create index stats tables failed: error %d line %d",
@@ -1533,6 +1650,82 @@ ndb_index_stat_check_or_create_systables
   DBUG_RETURN(-1);
 }
 
+int
+ndb_index_stat_check_or_create_sysevents(Ndb_index_stat_proc &pr)
+{
+  DBUG_ENTER("ndb_index_stat_check_or_create_sysevents");
+
+  NdbIndexStat *is= pr.is_util;
+  Ndb *ndb= pr.ndb;
+
+  if (is->check_sysevents(ndb) == 0)
+  {
+    DBUG_PRINT("index_stat", ("using existing index stats events"));
+    DBUG_RETURN(0);
+  }
+
+  if (is->create_sysevents(ndb) == 0)
+  {
+    DBUG_PRINT("index_stat", ("created index stats events"));
+    DBUG_RETURN(0);
+  }
+
+  if (is->getNdbError().code == 746)
+  {
+    // race between mysqlds, maybe
+    DBUG_PRINT("index_stat", ("create index stats events failed: error %d line %d",
+                              is->getNdbError().code, is->getNdbError().line));
+    DBUG_RETURN(-1);
+  }
+
+  sql_print_warning("create index stats events failed: error %d line %d",
+                    is->getNdbError().code, is->getNdbError().line);
+  DBUG_RETURN(-1);
+}
+
+int
+ndb_index_stat_start_listener(Ndb_index_stat_proc &pr)
+{
+  DBUG_ENTER("ndb_index_stat_start_listener");
+
+  NdbIndexStat *is= pr.is_util;
+  Ndb *ndb= pr.ndb;
+
+  if (is->create_listener(ndb) == -1)
+  {
+    sql_print_warning("create index stats listener failed: error %d line %d",
+                      is->getNdbError().code, is->getNdbError().line);
+    DBUG_RETURN(-1);
+  }
+
+  if (is->execute_listener(ndb) == -1)
+  {
+    sql_print_warning("execute index stats listener failed: error %d line %d",
+                      is->getNdbError().code, is->getNdbError().line);
+    DBUG_RETURN(-1);
+  }
+
+  DBUG_RETURN(0);
+}
+
+int
+ndb_index_stat_stop_listener(Ndb_index_stat_proc &pr)
+{
+  DBUG_ENTER("ndb_index_stat_stop_listener");
+
+  NdbIndexStat *is= pr.is_util;
+  Ndb *ndb= pr.ndb;
+
+  if (is->drop_listener(ndb) == -1)
+  {
+    sql_print_warning("drop index stats listener failed: error %d line %d",
+                      is->getNdbError().code, is->getNdbError().line);
+    DBUG_RETURN(-1);
+  }
+
+  DBUG_RETURN(0);
+}
+
 pthread_handler_t
 ndb_index_stat_thread_func(void *arg __attribute__((unused)))
 {
@@ -1543,6 +1736,11 @@ ndb_index_stat_thread_func(void *arg __a
   my_thread_init();
   DBUG_ENTER("ndb_index_stat_thread_func");
 
+  Ndb_index_stat_proc pr;
+
+  bool have_listener;
+  have_listener= false;
+
   // wl4124_todo remove useless stuff copied from utility thread
  
   pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
@@ -1613,6 +1811,14 @@ ndb_index_stat_thread_func(void *arg __a
   }
   pthread_mutex_unlock(&LOCK_ndb_index_stat_thread);
 
+  /* Get instance used for sys objects check and create */
+  if (!(pr.is_util= new NdbIndexStat))
+  {
+    sql_print_error("Could not allocate NdbIndexStat is_util object");
+    pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
+    goto ndb_index_stat_thread_end;
+  }
+
   /* Get thd_ndb for this thread */
   if (!(thd_ndb= ha_ndbcluster::seize_thd_ndb()))
   {
@@ -1629,6 +1835,7 @@ ndb_index_stat_thread_func(void *arg __a
     pthread_mutex_lock(&LOCK_ndb_index_stat_thread);
     goto ndb_index_stat_thread_end;
   }
+  pr.ndb= thd_ndb->ndb;
 
   ndb_index_stat_allow(1);
   bool enable_ok;
@@ -1653,9 +1860,6 @@ ndb_index_stat_thread_func(void *arg __a
     /* const bool enable_ok_new= THDVAR(NULL, index_stat_enable); */
     const bool enable_ok_new= ndb_index_stat_get_enable(NULL);
 
-    Ndb_index_stat_proc pr;
-    pr.ndb= thd_ndb->ndb;
-
     do
     {
       if (enable_ok != enable_ok_new)
@@ -1665,13 +1869,24 @@ ndb_index_stat_thread_func(void *arg __a
 
         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)
+          // at enable check or create stats tables and events
+          if (ndb_index_stat_check_or_create_systables(pr) == -1 ||
+              ndb_index_stat_check_or_create_sysevents(pr) == -1 ||
+              ndb_index_stat_start_listener(pr) == -1)
           {
             // try again in next loop
             break;
           }
+          have_listener= true;
+        }
+        else
+        {
+          // not a normal use-case
+          if (have_listener)
+          {
+            if (ndb_index_stat_stop_listener(pr) == 0)
+              have_listener= false;
+          }
         }
         enable_ok= enable_ok_new;
       }
@@ -1702,6 +1917,16 @@ ndb_index_stat_thread_end:
   net_end(&thd->net);
 
 ndb_index_stat_thread_fail:
+  if (have_listener)
+  {
+    if (ndb_index_stat_stop_listener(pr) == 0)
+      have_listener= false;
+  }
+  if (pr.is_util)
+  {
+    delete pr.is_util;
+    pr.is_util= 0;
+  }
   if (thd_ndb)
   {
     ha_ndbcluster::release_thd_ndb(thd_ndb);
@@ -1738,22 +1963,37 @@ ndb_index_stat_round(double x)
 }
 
 int
-ha_ndbcluster::ndb_index_stat_wait(Ndb_index_stat *st,
-                                   uint sample_version)
+ndb_index_stat_wait(Ndb_index_stat *st,
+                    uint sample_version,
+                    bool from_analyze)
 {
-  DBUG_ENTER("ha_ndbcluster::ndb_index_stat_wait");
+  DBUG_ENTER("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) {
+  while (true)
+  {
     int ret= 0;
-    if (st->error.code != 0 &&
-        (st->error.code != NdbIndexStat::NoIndexStats ||
-         st->force_update == 0))
+    if (count == 0)
+    {
+      if (st->lt == Ndb_index_stat::LT_Error && !from_analyze)
+      {
+        err= Ndb_index_stat_error_HAS_ERROR;
+        break;
+      }
+      ndb_index_stat_clear_error(st);
+    }
+    if (st->no_stats && !from_analyze)
     {
+      /* Have detected no stats now or before */
+      err= NdbIndexStat::NoIndexStats;
+      break;
+    }
+    if (st->error.code != 0)
+    {
+      /* A new error has occured */
       err= st->error.code;
       break;
     }
@@ -1775,7 +2015,8 @@ ha_ndbcluster::ndb_index_stat_wait(Ndb_i
     }
   }
   pthread_mutex_unlock(&ndb_index_stat_stat_mutex);
-  if (err != 0) {
+  if (err != 0)
+  {
     DBUG_PRINT("index_stat", ("st %s wait error: %d",
                                st->id, err));
     DBUG_RETURN(err);
@@ -1806,14 +2047,12 @@ ha_ndbcluster::ndb_index_stat_query(uint
   ib.range_no= 0;
 
   Ndb_index_stat *st=
-    ndb_index_stat_get_share(m_share, index, m_table, true, false);
+    ndb_index_stat_get_share(m_share, index, m_table, err, true, false);
   if (st == 0)
-  {
-    DBUG_PRINT("index_stat", ("failed to add index stat share"));
-    DBUG_RETURN(HA_ERR_OUT_OF_MEM);
-  }
+    DBUG_RETURN(err);
 
-  err= ndb_index_stat_wait(st, 0);
+  /* Pass old version 0 so existing stats terminates wait at once */
+  err= ndb_index_stat_wait(st, 0, false);
   if (err != 0)
     DBUG_RETURN(err);
 
@@ -1927,10 +2166,9 @@ ha_ndbcluster::ndb_index_stat_analyze(Nd
     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);
-
+      ndb_index_stat_get_share(m_share, index, m_table, err, true, true);
     if (st == 0)
-      DBUG_RETURN(HA_ERR_OUT_OF_MEM);
+      DBUG_RETURN(err);
 
     old[i].sample_version= st->sample_version;
     old[i].error_count= st->error_count;
@@ -1945,11 +2183,11 @@ ha_ndbcluster::ndb_index_stat_analyze(Nd
     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);
+      ndb_index_stat_get_share(m_share, index, m_table, err, false, false);
     if (st == 0)
-      DBUG_RETURN(HA_ERR_NO_SUCH_TABLE);
+      DBUG_RETURN(err);
 
-    err= ndb_index_stat_wait(st, old[i].sample_version);
+    err= ndb_index_stat_wait(st, old[i].sample_version, true);
     if (err != 0)
       DBUG_RETURN(err);
   }

=== modified file 'sql/ha_ndb_index_stat.h'
--- a/sql/ha_ndb_index_stat.h	2011-06-15 10:37:56 +0000
+++ b/sql/ha_ndb_index_stat.h	2011-08-11 17:11:30 +0000
@@ -19,16 +19,21 @@
 
 extern struct st_ndb_status g_ndb_status;
 
+extern pthread_mutex_t ndbcluster_mutex;
+
 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;
+
+/* protect entry lists where needed */
 extern pthread_mutex_t ndb_index_stat_list_mutex;
+
+/* protect and signal changes in stats entries */
 extern pthread_mutex_t ndb_index_stat_stat_mutex;
 extern pthread_cond_t ndb_index_stat_stat_cond;
 
+/* these have to live in ha_ndbcluster.cc */
 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;
 
@@ -36,3 +41,14 @@ void
 compute_index_bounds(NdbIndexScanOperation::IndexBound & bound,
                      const KEY *key_info,
                      const key_range *start_key, const key_range *end_key);
+
+/* error codes local to ha_ndb */
+
+/* stats thread is not open for requests (should not happen) */
+#define Ndb_index_stat_error_NOT_ALLOW          9001
+
+/* stats entry for existing index not found (should not happen) */
+#define Ndb_index_stat_error_NOT_FOUND          9002
+
+/* request on stats entry with recent error was ignored */
+#define Ndb_index_stat_error_HAS_ERROR          9003

=== modified file 'sql/ha_ndbcluster.cc'
--- a/sql/ha_ndbcluster.cc	2011-07-08 12:28:37 +0000
+++ b/sql/ha_ndbcluster.cc	2011-07-23 14:38:08 +0000
@@ -40,6 +40,7 @@
 #include "ha_ndbcluster_binlog.h"
 #include "ha_ndbcluster_tables.h"
 #include "ha_ndbcluster_connection.h"
+#include "ha_ndb_index_stat.h"
 
 #include <mysql/plugin.h>
 #include <ndb_version.h>
@@ -427,7 +428,6 @@ 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;
@@ -1353,6 +1353,7 @@ void ha_ndbcluster::set_rec_per_key()
   */
   for (uint i=0 ; i < table_share->keys ; i++)
   {
+    KEY* key_info= table->key_info + i;
     switch (get_index_type(i))
     {
     case UNIQUE_ORDERED_INDEX:
@@ -1362,7 +1363,6 @@ void ha_ndbcluster::set_rec_per_key()
     {
       // Index is unique when all 'key_parts' are specified,
       // else distribution is unknown and not specified here.
-      KEY* key_info= table->key_info + i;
       key_info->rec_per_key[key_info->key_parts-1]= 1;
       break;
     }
@@ -1376,8 +1376,18 @@ void ha_ndbcluster::set_rec_per_key()
       if (index_stat_enable)
       {
         int err= ndb_index_stat_set_rpk(i);
-        if (err == 0)
-          break;
+        if (err != 0 &&
+            /* no stats is not unexpected error */
+            err != NdbIndexStat::NoIndexStats &&
+            /* warning was printed at first error */
+            err != Ndb_index_stat_error_HAS_ERROR)
+        {
+          push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+                              ER_CANT_GET_STAT, /* pun? */
+                              "index stats (RPK) for key %s:"
+                              " unexpected error %d",
+                              key_info->name, err);
+        }
       }
       // no fallback method...
       break;
@@ -11427,7 +11437,6 @@ static int ndbcluster_init(void *p)
   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);
@@ -11538,7 +11547,6 @@ static int ndbcluster_init(void *p)
     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);
@@ -11559,7 +11567,6 @@ static int ndbcluster_init(void *p)
     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);
@@ -11821,6 +11828,18 @@ ha_ndbcluster::records_in_range(uint inx
           rows = 2;
         DBUG_RETURN(rows);
       }
+      if (err != 0 &&
+          /* no stats is not unexpected error */
+          err != NdbIndexStat::NoIndexStats &&
+          /* warning was printed at first error */
+          err != Ndb_index_stat_error_HAS_ERROR)
+      {
+        push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+                            ER_CANT_GET_STAT, /* pun? */
+                            "index stats (RIR) for key %s:"
+                            " unexpected error %d",
+                            key_info->name, err);
+      }
       /*fall through*/
     }
 

=== modified file 'sql/ha_ndbcluster.h'
--- a/sql/ha_ndbcluster.h	2011-07-07 14:48:06 +0000
+++ b/sql/ha_ndbcluster.h	2011-07-23 14:35:37 +0000
@@ -858,17 +858,15 @@ private:
   void no_uncommitted_rows_reset(THD *);
 
   /* Ordered index statistics v4 */
+  int ndb_index_stat_query(uint inx,
+                           const key_range *min_key,
+                           const key_range *max_key,
+                           NdbIndexStat::Stat& stat);
   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(struct 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);

=== modified file 'storage/ndb/include/ndb_constants.h'
--- a/storage/ndb/include/ndb_constants.h	2011-07-04 13:37:56 +0000
+++ b/storage/ndb/include/ndb_constants.h	2011-08-11 17:11:30 +0000
@@ -120,6 +120,7 @@
 #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_HEAD_EVENT    "ndb_index_stat_head_event"
 
 #define NDB_INDEX_STAT_PREFIX        "ndb_index_stat"
 

=== modified file 'storage/ndb/include/ndbapi/NdbIndexStat.hpp'
--- a/storage/ndb/include/ndbapi/NdbIndexStat.hpp	2011-07-04 13:37:56 +0000
+++ b/storage/ndb/include/ndbapi/NdbIndexStat.hpp	2011-08-11 17:11:30 +0000
@@ -100,7 +100,10 @@ public:
     InvalidCache = 4718,
     InternalError = 4719,
     BadSysTables = 4720,  // sys tables partly missing or invalid
-    HaveSysTables = 4244  // create error if all sys tables exist
+    HaveSysTables = 4244, // create error if all sys tables exist
+    NoSysEvents = 4710,
+    BadSysEvents = BadSysTables,
+    HaveSysEvents = 746
   };
 
   /*
@@ -191,6 +194,7 @@ public:
    */
   struct Head {
     Int32 m_found;        // -1 no read done, 0 = no record, 1 = exists
+    Int32 m_eventType;    // if polling, NdbDictionary::Event::TE_INSERT etc
     Uint32 m_indexId;
     Uint32 m_indexVersion;
     Uint32 m_tableId;
@@ -327,15 +331,51 @@ public:
   static void get_rule(const Stat& stat, char* buffer);
 
   /*
-   * Memory allocator for the stats caches.  By default each instance
-   * uses its own malloc-based implementation.
+   * Events (there is 1) for polling.  These are dictionary objects.
+   * Correct sys tables must exist.  Drop ignores non-existing events.
+   */
+  int create_sysevents(Ndb* ndb);
+  int drop_sysevents(Ndb* ndb);
+  int check_sysevents(Ndb* ndb);
+
+  /*
+   * Create listener for stats updates.  Only 1 is allowed.
+   */
+  int create_listener(Ndb* ndb);
+
+  /*
+   * Start listening for events (call NdbEventOperation::execute).
+   */
+  int execute_listener(Ndb* ndb);
+
+  /*
+   * Poll the listener (call Ndb::pollEvents).  Returns 1 if there are
+   * events available and 0 otherwise, or -1 on failure as usual.
+   */
+  int poll_listener(Ndb* ndb, int max_wait_ms);
+
+  /*
+   * Get next available event.  Returns 1 if a new event was returned
+   * and 0 otherwise, or -1 on failure as usual.  Use get_heed() to
+   * retrieve event type and data.
+   */
+  int next_listener(Ndb* ndb);
+
+  /*
+   * Drop the listener.
+   */
+  int drop_listener(Ndb* ndb);
+
+  /*
+   * Memory allocator for stats cache data (key and value byte arrays).
+   * Implementation default uses malloc/free.  The memory in use is the
+   * sum of CacheInfo::m_totalBytes from all cache types.
    */
   struct Mem {
     Mem();
     virtual ~Mem();
     virtual void* mem_alloc(UintPtr size) = 0;
     virtual void mem_free(void* ptr) = 0;
-    virtual UintPtr mem_used() const = 0;
   };
 
   /*

=== modified file 'storage/ndb/include/util/NdbPack.hpp'
--- a/storage/ndb/include/util/NdbPack.hpp	2011-05-04 09:44:18 +0000
+++ b/storage/ndb/include/util/NdbPack.hpp	2011-08-09 15:37:45 +0000
@@ -117,7 +117,7 @@ public:
   class Endian {
   public:
     enum Value {
-      Native = 0,
+      Native = 0, // replaced by actual value
       Little = 1,
       Big = 2
     };
@@ -300,6 +300,10 @@ public:
    * Instance of an array of data values.  The values are packed into
    * a byte buffer.  The buffer is also maintained as a single varbinary
    * value if non-zero var bytes (length bytes) is specified.
+   *
+   * Data instances can be received from another source (such as table
+   * in database) and may not be native-endian.  Such instances must
+   * first be completed with desc_all() and convert().
    */
   class Data : public DataC {
   public:
@@ -324,7 +328,7 @@ public:
     // convert endian
     int convert(Endian::Value to_endian);
     // create complete instance from buffer contents
-    int desc_all(Uint32 cnt);
+    int desc_all(Uint32 cnt, Endian::Value from_endian);
     // getters
     Uint32 get_max_len() const;
     Uint32 get_max_len4() const;
@@ -351,7 +355,7 @@ public:
     const Uint32 m_varBytes;
     Uint8* m_buf;
     Uint32 m_bufMaxLen;
-    Endian::Value m_endian; // Native until finalize()
+    Endian::Value m_endian;
     // iterator on items added
     Iter m_iter;
   };
@@ -685,7 +689,7 @@ NdbPack::Data::Data(const Spec& spec, bo
 {
   m_buf = 0;
   m_bufMaxLen = 0;
-  m_endian = Endian::Native;
+  m_endian = Endian::get_endian();
 }
 
 inline void
@@ -704,7 +708,7 @@ NdbPack::Data::reset()
   m_cnt = 0;  // in DataC
   const Uint32 bytes = m_varBytes + m_spec.get_nullmask_len(m_allNullable);
   memset(m_buf, 0, bytes);
-  m_endian = Endian::Native;
+  m_endian = Endian::get_endian();
   m_iter.reset();
 }
 
@@ -713,17 +717,14 @@ NdbPack::Data::finalize()
 {
   if (m_varBytes == 0 ||
       finalize_impl() == 0)
-  {
-    m_endian = Endian::get_endian();
     return 0;
-  }
   return -1;
 }
 
 inline int
 NdbPack::Data::convert(Endian::Value to_endian)
 {
-  if (unlikely(to_endian == Endian::Native))
+  if (to_endian == Endian::Native)
     to_endian = Endian::get_endian();
   if (m_endian == to_endian)
     return 0;

=== modified file 'storage/ndb/src/common/util/NdbPack.cpp'
--- a/storage/ndb/src/common/util/NdbPack.cpp	2011-05-09 15:35:25 +0000
+++ b/storage/ndb/src/common/util/NdbPack.cpp	2011-08-09 15:37:45 +0000
@@ -560,8 +560,11 @@ NdbPack::Data::finalize_impl()
 }
 
 int
-NdbPack::Data::desc_all(Uint32 cnt)
+NdbPack::Data::desc_all(Uint32 cnt, Endian::Value from_endian)
 {
+  if (from_endian == NdbPack::Endian::Native)
+    from_endian = NdbPack::Endian::get_endian();
+  m_endian = from_endian;
   assert(m_cnt == 0); // reset() would destroy nullmask
   for (Uint32 i = 0; i < cnt; i++)
   {
@@ -757,7 +760,7 @@ NdbPack::Spec::print(char* buf, Uint32 b
 
 // print DataC
 
-bool g_ndb_pack_print_hex_always = false;
+bool g_ndb_pack_print_hex_always = true;
 
 NdbOut&
 operator<<(NdbOut& out, const NdbPack::DataC& a)
@@ -1836,7 +1839,7 @@ testdesc(const Tdata& tdata)
   Uint8 buf_new[Tspec::MaxBuf];
   data_new.set_buf(buf_new, sizeof(buf_new));
   memcpy(buf_new, buf_old, fullLen);
-  chk2(data_new.desc_all(cnt) == 0, data_new);
+  chk2(data_new.desc_all(cnt, NdbPack::Endian::Native) == 0, data_new);
   chk1(memcmp(buf_new, data.get_full_buf(), data.get_full_len()) == 0);
   chk1(data_new.get_data_len() == data.get_data_len());
   chk1(data_new.get_cnt() == data.get_cnt());
@@ -1903,28 +1906,17 @@ testconvert(const Tdata& tdata)
   require(data.get_cnt() == data_new.get_cnt());
   const Uint32 cnt = tdata.m_cnt;
   Uint32 num_eq;
-  switch (NdbPack::Endian::get_endian()) {
-  case NdbPack::Endian::Little:
-    chk2(data_new.convert(NdbPack::Endian::Little) == 0, data_new);
-    num_eq = ~(Uint32)0;
-    chk1(data.cmp(data_new, cnt, num_eq) == 0);
-    chk2(data_new.convert(NdbPack::Endian::Big) == 0, data_new);
-    chk2(data_new.convert(NdbPack::Endian::Little) == 0, data_new);
-    num_eq = ~(Uint32)0;
-    chk1(data.cmp(data_new, cnt, num_eq) == 0);
-    break;
-  case NdbPack::Endian::Big:
-    chk2(data_new.convert(NdbPack::Endian::Big) == 0, data_new);
-    num_eq = ~(Uint32)0;
-    chk1(data.cmp(data_new, cnt, num_eq) == 0);
-    chk2(data_new.convert(NdbPack::Endian::Little) == 0, data_new);
-    chk2(data_new.convert(NdbPack::Endian::Big) == 0, data_new);
-    num_eq = ~(Uint32)0;
-    chk1(data.cmp(data_new, cnt, num_eq) == 0);
-    break;
-  default:
-    require(false);
-    break;
+  int i;
+  for (i = 0; i < 10; i++) {
+    int k = getrandom(3); // assumes Endian::Value 0,1,2
+    NdbPack::Endian::Value v = (NdbPack::Endian::Value)k;
+    chk2(data_new.convert(v) == 0, data_new);
+    if (v == NdbPack::Endian::Native ||
+        v == NdbPack::Endian::get_endian()) {
+      num_eq = ~(Uint32)0;
+      chk1(data.cmp(data_new, cnt, num_eq) == 0);
+      require(num_eq == cnt);
+    }
   }
 }
 

=== modified file 'storage/ndb/src/ndbapi/NdbIndexStat.cpp'
--- a/storage/ndb/src/ndbapi/NdbIndexStat.cpp	2011-07-04 13:37:56 +0000
+++ b/storage/ndb/src/ndbapi/NdbIndexStat.cpp	2011-08-11 17:11:30 +0000
@@ -656,6 +656,82 @@ NdbIndexStat::get_rule(const Stat& stat_
   DBUG_VOID_RETURN;
 }
 
+// events and polling
+
+int
+NdbIndexStat::create_sysevents(Ndb* ndb)
+{
+  DBUG_ENTER("NdbIndexStat::create_sysevents");
+  if (m_impl.create_sysevents(ndb) == -1)
+    DBUG_RETURN(-1);
+  DBUG_RETURN(0);
+}
+
+int
+NdbIndexStat::drop_sysevents(Ndb* ndb)
+{
+  DBUG_ENTER("NdbIndexStat::drop_sysevents");
+  if (m_impl.drop_sysevents(ndb) == -1)
+    DBUG_RETURN(-1);
+  DBUG_RETURN(0);
+}
+
+int
+NdbIndexStat::check_sysevents(Ndb* ndb)
+{
+  DBUG_ENTER("NdbIndexStat::check_sysevents");
+  if (m_impl.check_sysevents(ndb) == -1)
+    DBUG_RETURN(-1);
+  DBUG_RETURN(0);
+}
+
+int
+NdbIndexStat::create_listener(Ndb* ndb)
+{
+  DBUG_ENTER("NdbIndexStat::create_listener");
+  if (m_impl.create_listener(ndb) == -1)
+    DBUG_RETURN(-1);
+  DBUG_RETURN(0);
+}
+
+int
+NdbIndexStat::execute_listener(Ndb* ndb)
+{
+  DBUG_ENTER("NdbIndexStat::execute_listener");
+  if (m_impl.execute_listener(ndb) == -1)
+    DBUG_RETURN(-1);
+  DBUG_RETURN(0);
+}
+
+int
+NdbIndexStat::poll_listener(Ndb* ndb, int max_wait_ms)
+{
+  DBUG_ENTER("NdbIndexStat::poll_listener");
+  int ret = m_impl.poll_listener(ndb, max_wait_ms);
+  if (ret == -1)
+    DBUG_RETURN(-1);
+  DBUG_RETURN(ret);
+}
+
+int
+NdbIndexStat::next_listener(Ndb* ndb)
+{
+  DBUG_ENTER("NdbIndexStat::next_listener");
+  int ret = m_impl.next_listener(ndb);
+  if (ret == -1)
+    DBUG_RETURN(-1);
+  DBUG_RETURN(ret);
+}
+
+int
+NdbIndexStat::drop_listener(Ndb* ndb)
+{
+  DBUG_ENTER("NdbIndexStat::drop_listener");
+  if (m_impl.drop_listener(ndb) == -1)
+    DBUG_RETURN(-1);
+  DBUG_RETURN(0);
+}
+
 // mem
 
 NdbIndexStat::Mem::Mem()

=== modified file 'storage/ndb/src/ndbapi/NdbIndexStatImpl.cpp'
--- a/storage/ndb/src/ndbapi/NdbIndexStatImpl.cpp	2011-07-04 13:37:56 +0000
+++ b/storage/ndb/src/ndbapi/NdbIndexStatImpl.cpp	2011-08-12 05:16:41 +0000
@@ -22,6 +22,7 @@
 #include <Bitmask.hpp>
 #include <NdbSqlUtil.hpp>
 #include <NdbRecord.hpp>
+#include <NdbEventOperation.hpp>
 #include "NdbIndexStatImpl.hpp"
 
 #undef min
@@ -33,8 +34,8 @@ static const char* const g_headtable_nam
 static const char* const g_sampletable_name = NDB_INDEX_STAT_SAMPLE_TABLE;
 static const char* const g_sampleindex1_name = NDB_INDEX_STAT_SAMPLE_INDEX1;
 
-const int ERR_NoSuchObject[] = { 709, 723, 4243, 0 };
-const int ERR_TupleNotFound[] = { 626, 0 };
+static const int ERR_NoSuchObject[] = { 709, 723, 4243, 0 };
+static const int ERR_TupleNotFound[] = { 626, 0 };
 
 NdbIndexStatImpl::NdbIndexStatImpl(NdbIndexStat& facade) :
   NdbIndexStat(*this),
@@ -45,7 +46,8 @@ NdbIndexStatImpl::NdbIndexStatImpl(NdbIn
   init();
   m_query_mutex = NdbMutex_Create();
   assert(m_query_mutex != 0);
-  m_mem_handler = &g_mem_default_handler;
+  m_eventOp = 0;
+  m_mem_handler = &c_mem_default_handler;
 }
 
 void
@@ -544,10 +546,8 @@ NdbIndexStatImpl::drop_systables(Ndb* nd
 }
 
 int
-NdbIndexStatImpl::check_systables(Ndb* ndb)
+NdbIndexStatImpl::check_systables(Sys& sys)
 {
-  Sys sys(this, ndb);
-
   if (get_systables(sys) == -1)
     return -1;
 
@@ -566,6 +566,17 @@ NdbIndexStatImpl::check_systables(Ndb* n
   return 0;
 }
 
+int
+NdbIndexStatImpl::check_systables(Ndb* ndb)
+{
+  Sys sys(this, ndb);
+  
+  if (check_systables(sys) == -1)
+    return -1;
+
+  return 0;
+}
+
 // operation context
 
 NdbIndexStatImpl::Con::Con(NdbIndexStatImpl* impl, Head& head, Ndb* ndb) :
@@ -725,7 +736,7 @@ NdbIndexStatImpl::set_index(const NdbDic
       }
       NdbPack::Type type (
         icol->getType(),
-        icol->getArrayType() + icol->getSize() * icol->getLength(),
+        icol->getSizeInBytes(),
         icol->getNullable(),
         icol->getCharset() != 0 ? icol->getCharset()->number : 0
       );
@@ -742,7 +753,7 @@ NdbIndexStatImpl::set_index(const NdbDic
     // rir + rpk
     if (m_valueSpec.add(type, m_valueAttrs) == -1)
     {
-      setError(InternalError, __LINE__);
+      setError(InternalError, __LINE__, m_valueSpec.get_error_code());
       return -1;
     }
   }
@@ -781,6 +792,7 @@ void
 NdbIndexStatImpl::init_head(Head& head)
 {
   head.m_found = -1;
+  head.m_eventType = -1;
   head.m_indexId = 0;
   head.m_indexVersion = 0;
   head.m_tableId = 0;
@@ -1153,15 +1165,32 @@ NdbIndexStatImpl::read_next(Con& con)
       setError(con, __LINE__);
     return ret;
   }
-  // create consistent NdbPack::Data instances
-  if (m_keyData.desc_all(m_keyAttrs) == -1)
+
+  /*
+   * Key and value are raw data and little-endian.  Create the complete
+   * NdbPack::Data instance and convert it to native-endian.
+   */
+  const NdbPack::Endian::Value from_endian = NdbPack::Endian::Little;
+  const NdbPack::Endian::Value to_endian = NdbPack::Endian::Native;
+
+  if (m_keyData.desc_all(m_keyAttrs, from_endian) == -1)
   {
-    setError(InternalError, __LINE__);
+    setError(InternalError, __LINE__, m_keyData.get_error_code());
     return -1;
   }
-  if (m_valueData.desc_all(m_valueAttrs) == -1)
+  if (m_keyData.convert(to_endian) == -1)
   {
-    setError(InternalError, __LINE__);
+    setError(InternalError, __LINE__, m_keyData.get_error_code());
+    return -1;
+  }
+  if (m_valueData.desc_all(m_valueAttrs, from_endian) == -1)
+  {
+    setError(InternalError, __LINE__, m_valueData.get_error_code());
+    return -1;
+  }
+  if (m_valueData.convert(to_endian) == -1)
+  {
+    setError(InternalError, __LINE__, m_valueData.get_error_code());
     return -1;
   }
   return 0;
@@ -1183,13 +1212,12 @@ NdbIndexStatImpl::read_commit(Con& con)
 int
 NdbIndexStatImpl::save_start(Con& con)
 {
-  Mem* mem = m_mem_handler;
   if (m_cacheBuild != 0)
   {
     free_cache(m_cacheBuild);
     m_cacheBuild = 0;
   }
-  con.m_cacheBuild = (Cache*)mem->mem_alloc(sizeof(Cache));
+  con.m_cacheBuild = new Cache;
   if (con.m_cacheBuild == 0)
   {
     setError(NoMemError, __LINE__);
@@ -1725,7 +1753,7 @@ NdbIndexStatImpl::free_cache(Cache* c)
   mem->mem_free(c->m_addrArray);
   mem->mem_free(c->m_keyArray);
   mem->mem_free(c->m_valueArray);
-  mem->mem_free(c);
+  delete c;
 }
 
 void
@@ -1838,7 +1866,9 @@ NdbIndexStatImpl::convert_range(Range& r
     Uint32 len_out;
     for (uint i = 0; i < key_count; i++)
     {
-      const NdbRecord::Attr& attr = key_record->columns[i];
+      const uint i2 = key_record->key_indexes[i];
+      require(i2 < key_record->noOfColumns);
+      const NdbRecord::Attr& attr = key_record->columns[i2];
       if (!attr.is_null(key))
       {
         const char* data = key + attr.offset;
@@ -1855,7 +1885,7 @@ NdbIndexStatImpl::convert_range(Range& r
         }
         if (bound.m_data.add(data, &len_out) == -1)
         {
-          setError(InternalError, __LINE__);
+          setError(InternalError, __LINE__, bound.m_data.get_error_code());
           return -1;
         }
       }
@@ -1863,7 +1893,7 @@ NdbIndexStatImpl::convert_range(Range& r
       {
         if (bound.m_data.add_null(&len_out) == -1)
         {
-          setError(InternalError, __LINE__);
+          setError(InternalError, __LINE__, bound.m_data.get_error_code());
           return -1;
         }
       }
@@ -2241,59 +2271,206 @@ NdbIndexStatImpl::query_keycmp(const Cac
   return res;
 }
 
-// mem alloc - default impl
+// events and polling
 
-NdbIndexStatImpl::MemDefault
-NdbIndexStatImpl::g_mem_default_handler;
+int
+NdbIndexStatImpl::create_sysevents(Ndb* ndb)
+{
+  Sys sys(this, ndb);
+  NdbDictionary::Dictionary* const dic = ndb->getDictionary();
 
-NdbIndexStatImpl::MemDefault::MemDefault()
+  if (check_systables(sys) == -1)
+    return -1;
+  const NdbDictionary::Table* tab = sys.m_headtable;
+  require(tab != 0);
+
+  const char* const evname = NDB_INDEX_STAT_HEAD_EVENT;
+  NdbDictionary::Event ev(evname, *tab);
+  ev.addTableEvent(NdbDictionary::Event::TE_INSERT);
+  ev.addTableEvent(NdbDictionary::Event::TE_DELETE);
+  ev.addTableEvent(NdbDictionary::Event::TE_UPDATE);
+  for (int i = 0; i < tab->getNoOfColumns(); i++)
+    ev.addEventColumn(i);
+  ev.setReport(NdbDictionary::Event::ER_UPDATED);
+
+  if (dic->createEvent(ev) == -1)
+  {
+    setError(dic->getNdbError().code, __LINE__);
+    return -1;
+  }
+  return 0;
+}
+
+int
+NdbIndexStatImpl::drop_sysevents(Ndb* ndb)
 {
-  m_used = 0;
+  Sys sys(this, ndb);
+  NdbDictionary::Dictionary* const dic = ndb->getDictionary();
+
+  if (check_systables(sys) == -1)
+    return -1;
+
+  const char* const evname = NDB_INDEX_STAT_HEAD_EVENT;
+  if (dic->dropEvent(evname) == -1)
+  {
+    int code = dic->getNdbError().code;
+    if (code != 4710)
+    {
+      setError(dic->getNdbError().code, __LINE__);
+      return -1;
+    }
+  }
+  return 0;
 }
 
-NdbIndexStatImpl::MemDefault::~MemDefault()
+int
+NdbIndexStatImpl::check_sysevents(Ndb* ndb)
 {
-  assert(m_used == 0);
+  Sys sys(this, ndb);
+  NdbDictionary::Dictionary* const dic = ndb->getDictionary();
+
+  if (check_systables(sys) == -1)
+    return -1;
+
+  const char* const evname = NDB_INDEX_STAT_HEAD_EVENT;
+  const NdbDictionary::Event* ev = dic->getEvent(evname);
+  if (ev == 0)
+  {
+    setError(dic->getNdbError().code, __LINE__);
+    return -1;
+  }
+  delete ev; // getEvent() creates new instance
+  return 0;
 }
 
-void*
-NdbIndexStatImpl::MemDefault::mem_alloc(UintPtr size)
+int
+NdbIndexStatImpl::create_listener(Ndb* ndb)
 {
-  if (size == 0 || size % 4 != 0)
+  if (m_eventOp != 0)
   {
-    size += 4 - size % 4;
+    setError(UsageError, __LINE__);
+    return -1;
   }
-  Item* item = (Item*)malloc(sizeof(Item) + size);
-  if (item != 0)
+  const char* const evname = NDB_INDEX_STAT_HEAD_EVENT;
+  m_eventOp = ndb->createEventOperation(evname);
+  if (m_eventOp == 0)
   {
-    item->m_magic = MemMagic;
-    item->m_size = size;
-    void* ptr = &item[1];
-    m_used += size;
-    return ptr;
+    setError(ndb->getNdbError().code, __LINE__);
+    return -1;
+  }
+
+  // all columns are non-nullable
+  Head& head = m_facadeHead;
+  if (m_eventOp->getValue("index_id", (char*)&head.m_indexId) == 0 ||
+      m_eventOp->getValue("index_version", (char*)&head.m_indexVersion) == 0 ||
+      m_eventOp->getValue("table_id", (char*)&head.m_tableId) == 0 ||
+      m_eventOp->getValue("frag_count", (char*)&head.m_fragCount) == 0 ||
+      m_eventOp->getValue("value_format", (char*)&head.m_valueFormat) == 0 ||
+      m_eventOp->getValue("sample_version", (char*)&head.m_sampleVersion) == 0 ||
+      m_eventOp->getValue("load_time", (char*)&head.m_loadTime) == 0 ||
+      m_eventOp->getValue("sample_count", (char*)&head.m_sampleCount) == 0 ||
+      m_eventOp->getValue("key_bytes", (char*)&head.m_keyBytes) == 0)
+  {
+    setError(m_eventOp->getNdbError().code, __LINE__);
+    return -1;
+  }
+  // wl4124_todo why this
+  static Head xxx;
+  if (m_eventOp->getPreValue("index_id", (char*)&xxx.m_indexId) == 0 ||
+      m_eventOp->getPreValue("index_version", (char*)&xxx.m_indexVersion) == 0 ||
+      m_eventOp->getPreValue("table_id", (char*)&xxx.m_tableId) == 0 ||
+      m_eventOp->getPreValue("frag_count", (char*)&xxx.m_fragCount) == 0 ||
+      m_eventOp->getPreValue("value_format", (char*)&xxx.m_valueFormat) == 0 ||
+      m_eventOp->getPreValue("sample_version", (char*)&xxx.m_sampleVersion) == 0 ||
+      m_eventOp->getPreValue("load_time", (char*)&xxx.m_loadTime) == 0 ||
+      m_eventOp->getPreValue("sample_count", (char*)&xxx.m_sampleCount) == 0 ||
+      m_eventOp->getPreValue("key_bytes", (char*)&xxx.m_keyBytes) == 0)
+  {
+    setError(m_eventOp->getNdbError().code, __LINE__);
+    return -1;
   }
   return 0;
 }
 
-void
-NdbIndexStatImpl::MemDefault::mem_free(void* ptr)
+int
+NdbIndexStatImpl::execute_listener(Ndb* ndb)
 {
-  if (ptr != 0)
+  if (m_eventOp == 0)
+  {
+    setError(UsageError, __LINE__);
+    return -1;
+  }
+  if (m_eventOp->execute() == -1)
+  {
+    setError(m_eventOp->getNdbError().code, __LINE__);
+    return -1;
+  }
+  return 0;
+}
+
+int
+NdbIndexStatImpl::poll_listener(Ndb* ndb, int max_wait_ms)
+{
+  int ret;
+  if ((ret = ndb->pollEvents(max_wait_ms)) < 0)
+  {
+    setError(ndb->getNdbError().code, __LINE__);
+    return -1;
+  }
+  return (ret == 0 ? 0 : 1);
+}
+
+int
+NdbIndexStatImpl::next_listener(Ndb* ndb)
+{
+  NdbEventOperation* op = ndb->nextEvent();
+  if (op == 0)
+    return 0;
+
+  Head& head = m_facadeHead;
+  head.m_eventType = (int)op->getEventType();
+  return 1;
+}
+
+int
+NdbIndexStatImpl::drop_listener(Ndb* ndb)
+{
+  if (m_eventOp == 0)
   {
-    Item* item = (Item*)ptr - 1;
-    assert(item->m_magic == MemMagic);
-    size_t size = item->m_size;
-    item->m_magic = 0;
-    free(item);
-    assert(m_used >= size);
-    m_used -= size;
+    setError(UsageError, __LINE__);
+    return -1;
+  }
+  if (ndb->dropEventOperation(m_eventOp) != 0)
+  {
+    setError(ndb->getNdbError().code, __LINE__);
+    return -1;
   }
+  m_eventOp = 0;
+  return 0;
 }
 
-UintPtr
-NdbIndexStatImpl::MemDefault::mem_used() const
+// mem alloc - default impl
+
+NdbIndexStatImpl::MemDefault::MemDefault()
+{
+}
+
+NdbIndexStatImpl::MemDefault::~MemDefault()
+{
+}
+
+void*
+NdbIndexStatImpl::MemDefault::mem_alloc(UintPtr size)
+{
+  void* ptr = malloc(size);
+  return ptr;
+}
+
+void
+NdbIndexStatImpl::MemDefault::mem_free(void* ptr)
 {
-  return m_used;
+  if (ptr != 0)
+    free(ptr);
 }
 
 // error

=== modified file 'storage/ndb/src/ndbapi/NdbIndexStatImpl.hpp'
--- a/storage/ndb/src/ndbapi/NdbIndexStatImpl.hpp	2011-07-04 13:37:56 +0000
+++ b/storage/ndb/src/ndbapi/NdbIndexStatImpl.hpp	2011-08-11 17:11:30 +0000
@@ -29,6 +29,7 @@ class NdbTransaction;
 class NdbIndexScanOperation;
 class NdbRecAttr;
 class NdbOperation;
+class NdbEventOperation;
 
 extern const uint g_ndb_index_stat_head_frm_len;
 extern const uint8 g_ndb_index_stat_head_frm_data[];
@@ -71,6 +72,7 @@ public:
   Cache* m_cacheClean;
   // mutex for query cache switch, memory barrier would do
   NdbMutex* m_query_mutex;
+  NdbEventOperation* m_eventOp;
   Mem* m_mem_handler;
   NdbIndexStat::Error m_error;
 
@@ -98,6 +100,7 @@ public:
   int get_systables(Sys& sys);
   int create_systables(Ndb* ndb);
   int drop_systables(Ndb* ndb);
+  int check_systables(Sys& sys);
   int check_systables(Ndb* ndb);
 
   // operation context
@@ -279,22 +282,25 @@ public:
   void query_search(const Cache&, const Bound&, StatBound&);
   int query_keycmp(const Cache&, const Bound&, uint pos, Uint32& numEq);
 
+  // events and polling
+  int create_sysevents(Ndb* ndb);
+  int drop_sysevents(Ndb* ndb);
+  int check_sysevents(Ndb* ndb);
+  //
+  int create_listener(Ndb* ndb);
+  int execute_listener(Ndb* ndb);
+  int poll_listener(Ndb* ndb, int max_wait_ms);
+  int next_listener(Ndb* ndb);
+  int drop_listener(Ndb* ndb);
+
   // default memory allocator
   struct MemDefault : public Mem {
     virtual void* mem_alloc(UintPtr bytes);
-    virtual void mem_free(void* p);
-    virtual UintPtr mem_used() const;
+    virtual void mem_free(void* ptr);
     MemDefault();
     virtual ~MemDefault();
-  private:
-    enum { MemMagic = 0xf1f2f3f4 };
-    struct Item {
-      Uint32 m_magic;
-      size_t m_size;
-    };
-    size_t m_used;
   };
-  static MemDefault g_mem_default_handler;
+  MemDefault c_mem_default_handler;
 
   // error
   const NdbIndexStat::Error& getNdbError() const;

=== modified file 'storage/ndb/test/ndbapi/testIndexStat.cpp'
--- a/storage/ndb/test/ndbapi/testIndexStat.cpp	2011-07-04 13:37:56 +0000
+++ b/storage/ndb/test/ndbapi/testIndexStat.cpp	2011-08-11 17:11:30 +0000
@@ -110,7 +110,8 @@ static const NdbRecord* g_ind_rec = 0;
 
 struct my_record
 {
-  Uint32 m_null_bm;
+  Uint8 m_null_bm;
+  Uint8 fill[3];
   Uint32 m_a;
   Uint32 m_b;
   char m_c[1+g_charlen];
@@ -133,6 +134,7 @@ static NdbIndexScanOperation* g_rangesca
 
 static NdbIndexStat* g_is = 0;
 static bool g_has_created_stat_tables = false;
+static bool g_has_created_stat_events = false;
 
 static uint
 urandom()
@@ -212,7 +214,7 @@ errdb()
       ll0(++any << " rangescan_op: error " << e);
   }
   if (g_is != 0) {
-    const NdbError& e = g_is->getNdbError();
+    const NdbIndexStat::Error& e = g_is->getNdbError();
     if (e.code != 0)
       ll0(++any << " stat: error " << e);
   }
@@ -376,6 +378,7 @@ struct Val {
   void copy(const Val& val2);
   void make(uint numattrs, const Lim& lim);
   int cmp(const Val& val2, uint numattrs = g_numattrs, uint* num_eq = 0) const;
+  void fromib(const NdbIndexScanOperation::IndexBound& ib, uint j);
 
 private:
   Val& operator=(const Val&);
@@ -556,6 +559,40 @@ Val::cmp(const Val& val2, uint numattrs,
   return k;
 }
 
+void
+Val::fromib(const NdbIndexScanOperation::IndexBound& ib, uint j)
+{
+  const char* key = (j == 0 ? ib.low_key : ib.high_key);
+  const uint numattrs = (j == 0 ? ib.low_key_count : ib.high_key_count);
+  const Uint8 nullbits = *(const Uint8*)key;
+  require(numattrs <= g_numattrs);
+  if (numattrs >= 1) {
+    if (nullbits & (1 << g_ndbrec_b_nb_offset))
+      b_null = 1;
+    else {
+      memcpy(&b, &key[g_ndbrec_b_offset], sizeof(b));
+      b_null = 0;
+    }
+  }
+  if (numattrs >= 2) {
+    if (nullbits & (1 << g_ndbrec_c_nb_offset))
+      c_null = 1;
+    else {
+      memcpy(c, &key[g_ndbrec_c_offset], sizeof(c));
+      c_null = 0;
+    }
+  }
+  if (numattrs >= 3) {
+    if (nullbits & (1 << g_ndbrec_d_nb_offset))
+      d_null = 1;
+    else {
+      memcpy(&d, &key[g_ndbrec_d_offset], sizeof(d));
+      d_null = 0;
+    }
+  }
+  m_numattrs = numattrs;
+}
+
 // index keys
 
 struct Key {
@@ -844,7 +881,9 @@ struct Bnd {
   Bnd& make(uint minattrs);
   Bnd& make(uint minattrs, const Val& theval);
   int cmp(const Key& key) const;
+  int cmp(const Bnd& bnd2);
   int type(uint colno) const; // for setBound
+  void fromib(const NdbIndexScanOperation::IndexBound& ib, uint j);
 
 private:
   Bnd& operator=(const Bnd&);
@@ -937,6 +976,52 @@ Bnd::cmp(const Key& key) const
 }
 
 int
+Bnd::cmp(const Bnd& bnd2)
+{
+  int place; // debug
+  int ret;
+  const Bnd& bnd1 = *this;
+  const Val& val1 = bnd1.m_val;
+  const Val& val2 = bnd2.m_val;
+  const uint numattrs1 = val1.m_numattrs;
+  const uint numattrs2 = val2.m_numattrs;
+  const uint n = (numattrs1 < numattrs2 ? numattrs1 : numattrs2);
+  do {
+    int k = val1.cmp(val2, n);
+    if (k != 0) {
+      place = 1;
+      ret = k;
+      break;
+    }
+    if (numattrs1 < numattrs2) {
+      place = 2;
+      ret = (+1) * bnd1.m_side;
+      break;
+    }
+    if (numattrs1 > numattrs2) {
+      place = 3;
+      ret = (-1) * bnd1.m_side;
+      break;
+    }
+    if (bnd1.m_side < bnd2.m_side) {
+      place = 4;
+      ret = -1;
+      break;
+    }
+    if (bnd1.m_side > bnd2.m_side) {
+      place = 5;
+      ret = +1;
+      break;
+    }
+    place = 6;
+    ret = 0;
+  } while (0);
+  ll3("bnd: " << *this << " cmp bnd: " << bnd2
+      << " ret: " << ret << " place: " << place);
+  return ret;
+}
+
+int
 Bnd::type(uint colno) const
 {
   int t;
@@ -960,6 +1045,21 @@ Bnd::type(uint colno) const
   return t;
 }
 
+void
+Bnd::fromib(const NdbIndexScanOperation::IndexBound& ib, uint j)
+{
+  Val& val = m_val;
+  val.fromib(ib, j);
+  const uint numattrs = (j == 0 ? ib.low_key_count : ib.high_key_count);
+  const bool inclusive = (j == 0 ? ib.low_inclusive : ib.high_inclusive);
+  if (numattrs == 0) {
+    m_side = 0;
+  } else {
+    m_side = (j == 0 ? (inclusive ? -1 : +1) : (inclusive ? +1 : -1));
+  }
+  m_lohi = j;
+}
+
 // stats values
 
 struct Stval {
@@ -1016,6 +1116,7 @@ struct Rng {
   void copy(const Rng& rng2);
   int cmp(const Key& key) const; // -1,0,+1 = key is before,in,after range
   uint rowcount() const;
+  void fromib(const NdbIndexScanOperation::IndexBound& ib);
 
 private:
   Rng& operator=(const Rng&);
@@ -1164,6 +1265,15 @@ Rng::rowcount() const
   return count;
 }
 
+void
+Rng::fromib(const NdbIndexScanOperation::IndexBound& ib)
+{
+  for (uint j = 0; j <= 1; j++) {
+    Bnd& bnd = m_bnd[j];
+    bnd.fromib(ib, j);
+  }
+}
+
 static Rng* g_rnglist = 0;
 
 static void
@@ -1409,6 +1519,40 @@ readstat()
   return 0;
 }
 
+// test polling after updatestat
+
+static int
+startlistener()
+{
+  ll1("startlistener");
+  chkdb(g_is->create_listener(g_ndb_sys) == 0);
+  chkdb(g_is->execute_listener(g_ndb_sys) == 0);
+  return 0;
+}
+
+static int
+runlistener()
+{
+  ll1("runlistener");
+  int ret;
+  chkdb((ret = g_is->poll_listener(g_ndb_sys, 10000)) != -1);
+  chkrc(ret == 1);
+  // one event is expected
+  chkdb((ret = g_is->next_listener(g_ndb_sys)) != -1);
+  chkrc(ret == 1);
+  chkdb((ret = g_is->next_listener(g_ndb_sys)) != -1);
+  chkrc(ret == 0);
+  return 0;
+}
+
+static int
+stoplistener()
+{
+  ll1("stoplistener");
+  chkdb(g_is->drop_listener(g_ndb_sys) != -1);
+  return 0;
+}
+
 // stats queries
 
 // exact stats from scan results
@@ -1475,7 +1619,8 @@ queryscan(Rng& rng)
  */
 static int
 initialiseIndexBound(const Rng& rng, 
-                     NdbIndexScanOperation::IndexBound& ib)
+                     NdbIndexScanOperation::IndexBound& ib,
+                     my_record* low_key, my_record* high_key)
 {
   ll3("initialiseIndexBound: " << rng);
   uint i;
@@ -1483,9 +1628,13 @@ initialiseIndexBound(const Rng& rng,
   Uint32 colsInBound[2]= {0, 0};
   bool boundInclusive[2]= {false, false};
 
+  memset(&ib, 0xf1, sizeof(ib));
+  memset(low_key, 0xf2, sizeof(*low_key));
+  memset(high_key, 0xf3, sizeof(*high_key));
+
   // Clear nullbit storage
-  *((char *)ib.low_key) = 
-    *((char *)ib.high_key) = 0;
+  low_key->m_null_bm = 0;
+  high_key->m_null_bm = 0;
 
   for (i = 0; i < g_numattrs; i++) {
     const Uint32 no = i; // index attribute number
@@ -1499,7 +1648,7 @@ initialiseIndexBound(const Rng& rng,
     }
     for (j = 0; j <= 1; j++) {
       /* Get ptr to key storage space for this bound */
-      my_record* keyBuf= (my_record *)( (j==0) ? ib.low_key : ib.high_key);
+      my_record* keyBuf= (j==0) ? low_key : high_key;
       int t = type[j];
       if (t == -1)
         continue;
@@ -1542,8 +1691,10 @@ initialiseIndexBound(const Rng& rng,
   }
 
   /* Now have everything we need to initialise the IndexBound */
+  ib.low_key = (char*)low_key;
   ib.low_key_count= colsInBound[0];
   ib.low_inclusive= boundInclusive[0];
+  ib.high_key = (char*)high_key;
   ib.high_key_count= colsInBound[1];
   ib.high_inclusive= boundInclusive[1];
   ib.range_no= 0;
@@ -1554,11 +1705,18 @@ initialiseIndexBound(const Rng& rng,
       " high_inc=" << ib.high_inclusive);
   ll3(" low bound b=" << *((Uint32*) &ib.low_key[g_ndbrec_b_offset]) <<
       " d=" << *((Uint16*) &ib.low_key[g_ndbrec_d_offset]) <<
-      " first byte=%xu" << ib.low_key[0]);
+      " first byte=" << ib.low_key[0]);
   ll3(" high bound b=" << *((Uint32*) &ib.high_key[g_ndbrec_b_offset]) <<
       " d=" << *((Uint16*) &ib.high_key[g_ndbrec_d_offset]) <<
-      " first byte=%xu" << ib.high_key[0]);  
+      " first byte=" << ib.high_key[0]);  
 
+  // verify by reverse
+  {
+    Rng rng;
+    rng.fromib(ib);
+    require(rng.m_bnd[0].cmp(bnd[0]) == 0);
+    require(rng.m_bnd[1].cmp(bnd[1]) == 0);
+  }
   return 0;
 }
 
@@ -1568,13 +1726,12 @@ querystat_v2(Rng& rng)
   ll3("querystat_v2");
   
   /* Create IndexBound and key storage space */
-  char keySpace[2][g_ndbrecord_bytes];
   NdbIndexScanOperation::IndexBound ib;
-  ib.low_key= keySpace[0];
-  ib.high_key= keySpace[1];
+  my_record low_key;
+  my_record high_key;
 
   chkdb((g_con = g_ndb->startTransaction()) != 0);
-  chkrc(initialiseIndexBound(rng, ib) == 0);
+  chkrc(initialiseIndexBound(rng, ib, &low_key, &high_key) == 0);
 
   Uint64 count = ~(Uint64)0;
   chkdb(g_is->records_in_range(g_ind, 
@@ -1610,10 +1767,9 @@ querystat(Rng& rng)
 
   // convert to IndexBound (like in mysqld)
   NdbIndexScanOperation::IndexBound ib;
-  char keySpace[2][g_ndbrecord_bytes];
-  ib.low_key = keySpace[0];
-  ib.high_key = keySpace[1];
-  chkrc(initialiseIndexBound(rng, ib) == 0);
+  my_record low_key;
+  my_record high_key;
+  chkrc(initialiseIndexBound(rng, ib, &low_key, &high_key) == 0);
   chkrc(g_is->convert_range(range, g_ind_rec, &ib) == 0);
 
   // index stat query
@@ -1960,6 +2116,7 @@ runtest()
   chkrc(createindex() == 0);
   chkrc(createNdbRecords() == 0);
   chkrc(definestat() == 0);
+  chkrc(startlistener() == 0);
 
   for (g_loop = 0; g_opts.loops == 0 || g_loop < g_opts.loops; g_loop++) {
     ll0("=== loop " << g_loop << " ===");
@@ -1973,6 +2130,7 @@ runtest()
     makeranges();
     chkrc(scanranges() == 0);
     chkrc(updatestat() == 0);
+    chkrc(runlistener() == 0);
     chkrc(readstat() == 0);
     chkrc(queryranges() == 0);
     loopstats();
@@ -1980,6 +2138,7 @@ runtest()
   }
   finalstats();
 
+  chkrc(stoplistener() == 0);
   if (!g_opts.keeptable)
     chkrc(droptable() == 0);
   freeranges();
@@ -2113,22 +2272,66 @@ docreate_stat_tables()
 {
   if (g_is->check_systables(g_ndb_sys) == 0)
     return 0;
+  ll1("check_systables: " << g_is->getNdbError());
 
-  if (g_is->create_systables(g_ndb_sys) == 0)
-  {
-    g_has_created_stat_tables = true;
-    return 0;
-  }
-  return -1;
+  ll0("create stat tables");
+  chkdb(g_is->create_systables(g_ndb_sys) == 0);
+  g_has_created_stat_tables = true;
+  return 0;
 }
 
 static
-void
+int
 dodrop_stat_tables()
 {
   if (g_has_created_stat_tables == false)
-    return;
-  g_is->drop_systables(g_ndb_sys);
+    return 0;
+
+  ll0("drop stat tables");
+  chkdb(g_is->drop_systables(g_ndb_sys) == 0);
+  return 0;
+}
+
+static int
+docreate_stat_events()
+{
+  if (g_is->check_sysevents(g_ndb_sys) == 0)
+    return 0;
+  ll1("check_sysevents: " << g_is->getNdbError());
+
+  ll0("create stat events");
+  chkdb(g_is->create_sysevents(g_ndb_sys) == 0);
+  g_has_created_stat_events = true;
+  return 0;
+}
+
+static int
+dodrop_stat_events()
+{
+  if (g_has_created_stat_events == false)
+    return 0;
+
+  ll0("drop stat events");
+  chkdb(g_is->drop_sysevents(g_ndb_sys) == 0);
+  return 0;
+}
+
+static int
+docreate_sys_objects()
+{
+  require(g_is != 0 && g_ndb_sys != 0);
+  chkrc(docreate_stat_tables() == 0);
+  chkrc(docreate_stat_events() == 0);
+  return 0;
+}
+
+static int
+dodrop_sys_objects()
+{
+  require(g_is != 0 && g_ndb_sys != 0);
+  chkrc(dodrop_stat_events() == 0);
+  chkrc(dodrop_stat_tables() == 0);
+  return 0;
 }
 
 int
@@ -2156,17 +2359,22 @@ main(int argc, char** argv)
     ll0("connect failed");
     return NDBT_ProgramExit(NDBT_FAILED);
   }
-  if (docreate_stat_tables() == -1){
-    ll0("failed to create stat tables");
-    return NDBT_ProgramExit(NDBT_FAILED);
+  if (docreate_sys_objects() == -1) {
+    ll0("failed to check or create stat tables and events");
+    goto failed;
   }
   if (runtest() == -1) {
     ll0("test failed");
-    dodrop_stat_tables();
-    dodisconnect();
-    return NDBT_ProgramExit(NDBT_FAILED);
+    goto failed;
+  }
+  if (dodrop_sys_objects() == -1) {
+    ll0("failed to drop created stat tables or events");
+    goto failed;
   }
-  dodrop_stat_tables();
   dodisconnect();
   return NDBT_ProgramExit(NDBT_OK);
+failed:
+  (void)dodrop_sys_objects();
+  dodisconnect();
+  return NDBT_ProgramExit(NDBT_FAILED);
 }

=== modified file 'storage/ndb/tools/ndb_index_stat.cpp'
--- a/storage/ndb/tools/ndb_index_stat.cpp	2011-07-04 13:37:56 +0000
+++ b/storage/ndb/tools/ndb_index_stat.cpp	2011-08-11 17:11:30 +0000
@@ -35,6 +35,8 @@ static my_bool _sys_create = false;
 static my_bool _sys_create_if_not_exist = false;
 static my_bool _sys_create_if_not_valid = false;
 static my_bool _sys_check = false;
+static my_bool _sys_skip_tables = false;
+static my_bool _sys_skip_events = false;
 static int _sys_any = 0;
 // other
 static my_bool _verbose = false;
@@ -88,11 +90,13 @@ doconnect()
     CHK2(g_ncc->connect(6, 5) == 0, getNdbError(g_ncc));
     CHK2(g_ncc->wait_until_ready(30, 10) == 0, getNdbError(g_ncc));
 
-    g_ndb = new Ndb(g_ncc, _dbname);
-    CHK2(g_ndb->init() == 0, g_ndb->getNdbError());
-    CHK2(g_ndb->waitUntilReady(30) == 0, g_ndb->getNdbError());
-
-    g_dic = g_ndb->getDictionary();
+    if (!_sys_any)
+    {
+      g_ndb = new Ndb(g_ncc, _dbname);
+      CHK2(g_ndb->init() == 0, g_ndb->getNdbError());
+      CHK2(g_ndb->waitUntilReady(30) == 0, g_ndb->getNdbError());
+      g_dic = g_ndb->getDictionary();
+    }
 
     g_ndb_sys = new Ndb(g_ncc, NDB_INDEX_STAT_DB);
     CHK2(g_ndb_sys->init() == 0, g_ndb_sys->getNdbError());
@@ -112,7 +116,7 @@ dodisconnect()
   delete g_ndb_sys;
   delete g_ndb;
   delete g_ncc;
-    g_info << "disconnected" << endl;
+  g_info << "disconnected" << endl;
 }
 
 static const char*
@@ -380,69 +384,146 @@ dosys()
   {
     if (_sys_drop)
     {
-      g_info << "dropping any sys tables" << endl;
-      CHK2(g_is->drop_systables(g_ndb_sys) == 0, g_is->getNdbError());
-      CHK2(g_is->check_systables(g_ndb_sys) == -1, "unexpected success");
-      CHK2(g_is->getNdbError().code == NdbIndexStat::NoSysTables,
-           "unexpected error: " << g_is->getNdbError());
+      if (!_sys_skip_events)
+      {
+        g_info << "dropping sys events" << endl;
+        CHK2(g_is->drop_sysevents(g_ndb_sys) == 0, g_is->getNdbError());
+        CHK2(g_is->check_sysevents(g_ndb_sys) == -1, "unexpected success");
+        CHK2(g_is->getNdbError().code == NdbIndexStat::NoSysEvents,
+             "unexpected error: " << g_is->getNdbError());
+      }
+
+      if (!_sys_skip_tables)
+      {
+        g_info << "dropping all sys tables" << endl;
+        CHK2(g_is->drop_systables(g_ndb_sys) == 0, g_is->getNdbError());
+        CHK2(g_is->check_systables(g_ndb_sys) == -1, "unexpected success");
+        CHK2(g_is->getNdbError().code == NdbIndexStat::NoSysTables,
+             "unexpected error: " << g_is->getNdbError());
+      }
       g_info << "drop done" << endl;
     }
 
     if (_sys_create)
     {
-      g_info << "creating all sys tables" << endl;
-      CHK2(g_is->create_systables(g_ndb_sys) == 0, g_is->getNdbError());
-      CHK2(g_is->check_systables(g_ndb_sys) == 0, g_is->getNdbError());
-      g_info << "create done" << endl;
-    }
-
-    if (_sys_create_if_not_exist)
-    {
-      if (g_is->check_systables(g_ndb_sys) == -1)
+      if (!_sys_skip_tables)
       {
-        CHK2(g_is->getNdbError().code == NdbIndexStat::NoSysTables,
-             g_is->getNdbError());
         g_info << "creating all sys tables" << endl;
         CHK2(g_is->create_systables(g_ndb_sys) == 0, g_is->getNdbError());
         CHK2(g_is->check_systables(g_ndb_sys) == 0, g_is->getNdbError());
+      }
+
+      if (!_sys_skip_events)
+      {
+        g_info << "creating sys events" << endl;
+        CHK2(g_is->create_sysevents(g_ndb_sys) == 0, g_is->getNdbError());
+        CHK2(g_is->check_sysevents(g_ndb_sys) == 0, g_is->getNdbError());
         g_info << "create done" << endl;
       }
-      else
+    }
+
+    if (_sys_create_if_not_exist)
+    {
+      if (!_sys_skip_tables)
       {
-        g_info << "using existing sys tables" << endl;
+        if (g_is->check_systables(g_ndb_sys) == -1)
+        {
+          CHK2(g_is->getNdbError().code == NdbIndexStat::NoSysTables,
+               g_is->getNdbError());
+          g_info << "creating all sys tables" << endl;
+          CHK2(g_is->create_systables(g_ndb_sys) == 0, g_is->getNdbError());
+          CHK2(g_is->check_systables(g_ndb_sys) == 0, g_is->getNdbError());
+          g_info << "create done" << endl;
+        }
+        else
+        {
+          g_info << "using existing sys tables" << endl;
+        }
+      }
+
+      if (!_sys_skip_events)
+      {
+        if (g_is->check_sysevents(g_ndb_sys) == -1)
+        {
+          CHK2(g_is->getNdbError().code == NdbIndexStat::NoSysEvents,
+               g_is->getNdbError());
+          g_info << "creating sys events" << endl;
+          CHK2(g_is->create_sysevents(g_ndb_sys) == 0, g_is->getNdbError());
+          g_info << "create done" << endl;
+        }
+        else
+        {
+          g_info << "using existing sys events" << endl;
+        }
       }
     }
 
     if (_sys_create_if_not_valid)
     {
-      if (g_is->check_systables(g_ndb_sys) == -1)
+      if (!_sys_skip_tables)
       {
-        if (g_is->getNdbError().code != NdbIndexStat::NoSysTables)
+        if (g_is->check_systables(g_ndb_sys) == -1)
         {
-          CHK2(g_is->getNdbError().code == NdbIndexStat::BadSysTables,
-               g_is->getNdbError());
-          g_info << "dropping invalid sys tables" << endl;
-          CHK2(g_is->drop_systables(g_ndb_sys) == 0, g_is->getNdbError());
-          CHK2(g_is->check_systables(g_ndb_sys) == -1, "unexpected success");
-          CHK2(g_is->getNdbError().code == NdbIndexStat::NoSysTables,
-               "unexpected error: " << g_is->getNdbError());
-          g_info << "drop done" << endl;
+          if (g_is->getNdbError().code != NdbIndexStat::NoSysTables)
+          {
+            CHK2(g_is->getNdbError().code == NdbIndexStat::BadSysTables,
+                 g_is->getNdbError());
+            g_info << "dropping invalid sys tables" << endl;
+            CHK2(g_is->drop_systables(g_ndb_sys) == 0, g_is->getNdbError());
+            CHK2(g_is->check_systables(g_ndb_sys) == -1, "unexpected success");
+            CHK2(g_is->getNdbError().code == NdbIndexStat::NoSysTables,
+                 "unexpected error: " << g_is->getNdbError());
+            g_info << "drop done" << endl;
+          }
+          g_info << "creating all sys tables" << endl;
+          CHK2(g_is->create_systables(g_ndb_sys) == 0, g_is->getNdbError());
+          CHK2(g_is->check_systables(g_ndb_sys) == 0, g_is->getNdbError());
+          g_info << "create done" << endl;
+        }
+        else
+        {
+          g_info << "using existing sys tables" << endl;
         }
-        g_info << "creating all sys tables" << endl;
-        CHK2(g_is->create_systables(g_ndb_sys) == 0, g_is->getNdbError());
-        CHK2(g_is->check_systables(g_ndb_sys) == 0, g_is->getNdbError());
-        g_info << "create done" << endl;
       }
-      else
+      if (!_sys_skip_events)
       {
-        g_info << "using existing sys tables" << endl;
+        if (g_is->check_sysevents(g_ndb_sys) == -1)
+        {
+          if (g_is->getNdbError().code != NdbIndexStat::NoSysEvents)
+          {
+            CHK2(g_is->getNdbError().code == NdbIndexStat::BadSysEvents,
+                 g_is->getNdbError());
+            g_info << "dropping invalid sys events" << endl;
+            CHK2(g_is->drop_sysevents(g_ndb_sys) == 0, g_is->getNdbError());
+            CHK2(g_is->check_sysevents(g_ndb_sys) == -1, "unexpected success");
+            CHK2(g_is->getNdbError().code == NdbIndexStat::NoSysEvents,
+                 "unexpected error: " << g_is->getNdbError());
+            g_info << "drop done" << endl;
+          }
+          g_info << "creating sys events" << endl;
+          CHK2(g_is->create_sysevents(g_ndb_sys) == 0, g_is->getNdbError());
+          CHK2(g_is->check_sysevents(g_ndb_sys) == 0, g_is->getNdbError());
+          g_info << "create done" << endl;
+        }
+        else
+        {
+          g_info << "using existing sys events" << endl;
+        }
       }
     }
 
     if (_sys_check)
     {
-      CHK2(g_is->check_systables(g_ndb_sys) == 0, g_is->getNdbError());
-      g_info << "sys tables ok" << endl;
+      if (!_sys_skip_tables)
+      {
+        CHK2(g_is->check_systables(g_ndb_sys) == 0, g_is->getNdbError());
+        g_info << "sys tables ok" << endl;
+      }
+      if (!_sys_skip_events)
+      {
+        CHK2(g_is->check_sysevents(g_ndb_sys) == 0, g_is->getNdbError());
+        g_info << "sys events ok" << endl;
+      }
     }
   }
   while (0);
@@ -512,25 +593,33 @@ my_long_options[] =
     GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
   // sys options
   { "sys-drop", ++oi,
-    "Drop any stats tables in NDB kernel (all stats is lost)",
+    "Drop any stats tables and events in NDB kernel (all stats is lost)",
     (uchar **)&_sys_drop, (uchar **)&_sys_drop, 0,
     GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
   { "sys-create", ++oi,
-    "Create stats tables in NDB kernel (must not exist)",
+    "Create stats tables and events in NDB kernel (must not exist)",
     (uchar **)&_sys_create, (uchar **)&_sys_create, 0,
     GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
   { "sys-create-if-not-exist", ++oi,
-    "Like --sys-create but do nothing if correct stats tables exist",
+    "Like --sys-create but do nothing if correct objects exist",
     (uchar **)&_sys_create_if_not_exist, (uchar **)&_sys_create_if_not_exist, 0,
     GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
   { "sys-create-if-not-valid", ++oi,
-    "Like --sys-create-if-not-exist but first drop any invalid tables",
+    "Like --sys-create-if-not-exist but first drop any invalid objects",
     (uchar **)&_sys_create_if_not_valid, (uchar **)&_sys_create_if_not_valid, 0,
     GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
   { "sys-check", ++oi,
-    "Check that correct stats tables exist in NDB kernel",
+    "Check that correct stats tables and events exist in NDB kernel",
     (uchar **)&_sys_check, (uchar **)&_sys_check, 0,
     GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
+  { "sys-skip-tables", ++oi,
+    "Do not apply sys options to tables",
+    (uchar **)&_sys_skip_tables, (uchar **)&_sys_skip_tables, 0,
+    GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
+  { "sys-skip-events", ++oi,
+    "Do not apply sys options to events",
+    (uchar **)&_sys_skip_events, (uchar **)&_sys_skip_events, 0,
+    GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0 },
   // other
   { "verbose", 'v',
     "Verbose messages",
@@ -579,7 +668,9 @@ checkopts(int argc, char** argv)
       (_sys_create_if_not_exist != 0) +
       (_sys_create_if_not_valid != 0) +
       (_sys_drop != 0) +
-      ( _sys_check != 0);
+      ( _sys_check != 0) +
+      (_sys_skip_tables != 0) +
+      (_sys_skip_events != 0);
     if (!_sys_any)
     {
       if (_dbname == 0)

No bundle (reason: useless for push emails).
Thread
bzr push into mysql-5.1-telco-7.0 branch (jonas.oreland:4460 to 4461) Jonas Oreland22 Aug