List:Internals« Previous MessageNext Message »
From:pekka Date:September 15 2005 12:33am
Subject:bk commit into 5.1 tree (pekka:1.1893)
View as plain text  
Below is the list of changes that have just been committed into a local
5.1 repository of pekka. When pekka does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html

ChangeSet
  1.1893 05/09/15 02:33:28 pekka@stripped +27 -0
  ndb - wl#2624 re-commit due to bk problem

  storage/ndb/src/ndbapi/NdbScanOperation.cpp
    1.71 05/09/15 02:31:41 pekka@stripped +25 -0
    wl#2624 re-commit due to bk problem

  storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp
    1.91 05/09/15 02:31:41 pekka@stripped +10 -1
    wl#2624 re-commit due to bk problem

  storage/ndb/src/ndbapi/NdbDictionary.cpp
    1.39 05/09/15 02:31:41 pekka@stripped +1 -0
    wl#2624 re-commit due to bk problem

  storage/ndb/src/ndbapi/Makefile.am
    1.16 05/09/15 02:31:41 pekka@stripped +1 -0
    wl#2624 re-commit due to bk problem

  storage/ndb/src/kernel/blocks/dbtux/Makefile.am
    1.8 05/09/15 02:31:41 pekka@stripped +1 -0
    wl#2624 re-commit due to bk problem

  storage/ndb/src/kernel/blocks/dbtux/DbtuxTree.cpp
    1.16 05/09/15 02:31:41 pekka@stripped +51 -42
    wl#2624 re-commit due to bk problem

  storage/ndb/src/kernel/blocks/dbtux/DbtuxGen.cpp
    1.20 05/09/15 02:31:41 pekka@stripped +4 -0
    wl#2624 re-commit due to bk problem

  storage/ndb/src/kernel/blocks/dbtux/Dbtux.hpp
    1.42 05/09/15 02:31:41 pekka@stripped +16 -1
    wl#2624 re-commit due to bk problem

  storage/ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp
    1.20 05/09/15 02:31:41 pekka@stripped +12 -0
    wl#2624 re-commit due to bk problem

  storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp
    1.77 05/09/15 02:31:40 pekka@stripped +11 -2
    wl#2624 re-commit due to bk problem

  storage/ndb/include/ndbapi/NdbIndexScanOperation.hpp
    1.21 05/09/15 02:31:40 pekka@stripped +3 -0
    wl#2624 re-commit due to bk problem

  storage/ndb/include/ndbapi/NdbDictionary.hpp
    1.55 05/09/15 02:31:40 pekka@stripped +2 -0
    wl#2624 re-commit due to bk problem

  storage/ndb/include/kernel/AttributeHeader.hpp
    1.10 05/09/15 02:31:40 pekka@stripped +2 -0
    wl#2624 re-commit due to bk problem

  sql/sql_class.h
    1.262 05/09/15 02:31:40 pekka@stripped +3 -0
    wl#2624 re-commit due to bk problem

  sql/set_var.cc
    1.137 05/09/15 02:31:40 pekka@stripped +16 -1
    wl#2624 re-commit due to bk problem

  sql/mysqld.cc
    1.480 05/09/15 02:31:39 pekka@stripped +25 -0
    wl#2624 re-commit due to bk problem

  sql/ha_ndbcluster.h
    1.95 05/09/15 02:31:39 pekka@stripped +9 -1
    wl#2624 re-commit due to bk problem

  sql/ha_ndbcluster.cc
    1.209 05/09/15 02:31:39 pekka@stripped +123 -4
    wl#2624 re-commit due to bk problem

  mysql-test/t/ndb_index_ordered.test
    1.26 05/09/15 02:31:39 pekka@stripped +74 -0
    wl#2624 re-commit due to bk problem

  mysql-test/r/ndb_index_ordered.result
    1.23 05/09/15 02:31:39 pekka@stripped +138 -0
    wl#2624 re-commit due to bk problem

  mysql-test/r/ndb_condition_pushdown.result
    1.16 05/09/15 02:31:39 pekka@stripped +13 -13
    wl#2624 re-commit due to bk problem

  mysql-test/r/ndb_charset.result
    1.6 05/09/15 02:31:38 pekka@stripped +2 -2
    wl#2624 re-commit due to bk problem

  mysql-test/r/ndb_blob.result
    1.16 05/09/15 02:31:38 pekka@stripped +3 -3
    wl#2624 re-commit due to bk problem

  mysql-test/r/ndb_basic.result
    1.29 05/09/15 02:31:38 pekka@stripped +1 -1
    wl#2624 re-commit due to bk problem

  storage/ndb/src/ndbapi/NdbIndexStat.cpp
    1.1 05/09/15 02:30:17 pekka@stripped +490 -0

  storage/ndb/src/kernel/blocks/dbtux/DbtuxStat.cpp
    1.1 05/09/15 02:30:17 pekka@stripped +160 -0

  storage/ndb/include/ndbapi/NdbIndexStat.hpp
    1.1 05/09/15 02:30:17 pekka@stripped +141 -0

  storage/ndb/src/ndbapi/NdbIndexStat.cpp
    1.0 05/09/15 02:30:17 pekka@stripped +0 -0
    BitKeeper file /export/space/pekka/ndb/version/my51-rir/storage/ndb/src/ndbapi/NdbIndexStat.cpp

  storage/ndb/src/kernel/blocks/dbtux/DbtuxStat.cpp
    1.0 05/09/15 02:30:17 pekka@stripped +0 -0
    BitKeeper file /export/space/pekka/ndb/version/my51-rir/storage/ndb/src/kernel/blocks/dbtux/DbtuxStat.cpp

  storage/ndb/include/ndbapi/NdbIndexStat.hpp
    1.0 05/09/15 02:30:17 pekka@stripped +0 -0
    BitKeeper file /export/space/pekka/ndb/version/my51-rir/storage/ndb/include/ndbapi/NdbIndexStat.hpp

# This is a BitKeeper patch.  What follows are the unified diffs for the
# set of deltas contained in the patch.  The rest of the patch, the part
# that BitKeeper cares about, is below these diffs.
# User:	pekka
# Host:	clam.ndb.mysql.com
# Root:	/export/space/pekka/ndb/version/my51-rir

--- 1.479/sql/mysqld.cc	2005-09-07 09:39:26 +02:00
+++ 1.480/sql/mysqld.cc	2005-09-15 02:31:39 +02:00
@@ -4335,6 +4335,8 @@
   OPT_NDB_SHM, OPT_NDB_OPTIMIZED_NODE_SELECTION, OPT_NDB_CACHE_CHECK_TIME,
   OPT_NDB_MGMD, OPT_NDB_NODEID,
   OPT_NDB_DISTRIBUTION,
+  OPT_NDB_INDEX_STAT_ENABLE,
+  OPT_NDB_INDEX_STAT_CACHE_ENTRIES, OPT_NDB_INDEX_STAT_UPDATE_FREQ,
   OPT_SKIP_SAFEMALLOC,
   OPT_TEMP_POOL, OPT_TX_ISOLATION, OPT_COMPLETION_TYPE,
   OPT_SKIP_STACK_TRACE, OPT_SKIP_SYMLINKS,
@@ -4949,6 +4951,23 @@
     "A dedicated thread is created to, at the given millisecons interval, invalidate the query cache if another MySQL server in the cluster has changed the data in the database.",
     (gptr*) &opt_ndb_cache_check_time, (gptr*) &opt_ndb_cache_check_time, 0, GET_ULONG, REQUIRED_ARG,
     0, 0, LONG_TIMEOUT, 0, 1, 0},
+  {"ndb-index-stat-enable", OPT_NDB_INDEX_STAT_ENABLE,
+   "Use ndb index statistics in query optimization.",
+   (gptr*) &global_system_variables.ndb_index_stat_enable,
+   (gptr*) &max_system_variables.ndb_index_stat_enable,
+   0, GET_BOOL, OPT_ARG, 1, 0, 1, 0, 0, 0},
+  {"ndb-index-stat-cache-entries", OPT_NDB_INDEX_STAT_CACHE_ENTRIES,
+   "Number of start/end keys to store in statistics memory cache."
+    " Zero means no cache and forces query of db nodes always.",
+   (gptr*) &global_system_variables.ndb_index_stat_cache_entries,
+   (gptr*) &max_system_variables.ndb_index_stat_cache_entries,
+   0, GET_ULONG, OPT_ARG, 32, 0, ~0L, 0, 0, 0},
+  {"ndb-index-stat-update-freq", OPT_NDB_INDEX_STAT_UPDATE_FREQ,
+   "How often, in the long run, to query db nodes instead of statistics cache."
+   " For example 20 means every 20th time.",
+   (gptr*) &global_system_variables.ndb_index_stat_update_freq,
+   (gptr*) &max_system_variables.ndb_index_stat_update_freq,
+   0, GET_ULONG, OPT_ARG, 20, 0, ~0L, 0, 0, 0},
 #endif
   {"new", 'n', "Use very new possible 'unsafe' functions.",
    (gptr*) &global_system_variables.new_mode,
@@ -6211,6 +6230,12 @@
 #endif
 #ifdef HAVE_NDBCLUSTER_DB
   have_ndbcluster=SHOW_OPTION_DISABLED;
+  global_system_variables.ndb_index_stat_enable=TRUE;
+  max_system_variables.ndb_index_stat_enable=TRUE;
+  global_system_variables.ndb_index_stat_cache_entries=32;
+  max_system_variables.ndb_index_stat_cache_entries=~0L;
+  global_system_variables.ndb_index_stat_update_freq=20;
+  max_system_variables.ndb_index_stat_update_freq=~0L;
 #else
   have_ndbcluster=SHOW_OPTION_NO;
 #endif

--- 1.261/sql/sql_class.h	2005-09-14 09:39:44 +02:00
+++ 1.262/sql/sql_class.h	2005-09-15 02:31:40 +02:00
@@ -565,6 +565,9 @@
   my_bool ndb_force_send;
   my_bool ndb_use_exact_count;
   my_bool ndb_use_transactions;
+  my_bool ndb_index_stat_enable;
+  ulong ndb_index_stat_cache_entries;
+  ulong ndb_index_stat_update_freq;
 #endif /* HAVE_NDBCLUSTER_DB */
   my_bool old_alter_table;
   my_bool old_passwords;

--- 1.20/storage/ndb/include/ndbapi/NdbIndexScanOperation.hpp	2005-07-26 13:11:34 +02:00
+++ 1.21/storage/ndb/include/ndbapi/NdbIndexScanOperation.hpp	2005-09-15 02:31:40 +02:00
@@ -30,6 +30,7 @@
   friend class NdbResultSet;
   friend class NdbOperation;
   friend class NdbScanOperation;
+  friend class NdbIndexStat;
 #endif
 
 public:
@@ -149,12 +150,14 @@
    * Is current scan sorted descending
    */
   bool getDescending() const { return m_descending; }
+
 private:
   NdbIndexScanOperation(Ndb* aNdb);
   virtual ~NdbIndexScanOperation();
 
   int setBound(const NdbColumnImpl*, int type, const void* aValue, Uint32 len);
   int insertBOUNDS(Uint32 * data, Uint32 sz);
+  Uint32 getKeyFromSCANTABREQ(Uint32* data, Uint32 size);
 
   virtual int equal_impl(const NdbColumnImpl*, const char*, Uint32);
   virtual NdbRecAttr* getValue_impl(const NdbColumnImpl*, char*);

--- 1.28/mysql-test/r/ndb_basic.result	2005-05-19 20:38:45 +02:00
+++ 1.29/mysql-test/r/ndb_basic.result	2005-09-15 02:31:38 +02:00
@@ -568,7 +568,7 @@
 insert into t1 values (1,1),(2,1),(3,1),(4,1),(5,2),(6,1),(7,1);
 explain select * from t1 where a12345678901234567890123456789a1234567890=2;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	ref	a12345678901234567890123456789a1234567890	a12345678901234567890123456789a1234567890	5	const	10	Using where
+1	SIMPLE	t1	ref	a12345678901234567890123456789a1234567890	a12345678901234567890123456789a1234567890	5	const	1	Using where
 select * from t1 where a12345678901234567890123456789a1234567890=2;
 a1234567890123456789012345678901234567890	a12345678901234567890123456789a1234567890
 5	2

--- 1.22/mysql-test/r/ndb_index_ordered.result	2005-09-01 14:12:04 +02:00
+++ 1.23/mysql-test/r/ndb_index_ordered.result	2005-09-15 02:31:39 +02:00
@@ -658,3 +658,141 @@
 select count(*) from t1 where c<'bbb';
 count(*)
 1
+drop table t1;
+set autocommit=1;
+show session variables like 'ndb_index_stat_%';
+Variable_name	Value
+ndb_index_stat_cache_entries	32
+ndb_index_stat_enable	ON
+ndb_index_stat_update_freq	20
+set ndb_index_stat_enable = off;
+show session variables like 'ndb_index_stat_%';
+Variable_name	Value
+ndb_index_stat_cache_entries	32
+ndb_index_stat_enable	OFF
+ndb_index_stat_update_freq	20
+create table t1 (a int, b int, c varchar(10) not null,
+primary key using hash (a), index(b,c)) engine=ndb;
+insert into t1 values
+(1,10,'aaa'),(2,10,'bbb'),(3,10,'ccc'),
+(4,20,'aaa'),(5,20,'bbb'),(6,20,'ccc'),
+(7,30,'aaa'),(8,30,'bbb'),(9,30,'ccc');
+select count(*) from t1 where b < 10;
+count(*)
+0
+select count(*) from t1 where b >= 10 and c >= 'bbb';
+count(*)
+6
+select count(*) from t1 where b > 10;
+count(*)
+6
+select count(*) from t1 where b <= 20 and c < 'ccc';
+count(*)
+4
+select count(*) from t1 where b = 20 and c = 'ccc';
+count(*)
+1
+select count(*) from t1 where b > 20;
+count(*)
+3
+select count(*) from t1 where b = 30 and c > 'aaa';
+count(*)
+2
+select count(*) from t1 where b <= 20;
+count(*)
+6
+select count(*) from t1 where b >= 20 and c > 'aaa';
+count(*)
+4
+drop table t1;
+set ndb_index_stat_enable = on;
+set ndb_index_stat_cache_entries = 0;
+show session variables like 'ndb_index_stat_%';
+Variable_name	Value
+ndb_index_stat_cache_entries	0
+ndb_index_stat_enable	ON
+ndb_index_stat_update_freq	20
+create table t1 (a int, b int, c varchar(10) not null,
+primary key using hash (a), index(b,c)) engine=ndb;
+insert into t1 values
+(1,10,'aaa'),(2,10,'bbb'),(3,10,'ccc'),
+(4,20,'aaa'),(5,20,'bbb'),(6,20,'ccc'),
+(7,30,'aaa'),(8,30,'bbb'),(9,30,'ccc');
+select count(*) from t1 where b < 10;
+count(*)
+0
+select count(*) from t1 where b >= 10 and c >= 'bbb';
+count(*)
+6
+select count(*) from t1 where b > 10;
+count(*)
+6
+select count(*) from t1 where b <= 20 and c < 'ccc';
+count(*)
+4
+select count(*) from t1 where b = 20 and c = 'ccc';
+count(*)
+1
+select count(*) from t1 where b > 20;
+count(*)
+3
+select count(*) from t1 where b = 30 and c > 'aaa';
+count(*)
+2
+select count(*) from t1 where b <= 20;
+count(*)
+6
+select count(*) from t1 where b >= 20 and c > 'aaa';
+count(*)
+4
+drop table t1;
+set ndb_index_stat_enable = on;
+set ndb_index_stat_cache_entries = 4;
+set ndb_index_stat_update_freq = 2;
+show session variables like 'ndb_index_stat_%';
+Variable_name	Value
+ndb_index_stat_cache_entries	4
+ndb_index_stat_enable	ON
+ndb_index_stat_update_freq	2
+create table t1 (a int, b int, c varchar(10) not null,
+primary key using hash (a), index(b,c)) engine=ndb;
+insert into t1 values
+(1,10,'aaa'),(2,10,'bbb'),(3,10,'ccc'),
+(4,20,'aaa'),(5,20,'bbb'),(6,20,'ccc'),
+(7,30,'aaa'),(8,30,'bbb'),(9,30,'ccc');
+select count(*) from t1 where b < 10;
+count(*)
+0
+select count(*) from t1 where b >= 10 and c >= 'bbb';
+count(*)
+6
+select count(*) from t1 where b > 10;
+count(*)
+6
+select count(*) from t1 where b <= 20 and c < 'ccc';
+count(*)
+4
+select count(*) from t1 where b = 20 and c = 'ccc';
+count(*)
+1
+select count(*) from t1 where b > 20;
+count(*)
+3
+select count(*) from t1 where b = 30 and c > 'aaa';
+count(*)
+2
+select count(*) from t1 where b <= 20;
+count(*)
+6
+select count(*) from t1 where b >= 20 and c > 'aaa';
+count(*)
+4
+drop table t1;
+set ndb_index_stat_enable = @@global.ndb_index_stat_enable;
+set ndb_index_stat_cache_entries = @@global.ndb_index_stat_cache_entries;
+set ndb_index_stat_update_freq = @@global.ndb_index_stat_update_freq;
+show session variables like 'ndb_index_stat_%';
+Variable_name	Value
+ndb_index_stat_cache_entries	32
+ndb_index_stat_enable	ON
+ndb_index_stat_update_freq	20

--- 1.25/mysql-test/t/ndb_index_ordered.test	2005-09-01 14:12:04 +02:00
+++ 1.26/mysql-test/t/ndb_index_ordered.test	2005-09-15 02:31:39 +02:00
@@ -354,5 +354,79 @@
   primary key using hash (a), index(c)) engine=ndb;
 insert into t1 (a, c) values (1,'aaa'),(3,'bbb');
 select count(*) from t1 where c<'bbb';
+drop table t1;
+
+# -- index statistics --
+
+set autocommit=1;
+show session variables like 'ndb_index_stat_%';
+
+set ndb_index_stat_enable = off;
+show session variables like 'ndb_index_stat_%';
+
+create table t1 (a int, b int, c varchar(10) not null,
+  primary key using hash (a), index(b,c)) engine=ndb;
+insert into t1 values
+  (1,10,'aaa'),(2,10,'bbb'),(3,10,'ccc'),
+  (4,20,'aaa'),(5,20,'bbb'),(6,20,'ccc'),
+  (7,30,'aaa'),(8,30,'bbb'),(9,30,'ccc');
+select count(*) from t1 where b < 10;
+select count(*) from t1 where b >= 10 and c >= 'bbb';
+select count(*) from t1 where b > 10;
+select count(*) from t1 where b <= 20 and c < 'ccc';
+select count(*) from t1 where b = 20 and c = 'ccc';
+select count(*) from t1 where b > 20;
+select count(*) from t1 where b = 30 and c > 'aaa';
+select count(*) from t1 where b <= 20;
+select count(*) from t1 where b >= 20 and c > 'aaa';
+drop table t1;
+
+set ndb_index_stat_enable = on;
+set ndb_index_stat_cache_entries = 0;
+show session variables like 'ndb_index_stat_%';
+
+create table t1 (a int, b int, c varchar(10) not null,
+  primary key using hash (a), index(b,c)) engine=ndb;
+insert into t1 values
+  (1,10,'aaa'),(2,10,'bbb'),(3,10,'ccc'),
+  (4,20,'aaa'),(5,20,'bbb'),(6,20,'ccc'),
+  (7,30,'aaa'),(8,30,'bbb'),(9,30,'ccc');
+select count(*) from t1 where b < 10;
+select count(*) from t1 where b >= 10 and c >= 'bbb';
+select count(*) from t1 where b > 10;
+select count(*) from t1 where b <= 20 and c < 'ccc';
+select count(*) from t1 where b = 20 and c = 'ccc';
+select count(*) from t1 where b > 20;
+select count(*) from t1 where b = 30 and c > 'aaa';
+select count(*) from t1 where b <= 20;
+select count(*) from t1 where b >= 20 and c > 'aaa';
+drop table t1;
+
+set ndb_index_stat_enable = on;
+set ndb_index_stat_cache_entries = 4;
+set ndb_index_stat_update_freq = 2;
+show session variables like 'ndb_index_stat_%';
+
+create table t1 (a int, b int, c varchar(10) not null,
+  primary key using hash (a), index(b,c)) engine=ndb;
+insert into t1 values
+  (1,10,'aaa'),(2,10,'bbb'),(3,10,'ccc'),
+  (4,20,'aaa'),(5,20,'bbb'),(6,20,'ccc'),
+  (7,30,'aaa'),(8,30,'bbb'),(9,30,'ccc');
+select count(*) from t1 where b < 10;
+select count(*) from t1 where b >= 10 and c >= 'bbb';
+select count(*) from t1 where b > 10;
+select count(*) from t1 where b <= 20 and c < 'ccc';
+select count(*) from t1 where b = 20 and c = 'ccc';
+select count(*) from t1 where b > 20;
+select count(*) from t1 where b = 30 and c > 'aaa';
+select count(*) from t1 where b <= 20;
+select count(*) from t1 where b >= 20 and c > 'aaa';
+drop table t1;
+
+set ndb_index_stat_enable = @@global.ndb_index_stat_enable;
+set ndb_index_stat_cache_entries = @@global.ndb_index_stat_cache_entries;
+set ndb_index_stat_update_freq = @@global.ndb_index_stat_update_freq;
+show session variables like 'ndb_index_stat_%';
 
 # End of 4.1 tests

--- 1.9/storage/ndb/include/kernel/AttributeHeader.hpp	2005-07-18 13:30:18 +02:00
+++ 1.10/storage/ndb/include/kernel/AttributeHeader.hpp	2005-09-15 02:31:40 +02:00
@@ -41,6 +41,8 @@
   STATIC_CONST( ROW_SIZE     = 0xFFFA );
   STATIC_CONST( FRAGMENT_MEMORY= 0xFFF9 );
 
+  STATIC_CONST( RECORDS_IN_RANGE = 0xFFF8 );
+
   /** Initialize AttributeHeader at location aHeaderPtr */
   static AttributeHeader& init(void* aHeaderPtr, Uint32 anAttributeId, 
 			       Uint32 aDataSize);

--- 1.54/storage/ndb/include/ndbapi/NdbDictionary.hpp	2005-08-27 07:42:49 +02:00
+++ 1.55/storage/ndb/include/ndbapi/NdbDictionary.hpp	2005-09-15 02:31:40 +02:00
@@ -456,6 +456,7 @@
     static const Column * COMMIT_COUNT;
     static const Column * ROW_SIZE;
     static const Column * RANGE_NO;
+    static const Column * RECORDS_IN_RANGE;
     
     int getSizeInBytes() const;
 #endif
@@ -929,6 +930,7 @@
   private:
 #ifndef DOXYGEN_SHOULD_SKIP_INTERNAL
     friend class NdbIndexImpl;
+    friend class NdbIndexStat;
 #endif
     class NdbIndexImpl & m_impl;
     Index(NdbIndexImpl&);

--- 1.76/storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp	2005-09-10 21:06:16 +02:00
+++ 1.77/storage/ndb/src/kernel/blocks/dblqh/DblqhMain.cpp	2005-09-15 02:31:40 +02:00
@@ -2622,7 +2622,10 @@
   regTcPtr.i = signal->theData[0];
   ptrCheckGuard(regTcPtr, ctcConnectrecFileSize, tcConnectionrec);
   
-  if(signal->theData[1] != AttributeHeader::RANGE_NO)
+  if (signal->theData[1] == AttributeHeader::RANGE_NO) {
+    signal->theData[0] = regTcPtr.p->m_scan_curr_range_no;
+  }
+  else if (signal->theData[1] != AttributeHeader::RECORDS_IN_RANGE)
   {
     jam();
     FragrecordPtr regFragptr;
@@ -2634,7 +2637,13 @@
   }
   else
   {
-    signal->theData[0] = regTcPtr.p->m_scan_curr_range_no;
+    jam();
+    // scanptr gets reset somewhere within the timeslice
+    ScanRecordPtr tmp;
+    tmp.i = regTcPtr.p->tcScanRec;
+    c_scanRecordPool.getPtr(tmp);
+    signal->theData[0] = tmp.p->scanAccPtr;
+    EXECUTE_DIRECT(DBTUX, GSN_READ_PSEUDO_REQ, signal, 2);
   }
 }
 

--- 1.19/storage/ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp	2005-07-18 13:30:23 +02:00
+++ 1.20/storage/ndb/src/kernel/blocks/dbtup/DbtupRoutines.cpp	2005-09-15 02:31:41 +02:00
@@ -1028,6 +1028,18 @@
     EXECUTE_DIRECT(DBLQH, GSN_READ_PSEUDO_REQ, signal, 2);
     outBuffer[0] = signal->theData[0];
     return 1;
+
+  case AttributeHeader::RECORDS_IN_RANGE:
+    signal->theData[0] = operPtr.p->userpointer;
+    signal->theData[1] = attrId;
+    
+    EXECUTE_DIRECT(DBLQH, GSN_READ_PSEUDO_REQ, signal, 2);
+    outBuffer[0] = signal->theData[0];
+    outBuffer[1] = signal->theData[1];
+    outBuffer[2] = signal->theData[2];
+    outBuffer[3] = signal->theData[3];
+    return 4;
+
   default:
     return 0;
   }

--- 1.41/storage/ndb/src/kernel/blocks/dbtux/Dbtux.hpp	2005-05-31 14:34:31 +02:00
+++ 1.42/storage/ndb/src/kernel/blocks/dbtux/Dbtux.hpp	2005-09-15 02:31:41 +02:00
@@ -82,10 +82,14 @@
 #define jam()           jamLine(80000 + __LINE__)
 #define jamEntry()      jamEntryLine(80000 + __LINE__)
 #endif
-#ifdef DBTUX_DEBUG_CPP
+#ifdef DBTUX_STAT_CPP
 #define jam()           jamLine(90000 + __LINE__)
 #define jamEntry()      jamEntryLine(90000 + __LINE__)
 #endif
+#ifdef DBTUX_DEBUG_CPP
+#define jam()           jamLine(100000 + __LINE__)
+#define jamEntry()      jamEntryLine(100000 + __LINE__)
+#endif
 #ifndef jam
 #define jam()           jamLine(__LINE__)
 #define jamEntry()      jamEntryLine(__LINE__)
@@ -116,6 +120,7 @@
   STATIC_CONST( MaxPrefSize = MAX_TTREE_PREF_SIZE );
   STATIC_CONST( ScanBoundSegmentSize = 7 );
   STATIC_CONST( MaxAccLockOps = MAX_PARALLEL_OP_PER_SCAN );
+  STATIC_CONST( MaxTreeDepth = 32 );    // strict
   BLOCK_DEFINES(Dbtux);
 
   // forward declarations
@@ -269,6 +274,7 @@
     Uint8 m_prefSize;           // words in min prefix
     Uint8 m_minOccup;           // min entries in internal node
     Uint8 m_maxOccup;           // max entries in node
+    Uint32 m_entryCount;        // stat: current entries
     TupLoc m_root;              // root node
     TreeHead();
     // methods
@@ -660,6 +666,14 @@
   int cmpScanBound(const Frag& frag, unsigned dir, ConstData boundInfo, unsigned boundCount, ConstData entryData, unsigned maxlen = MaxAttrDataSize);
 
   /*
+   * DbtuxStat.cpp
+   */
+  void execREAD_PSEUDO_REQ(Signal* signal);
+  void statRecordsInRange(ScanOpPtr scanPtr, Uint32* out);
+  Uint32 getEntriesBeforeOrAfter(Frag& frag, TreePos pos, unsigned idir);
+  unsigned getPathToNode(NodeHandle node, Uint16* path);
+
+  /*
    * DbtuxDebug.cpp
    */
   void execDUMP_STATE_ORD(Signal* signal);
@@ -952,6 +966,7 @@
   m_prefSize(0),
   m_minOccup(0),
   m_maxOccup(0),
+  m_entryCount(0),
   m_root()
 {
 }

--- 1.19/storage/ndb/src/kernel/blocks/dbtux/DbtuxGen.cpp	2005-04-08 02:44:05 +02:00
+++ 1.20/storage/ndb/src/kernel/blocks/dbtux/DbtuxGen.cpp	2005-09-15 02:31:41 +02:00
@@ -66,6 +66,10 @@
   addRecSignal(GSN_ACCKEYREF, &Dbtux::execACCKEYREF);
   addRecSignal(GSN_ACC_ABORTCONF, &Dbtux::execACC_ABORTCONF);
   /*
+   * DbtuxStat.cpp
+   */
+  addRecSignal(GSN_READ_PSEUDO_REQ, &Dbtux::execREAD_PSEUDO_REQ);
+  /*
    * DbtuxDebug.cpp
    */
   addRecSignal(GSN_DUMP_STATE_ORD, &Dbtux::execDUMP_STATE_ORD);

--- 1.15/storage/ndb/src/kernel/blocks/dbtux/DbtuxTree.cpp	2005-04-08 02:44:05 +02:00
+++ 1.16/storage/ndb/src/kernel/blocks/dbtux/DbtuxTree.cpp	2005-09-15 02:31:41 +02:00
@@ -26,25 +26,29 @@
 {
   TreeHead& tree = frag.m_tree;
   NodeHandle node(frag);
-  if (treePos.m_loc != NullTupLoc) {
-    // non-empty tree
-    jam();
-    selectNode(node, treePos.m_loc);
-    unsigned pos = treePos.m_pos;
-    if (node.getOccup() < tree.m_maxOccup) {
-      // node has room
-      jam();
-      nodePushUp(node, pos, ent, RNIL);
-      return;
+  do {
+    if (treePos.m_loc != NullTupLoc) {
+      // non-empty tree
+      jam();
+      selectNode(node, treePos.m_loc);
+      unsigned pos = treePos.m_pos;
+      if (node.getOccup() < tree.m_maxOccup) {
+        // node has room
+        jam();
+        nodePushUp(node, pos, ent, RNIL);
+        break;
+      }
+      treeAddFull(frag, node, pos, ent);
+      break;
     }
-    treeAddFull(frag, node, pos, ent);
-    return;
-  }
-  jam();
-  insertNode(node);
-  nodePushUp(node, 0, ent, RNIL);
-  node.setSide(2);
-  tree.m_root = node.m_loc;
+    jam();
+    insertNode(node);
+    nodePushUp(node, 0, ent, RNIL);
+    node.setSide(2);
+    tree.m_root = node.m_loc;
+    break;
+  } while (0);
+  tree.m_entryCount++;
 }
 
 /*
@@ -178,31 +182,36 @@
   NodeHandle node(frag);
   selectNode(node, treePos.m_loc);
   TreeEnt ent;
-  if (node.getOccup() > tree.m_minOccup) {
-    // no underflow in any node type
-    jam();
+  do {
+    if (node.getOccup() > tree.m_minOccup) {
+      // no underflow in any node type
+      jam();
+      nodePopDown(node, pos, ent, 0);
+      break;
+    }
+    if (node.getChilds() == 2) {
+      // underflow in interior node
+      jam();
+      treeRemoveInner(frag, node, pos);
+      break;
+    }
+    // remove entry in semi/leaf
     nodePopDown(node, pos, ent, 0);
-    return;
-  }
-  if (node.getChilds() == 2) {
-    // underflow in interior node
-    jam();
-    treeRemoveInner(frag, node, pos);
-    return;
-  }
-  // remove entry in semi/leaf
-  nodePopDown(node, pos, ent, 0);
-  if (node.getLink(0) != NullTupLoc) {
-    jam();
-    treeRemoveSemi(frag, node, 0);
-    return;
-  }
-  if (node.getLink(1) != NullTupLoc) {
-    jam();
-    treeRemoveSemi(frag, node, 1);
-    return;
-  }
-  treeRemoveLeaf(frag, node);
+    if (node.getLink(0) != NullTupLoc) {
+      jam();
+      treeRemoveSemi(frag, node, 0);
+      break;
+    }
+    if (node.getLink(1) != NullTupLoc) {
+      jam();
+      treeRemoveSemi(frag, node, 1);
+      break;
+    }
+    treeRemoveLeaf(frag, node);
+    break;
+  } while (0);
+  ndbrequire(tree.m_entryCount != 0);
+  tree.m_entryCount--;
 }
 
 /*

--- 1.38/storage/ndb/src/ndbapi/NdbDictionary.cpp	2005-07-18 13:30:24 +02:00
+++ 1.39/storage/ndb/src/ndbapi/NdbDictionary.cpp	2005-09-15 02:31:41 +02:00
@@ -1070,3 +1070,4 @@
 const NdbDictionary::Column * NdbDictionary::Column::COMMIT_COUNT = 0;
 const NdbDictionary::Column * NdbDictionary::Column::ROW_SIZE = 0;
 const NdbDictionary::Column * NdbDictionary::Column::RANGE_NO = 0;
+const NdbDictionary::Column * NdbDictionary::Column::RECORDS_IN_RANGE = 0;

--- 1.90/storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp	2005-08-27 07:42:51 +02:00
+++ 1.91/storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp	2005-09-15 02:31:41 +02:00
@@ -266,6 +266,11 @@
     col->m_impl.m_attrId = AttributeHeader::RANGE_NO;
     col->m_impl.m_attrSize = 4;
     col->m_impl.m_arraySize = 1;
+  } else if(!strcmp(name, "NDB$RECORDS_IN_RANGE")){
+    col->setType(NdbDictionary::Column::Unsigned);
+    col->m_impl.m_attrId = AttributeHeader::RECORDS_IN_RANGE;
+    col->m_impl.m_attrSize = 4;
+    col->m_impl.m_arraySize = 4;
   } else {
     abort();
   }
@@ -739,12 +744,14 @@
       delete NdbDictionary::Column::COMMIT_COUNT;
       delete NdbDictionary::Column::ROW_SIZE;
       delete NdbDictionary::Column::RANGE_NO;
+      delete NdbDictionary::Column::RECORDS_IN_RANGE;
       NdbDictionary::Column::FRAGMENT= 0;
       NdbDictionary::Column::FRAGMENT_MEMORY= 0;
       NdbDictionary::Column::ROW_COUNT= 0;
       NdbDictionary::Column::COMMIT_COUNT= 0;
       NdbDictionary::Column::ROW_SIZE= 0;
       NdbDictionary::Column::RANGE_NO= 0;
+      NdbDictionary::Column::RECORDS_IN_RANGE= 0;
     }
     m_globalHash->unlock();
   } else {
@@ -817,6 +824,8 @@
 	NdbColumnImpl::create_pseudo("NDB$ROW_SIZE");
       NdbDictionary::Column::RANGE_NO= 
 	NdbColumnImpl::create_pseudo("NDB$RANGE_NO");
+      NdbDictionary::Column::RECORDS_IN_RANGE= 
+	NdbColumnImpl::create_pseudo("NDB$RECORDS_IN_RANGE");
     }
     m_globalHash->unlock();
     return true;
@@ -2175,6 +2184,7 @@
   }
 
   * dst = idx;
+
   return 0;
 }
 
@@ -3209,4 +3219,3 @@
 template class Vector<Vector<Uint32> >;
 template class Vector<NdbTableImpl*>;
 template class Vector<NdbColumnImpl*>;
-

--- 1.70/storage/ndb/src/ndbapi/NdbScanOperation.cpp	2005-07-26 13:12:09 +02:00
+++ 1.71/storage/ndb/src/ndbapi/NdbScanOperation.cpp	2005-09-15 02:31:41 +02:00
@@ -1198,6 +1198,31 @@
   return -1;
 }
 
+Uint32
+NdbIndexScanOperation::getKeyFromSCANTABREQ(Uint32* data, Uint32 size)
+{
+  DBUG_ENTER("NdbIndexScanOperation::getKeyFromSCANTABREQ");
+  assert(size >= theTotalNrOfKeyWordInSignal);
+  size = theTotalNrOfKeyWordInSignal;
+  NdbApiSignal* tSignal = theSCAN_TABREQ->next();
+  Uint32 pos = 0;
+  while (pos < size) {
+    assert(tSignal != NULL);
+    Uint32* tData = tSignal->getDataPtrSend();
+    Uint32 rem = size - pos;
+    if (rem > KeyInfo::DataLength)
+      rem = KeyInfo::DataLength;
+    Uint32 i = 0;
+    while (i < rem) {
+      data[pos + i] = tData[KeyInfo::HeaderLength + i];
+      i++;
+    }
+    pos += rem;
+  }
+  DBUG_DUMP("key", (char*)data, size << 2);
+  DBUG_RETURN(size);
+}
+
 int
 NdbIndexScanOperation::readTuples(LockMode lm,
 				  Uint32 scan_flags,

--- 1.208/sql/ha_ndbcluster.cc	2005-09-14 09:39:43 +02:00
+++ 1.209/sql/ha_ndbcluster.cc	2005-09-15 02:31:39 +02:00
@@ -31,6 +31,7 @@
 #include "ha_ndbcluster.h"
 #include <ndbapi/NdbApi.hpp>
 #include <ndbapi/NdbScanFilter.hpp>
+#include <ndbapi/NdbIndexStat.hpp>
 
 // options from from mysqld.cc
 extern my_bool opt_ndb_optimized_node_selection;
@@ -85,6 +86,14 @@
   DBUG_RETURN(ndb_to_mysql_error(&tmp)); \
 }
 
+#define ERR_BREAK(err, code)             \
+{                                        \
+  const NdbError& tmp= err;              \
+  ERR_PRINT(tmp);                        \
+  code= ndb_to_mysql_error(&tmp);        \
+  break;                                 \
+}
+
 // Typedefs for long names
 typedef NdbDictionary::Column NDBCOL;
 typedef NdbDictionary::Table NDBTAB;
@@ -1064,6 +1073,26 @@
       const NDBINDEX *index= dict->getIndex(index_name, m_tabname);
       if (!index) DBUG_RETURN(1);
       m_index[i].index= (void *) index;
+      // ordered index - add stats
+      NDB_INDEX_DATA& d=m_index[i];
+      delete d.index_stat;
+      d.index_stat=NULL;
+      THD *thd=current_thd;
+      if (thd->variables.ndb_index_stat_enable)
+      {
+        d.index_stat=new NdbIndexStat(index);
+        d.index_stat_cache_entries=thd->variables.ndb_index_stat_cache_entries;
+        d.index_stat_update_freq=thd->variables.ndb_index_stat_update_freq;
+        d.index_stat_query_count=0;
+        d.index_stat->alloc_cache(d.index_stat_cache_entries);
+        DBUG_PRINT("info", ("index %s stat=on cache_entries=%u update_freq=%u",
+                            index->getName(),
+                            d.index_stat_cache_entries,
+                            d.index_stat_update_freq));
+      } else
+      {
+        DBUG_PRINT("info", ("index %s stat=off", index->getName()));
+      }
     }
     if (idx_type == UNIQUE_ORDERED_INDEX || idx_type == UNIQUE_INDEX)
     {
@@ -1135,6 +1164,8 @@
       my_free((char *)m_index[i].unique_index_attrid_map, MYF(0));
       m_index[i].unique_index_attrid_map= NULL;
     }
+    delete m_index[i].index_stat;
+    m_index[i].index_stat=NULL;
   }
 
   DBUG_VOID_RETURN;
@@ -1648,10 +1679,12 @@
 */
 
 int ha_ndbcluster::set_bounds(NdbIndexScanOperation *op,
+                              uint inx,
+                              bool rir,
                               const key_range *keys[2],
                               uint range_no)
 {
-  const KEY *const key_info= table->key_info + active_index;
+  const KEY *const key_info= table->key_info + inx;
   const uint key_parts= key_info->key_parts;
   uint key_tot_len[2];
   uint tot_len;
@@ -1716,7 +1749,10 @@
           switch (p.key->flag)
           {
             case HA_READ_KEY_EXACT:
-              p.bound_type= NdbIndexScanOperation::BoundEQ;
+              if (! rir)
+                p.bound_type= NdbIndexScanOperation::BoundEQ;
+              else // differs for records_in_range
+                p.bound_type= NdbIndexScanOperation::BoundLE;
               break;
             // ascending
             case HA_READ_KEY_OR_NEXT:
@@ -1874,7 +1910,7 @@
   
   {
     const key_range *keys[2]= { start_key, end_key };
-    res= set_bounds(op, keys);
+    res= set_bounds(op, active_index, false, keys);
     if (res)
       DBUG_RETURN(res);
   }
@@ -4162,6 +4198,10 @@
     m_index[i].unique_index= NULL;
     m_index[i].index= NULL;
     m_index[i].unique_index_attrid_map= NULL;
+    m_index[i].index_stat=NULL;
+    m_index[i].index_stat_cache_entries=0;
+    m_index[i].index_stat_update_freq=0;
+    m_index[i].index_stat_query_count=0;
   }
 
   DBUG_VOID_RETURN;
@@ -4914,6 +4954,84 @@
        (max_key && max_key->length == key_length)))
     DBUG_RETURN(1);
   
+  if ((idx_type == PRIMARY_KEY_ORDERED_INDEX ||
+       idx_type == UNIQUE_ORDERED_INDEX ||
+       idx_type == ORDERED_INDEX) &&
+    m_index[inx].index_stat != NULL)
+  {
+    NDB_INDEX_DATA& d=m_index[inx];
+    NDBINDEX* index=(NDBINDEX*)d.index;
+    Ndb* ndb=get_ndb();
+    NdbTransaction* trans=NULL;
+    NdbIndexScanOperation* op=NULL;
+    int res=0;
+    Uint64 rows;
+
+    do
+    {
+      // We must provide approx table rows
+      Uint64 table_rows=0;
+      Ndb_local_table_statistics *info= 
+        (Ndb_local_table_statistics *)m_table_info;
+      if (info->records != ~(ha_rows)0 && info->records != 0)
+      {
+        table_rows = info->records;
+        DBUG_PRINT("info", ("use info->records: %llu", table_rows));
+      }
+      else
+      {
+        Ndb_statistics stat;
+        if ((res=ndb_get_table_statistics(ndb, m_tabname, &stat)) != 0)
+          break;
+        table_rows=stat.row_count;
+        DBUG_PRINT("info", ("use db row_count: %llu", table_rows));
+        if (table_rows == 0) {
+          // Problem if autocommit=0
+#ifdef ndb_get_table_statistics_uses_active_trans
+          rows=0;
+          break;
+#endif
+        }
+      }
+
+      // Define scan op for the range
+      if ((trans=m_active_trans) == NULL)
+      {
+        DBUG_PRINT("info", ("no active trans"));
+        if (! (trans=ndb->startTransaction()))
+          ERR_BREAK(ndb->getNdbError(), res);
+      }
+      if (! (op=trans->getNdbIndexScanOperation(index, (NDBTAB*)m_table)))
+        ERR_BREAK(trans->getNdbError(), res);
+      if ((op->readTuples(NdbOperation::LM_CommittedRead)) == -1)
+        ERR_BREAK(op->getNdbError(), res);
+      const key_range *keys[2]={ min_key, max_key };
+      if ((res=set_bounds(op, inx, true, keys)) != 0)
+        break;
+
+      // Decide if db should be contacted
+      int flags=0;
+      if (d.index_stat_query_count < d.index_stat_cache_entries ||
+          (d.index_stat_update_freq != 0 &&
+           d.index_stat_query_count % d.index_stat_update_freq == 0))
+      {
+        DBUG_PRINT("info", ("force stat from db"));
+        flags|=NdbIndexStat::RR_UseDb;
+      }
+      if (d.index_stat->records_in_range(index, op, table_rows, &rows, flags) == -1)
+        ERR_BREAK(d.index_stat->getNdbError(), res);
+      d.index_stat_query_count++;
+    } while (0);
+
+    if (trans != m_active_trans && rows == 0)
+      rows = 1;
+    if (trans != m_active_trans && trans != NULL)
+      ndb->closeTransaction(trans);
+    if (res != 0)
+      DBUG_RETURN(HA_POS_ERROR);
+    DBUG_RETURN(rows);
+  }
+
   DBUG_RETURN(10); /* Good guess when you don't know anything */
 }
 
@@ -5612,7 +5730,8 @@
 
       const key_range *keys[2]= { &multi_range_curr->start_key, 
                                   &multi_range_curr->end_key };
-      if ((res= set_bounds(scanOp, keys, multi_range_curr-ranges)))
+      if ((res= set_bounds(scanOp, active_index, false, keys,
+                           multi_range_curr-ranges)))
         DBUG_RETURN(res);
       break;
     }

--- 1.94/sql/ha_ndbcluster.h	2005-08-22 12:41:17 +02:00
+++ 1.95/sql/ha_ndbcluster.h	2005-09-15 02:31:39 +02:00
@@ -35,6 +35,7 @@
 class NdbScanFilter; 
 class NdbIndexScanOperation; 
 class NdbBlob;
+class NdbIndexStat;
 
 // connectstring to cluster if given by mysqld
 extern const char *ndbcluster_connectstring;
@@ -54,6 +55,12 @@
   void *index;
   void *unique_index;
   unsigned char *unique_index_attrid_map;
+  // In this version stats are not shared between threads
+  NdbIndexStat* index_stat;
+  uint index_stat_cache_entries;
+  // Simple counter mechanism to decide when to connect to db
+  uint index_stat_update_freq;
+  uint index_stat_query_count;
 } NDB_INDEX_DATA;
 
 typedef struct st_ndbcluster_share {
@@ -642,7 +649,8 @@
   int get_ndb_blobs_value(NdbBlob *last_ndb_blob);
   int set_primary_key(NdbOperation *op, const byte *key);
   int set_primary_key_from_record(NdbOperation *op, const byte *record);
-  int set_bounds(NdbIndexScanOperation*, const key_range *keys[2], uint= 0);
+  int set_bounds(NdbIndexScanOperation*, uint inx, bool rir,
+                 const key_range *keys[2], uint= 0);
   int key_cmp(uint keynr, const byte * old_row, const byte * new_row);
   int set_index_key(NdbOperation *, const KEY *key_info, const byte *key_ptr);
   void print_results();

--- 1.136/sql/set_var.cc	2005-09-14 13:34:07 +02:00
+++ 1.137/sql/set_var.cc	2005-09-15 02:31:40 +02:00
@@ -433,6 +433,15 @@
 sys_ndb_use_transactions("ndb_use_transactions", &SV::ndb_use_transactions);
 sys_var_long_ptr
 sys_ndb_cache_check_time("ndb_cache_check_time", &ndb_cache_check_time);
+sys_var_thd_bool
+sys_ndb_index_stat_enable("ndb_index_stat_enable",
+                          &SV::ndb_index_stat_enable);
+sys_var_thd_ulong
+sys_ndb_index_stat_cache_entries("ndb_index_stat_cache_entries",
+                                 &SV::ndb_index_stat_cache_entries);
+sys_var_thd_ulong
+sys_ndb_index_stat_update_freq("ndb_index_stat_update_freq",
+                               &SV::ndb_index_stat_update_freq);
 #endif
 
 /* Time/date/datetime formats */
@@ -725,6 +734,9 @@
   &sys_ndb_force_send,
   &sys_ndb_use_exact_count,
   &sys_ndb_use_transactions,
+  &sys_ndb_index_stat_enable,
+  &sys_ndb_index_stat_cache_entries,
+  &sys_ndb_index_stat_update_freq,
 #endif
   &sys_unique_checks,
   &sys_updatable_views_with_limit,
@@ -906,10 +918,13 @@
 #ifdef HAVE_NDBCLUSTER_DB
   {sys_ndb_autoincrement_prefetch_sz.name,
    (char*) &sys_ndb_autoincrement_prefetch_sz,                      SHOW_SYS},
+  {sys_ndb_cache_check_time.name,(char*) &sys_ndb_cache_check_time, SHOW_SYS},
   {sys_ndb_force_send.name,   (char*) &sys_ndb_force_send,          SHOW_SYS},
+  {sys_ndb_index_stat_cache_entries.name, (char*) &sys_ndb_index_stat_cache_entries, SHOW_SYS},
+  {sys_ndb_index_stat_enable.name, (char*) &sys_ndb_index_stat_enable, SHOW_SYS},
+  {sys_ndb_index_stat_update_freq.name, (char*) &sys_ndb_index_stat_update_freq, SHOW_SYS},
   {sys_ndb_use_exact_count.name,(char*) &sys_ndb_use_exact_count,   SHOW_SYS},
   {sys_ndb_use_transactions.name,(char*) &sys_ndb_use_transactions, SHOW_SYS},
-  {sys_ndb_cache_check_time.name,(char*) &sys_ndb_cache_check_time, SHOW_SYS},
 #endif
   {sys_net_buffer_length.name,(char*) &sys_net_buffer_length,       SHOW_SYS},
   {sys_net_read_timeout.name, (char*) &sys_net_read_timeout,        SHOW_SYS},

--- 1.15/mysql-test/r/ndb_condition_pushdown.result	2005-07-31 21:33:20 +02:00
+++ 1.16/mysql-test/r/ndb_condition_pushdown.result	2005-09-15 02:31:39 +02:00
@@ -868,7 +868,7 @@
 date_time = '1901-01-01 01:01:01' 
 order by auto;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	ref	medium_index	medium_index	3	const	10	Using where with pushed condition; Using filesort
+1	SIMPLE	t1	ref	medium_index	medium_index	3	const	1	Using where with pushed condition; Using filesort
 select auto from t1 where 
 string = "aaaa" and 
 vstring = "aaaa" and 
@@ -925,7 +925,7 @@
 date_time != '1901-01-01 01:01:01' 
 order by auto;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	range	medium_index	medium_index	3	NULL	20	Using where with pushed condition; Using filesort
+1	SIMPLE	t1	range	medium_index	medium_index	3	NULL	3	Using where with pushed condition; Using filesort
 select auto from t1 where 
 string != "aaaa" and 
 vstring != "aaaa" and 
@@ -984,7 +984,7 @@
 date_time > '1901-01-01 01:01:01'
 order by auto;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	range	medium_index	medium_index	3	NULL	10	Using where with pushed condition; Using filesort
+1	SIMPLE	t1	range	medium_index	medium_index	3	NULL	3	Using where with pushed condition; Using filesort
 select auto from t1 where 
 string > "aaaa" and 
 vstring > "aaaa" and 
@@ -1043,7 +1043,7 @@
 date_time >= '1901-01-01 01:01:01' 
 order by auto;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	range	medium_index	medium_index	3	NULL	10	Using where with pushed condition; Using filesort
+1	SIMPLE	t1	range	medium_index	medium_index	3	NULL	4	Using where with pushed condition; Using filesort
 select auto from t1 where 
 string >= "aaaa" and 
 vstring >= "aaaa" and 
@@ -1103,7 +1103,7 @@
 date_time < '1904-04-04 04:04:04' 
 order by auto;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	range	medium_index	medium_index	3	NULL	10	Using where with pushed condition; Using filesort
+1	SIMPLE	t1	range	medium_index	medium_index	3	NULL	3	Using where with pushed condition; Using filesort
 select auto from t1 where 
 string < "dddd" and 
 vstring < "dddd" and 
@@ -1162,7 +1162,7 @@
 date_time <= '1904-04-04 04:04:04' 
 order by auto;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	range	medium_index	medium_index	3	NULL	10	Using where with pushed condition; Using filesort
+1	SIMPLE	t1	range	medium_index	medium_index	3	NULL	4	Using where with pushed condition; Using filesort
 select auto from t1 where 
 string <= "dddd" and 
 vstring <= "dddd" and 
@@ -1255,7 +1255,7 @@
 (date_time between '1901-01-01 01:01:01' and '1903-03-03 03:03:03') 
 order by auto;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	range	medium_index	medium_index	3	NULL	10	Using where with pushed condition; Using filesort
+1	SIMPLE	t1	range	medium_index	medium_index	3	NULL	3	Using where with pushed condition; Using filesort
 select auto from t1 where
 (string between "aaaa" and "cccc") and 
 (vstring between "aaaa" and "cccc") and 
@@ -1358,7 +1358,7 @@
 (date_time not between '1901-01-01 01:01:01' and '1903-03-03 03:03:03') 
 order by auto;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	range	medium_index	medium_index	3	NULL	20	Using where with pushed condition; Using filesort
+1	SIMPLE	t1	range	medium_index	medium_index	3	NULL	1	Using where with pushed condition; Using filesort
 select auto from t1 where
 (string not between "aaaa" and "cccc") and 
 (vstring not between "aaaa" and "cccc") and 
@@ -1462,7 +1462,7 @@
 date_time in('1901-01-01 01:01:01','1903-03-03 03:03:03') 
 order by auto;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	range	medium_index	medium_index	3	NULL	20	Using where with pushed condition; Using filesort
+1	SIMPLE	t1	range	medium_index	medium_index	3	NULL	2	Using where with pushed condition; Using filesort
 select auto from t1 where
 string in("aaaa","cccc") and 
 vstring in("aaaa","cccc") and 
@@ -1514,7 +1514,7 @@
 '1901-01-01 01:01:01' in(date_time) 
 order by auto;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	ref	medium_index	medium_index	3	const	10	Using where with pushed condition; Using filesort
+1	SIMPLE	t1	ref	medium_index	medium_index	3	const	1	Using where with pushed condition; Using filesort
 select auto from t1 where
 "aaaa" in(string) and 
 "aaaa" in(vstring) and 
@@ -1565,7 +1565,7 @@
 date_time not in('1901-01-01 01:01:01','1903-03-03 03:03:03') 
 order by auto;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	range	medium_index	medium_index	3	NULL	30	Using where with pushed condition; Using filesort
+1	SIMPLE	t1	range	medium_index	medium_index	3	NULL	2	Using where with pushed condition; Using filesort
 select auto from t1 where
 string not in("aaaa","cccc") and 
 vstring not in("aaaa","cccc") and 
@@ -1738,7 +1738,7 @@
 explain
 select * from t4 where attr1 < 5 and attr2 > 9223372036854775803 and attr3 != 3 order by t4.pk1;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t4	range	attr1	attr1	4	NULL	10	Using where with pushed condition; Using filesort
+1	SIMPLE	t4	range	attr1	attr1	4	NULL	5	Using where with pushed condition; Using filesort
 select * from t4 where attr1 < 5 and attr2 > 9223372036854775803 and attr3 != 3 order by t4.pk1;
 pk1	attr1	attr2	attr3	attr4
 2	2	9223372036854775804	2	c
@@ -1746,7 +1746,7 @@
 explain
 select * from t3,t4 where t4.attr1 > 1 and t4.attr2 = t3.attr2 and t4.attr3 < 5 order by t4.pk1;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t4	range	attr1	attr1	4	NULL	10	Using where with pushed condition; Using temporary; Using filesort
+1	SIMPLE	t4	range	attr1	attr1	4	NULL	4	Using where with pushed condition; Using temporary; Using filesort
 1	SIMPLE	t3	ALL	NULL	NULL	NULL	NULL	6	Using where
 select * from t3,t4 where t4.attr1 > 1 and t4.attr2 = t3.attr2 and t4.attr3 < 5 order by t4.pk1;
 pk1	attr1	attr2	attr3	attr4	pk1	attr1	attr2	attr3	attr4

--- 1.15/mysql-test/r/ndb_blob.result	2004-12-31 14:30:46 +01:00
+++ 1.16/mysql-test/r/ndb_blob.result	2005-09-15 02:31:38 +02:00
@@ -134,7 +134,7 @@
 commit;
 explain select * from t1 where c = 111;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	ref	c	c	4	const	10	
+1	SIMPLE	t1	ref	c	c	4	const	1	
 select a,length(b),substr(b,1+2*900,2),length(d),substr(d,1+3*900,3)
 from t1 where c=111;
 a	length(b)	substr(b,1+2*900,2)	length(d)	substr(d,1+3*900,3)
@@ -242,7 +242,7 @@
 commit;
 explain select * from t1 where c >= 100 order by a;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	range	c	c	4	NULL	10	Using where; Using filesort
+1	SIMPLE	t1	range	c	c	4	NULL	9	Using where; Using filesort
 select * from t1 where c >= 100 order by a;
 a	b	c	d
 1	b1	111	dd1
@@ -278,7 +278,7 @@
 commit;
 explain select * from t1 where c >= 100 order by a;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	range	c	c	4	NULL	10	Using where; Using filesort
+1	SIMPLE	t1	range	c	c	4	NULL	2	Using where; Using filesort
 select a,length(b),substr(b,1+2*900,2),length(d),substr(d,1+3*900,3)
 from t1 where c >= 100 order by a;
 a	length(b)	substr(b,1+2*900,2)	length(d)	substr(d,1+3*900,3)

--- 1.5/mysql-test/r/ndb_charset.result	2005-01-09 11:00:34 +01:00
+++ 1.6/mysql-test/r/ndb_charset.result	2005-09-15 02:31:38 +02:00
@@ -188,7 +188,7 @@
 6	AAA
 explain select * from t1 where a = 'zZz' order by p;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	ref	a	a	3	const	10	Using where; Using filesort
+1	SIMPLE	t1	const	a	NULL	NULL	NULL	1	
 select * from t1 where a = 'aAa' order by p;
 p	a
 1	aAa
@@ -225,7 +225,7 @@
 6	AAA
 explain select * from t1 where a = 'zZz' order by p;
 id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
-1	SIMPLE	t1	ref	a	a	3	const	10	Using where; Using filesort
+1	SIMPLE	t1	const	a	NULL	NULL	NULL	1	
 select * from t1 where a = 'aAa' order by p;
 p	a
 1	aAa
--- New file ---
+++ storage/ndb/include/ndbapi/NdbIndexStat.hpp	05/09/15 02:30:17
/* Copyright (C) 2003 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

#ifndef NdbIndexStat_H
#define NdbIndexStat_H

#include <ndb_global.h>
#include <NdbDictionary.hpp>
#include <NdbError.hpp>
class NdbIndexImpl;
class NdbIndexScanOperation;

/*
 * Statistics for an ordered index.
 */
class NdbIndexStat {
public:
  NdbIndexStat(const NdbDictionary::Index* index);
  ~NdbIndexStat();
  /*
   * Allocate memory for cache.  Argument is minimum number of stat
   * entries and applies to lower and upper bounds separately.  More
   * entries may fit (keys have variable size).  If not used, db is
   * contacted always.
   */
  int alloc_cache(Uint32 entries);
  /*
   * Flags for records_in_range.
   */
  enum {
    RR_UseDb = 1,       // contact db
    RR_NoUpdate = 2     // but do not update cache
  };
  /*
   * Estimate how many index records need to be scanned.  The scan
   * operation must be prepared with lock mode LM_CommittedRead and must
   * have the desired bounds set.  The routine may use local cache or
   * may contact db by executing the operation.
   *
   * If returned count is zero then db was contacted and the count is
   * exact.  Otherwise the count is approximate.  If cache is used then
   * caller must provide estimated number of table rows.  It will be
   * multiplied by a percentage obtained from the cache (result zero is
   * returned as 1).
   */
  int records_in_range(NdbDictionary::Index* index,
                       NdbIndexScanOperation* op,
                       Uint64 table_rows,
                       Uint64* count,
                       int flags);
  /*
   * Get latest error.
   */
  const NdbError& getNdbError() const;

private:
  /*
   * There are 2 areas: start keys and end keys.  An area has pointers
   * at beginning and entries at end.  Pointers are sorted by key.
   *
   * A pointer contains entry offset and also entry timestamp.  An entry
   * contains the key and percentage of rows _not_ satisfying the bound
   * i.e. less than start key or greater than end key.
   *
   * A key is an array of index key bounds.  Each has type (0-4) in
   * first word followed by data with AttributeHeader.
   *
   * Stat update comes as pair of start and end key and associated
   * percentages.  Stat query takes best match of start and end key from
   * each area separately.  Rows in range percentage is then computed by
   * excluding the two i.e. as 100 - (start key pct + end key pct).
   *
   * TODO use more compact key format
   */
  friend struct Area;
  struct Pointer {
    Uint16 m_pos;
    Uint16 m_seq;
  };
  struct Entry {
    float m_pct;
    Uint32 m_keylen;
  };
  STATIC_CONST( EntrySize = sizeof(Entry) >> 2 );
  STATIC_CONST( PointerSize = sizeof(Pointer) >> 2 );
  struct Area {
    Uint32* m_data;
    Uint32 m_offset;
    Uint32 m_free;
    Uint16 m_entries;
    Uint8 m_idir;
    Uint8 pad1;
    Pointer& get_pointer(unsigned i) const {
      return *(Pointer*)&m_data[i];
    }
    Entry& get_entry(unsigned i) const {
      return *(Entry*)&m_data[get_pointer(i).m_pos];
    }
    Uint32 get_pos(const Entry& e) const {
      return (const Uint32*)&e - m_data;
    }
    unsigned get_firstpos() const {
      return PointerSize * m_entries + m_free;
    }
  };
  const NdbIndexImpl& m_index;
  Uint32 m_areasize;
  Uint16 m_seq;
  Area m_area[2];
  Uint32* m_cache;
  NdbError m_error;
#ifdef VM_TRACE
  void stat_verify();
#endif
  int stat_cmpkey(const Area& a, const Uint32* key1, Uint32 keylen1,
                  const Uint32* key2, Uint32 keylen2);
  int stat_search(const Area& a, const Uint32* key, Uint32 keylen,
                  Uint32* idx, bool* match);
  int stat_oldest(const Area& a);
  int stat_delete(Area& a, Uint32 k);
  int stat_update(const Uint32* key1, Uint32 keylen1,
                  const Uint32* key2, Uint32 keylen2, const float pct[2]);
  int stat_select(const Uint32* key1, Uint32 keylen1,
                  const Uint32* key2, Uint32 keylen2, float pct[2]);
  void set_error(int code);
};

#endif

--- New file ---
+++ storage/ndb/src/kernel/blocks/dbtux/DbtuxStat.cpp	05/09/15 02:30:17
/* Copyright (C) 2003 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

#define DBTUX_STAT_CPP
#include "Dbtux.hpp"

void
Dbtux::execREAD_PSEUDO_REQ(Signal* signal)
{
  jamEntry();
  ScanOpPtr scanPtr;
  scanPtr.i = signal->theData[0];
  c_scanOpPool.getPtr(scanPtr);
  if (signal->theData[1] == AttributeHeader::RECORDS_IN_RANGE) {
    jam();
    statRecordsInRange(scanPtr, &signal->theData[0]);
  } else {
    ndbassert(false);
  }
}

/*
 * Estimate entries in range.  Scan is at first entry.  Search for last
 * entry i.e. start of descending scan.  Use the 2 positions to estimate
 * entries before and after the range.  Finally get entries in range by
 * subtracting from total.  Errors come from imperfectly balanced tree
 * and from uncommitted entries which differ only in tuple version.
 *
 * Returns 4 Uint32 values: 0) total entries 1) in range 2) before range
 * 3) after range.  1-3) are estimates and need not add up to 0).
 */
void
Dbtux::statRecordsInRange(ScanOpPtr scanPtr, Uint32* out)
{
  ScanOp& scan = *scanPtr.p;
  Frag& frag = *c_fragPool.getPtr(scan.m_fragPtrI);
  TreeHead& tree = frag.m_tree;
  // get first and last position
  TreePos pos1 = scan.m_scanPos;
  TreePos pos2;
  { // as in scanFirst()
    TreeHead& tree = frag.m_tree;
    setKeyAttrs(frag);
    const unsigned idir = 1;
    const ScanBound& bound = *scan.m_bound[idir];
    ScanBoundIterator iter;
    bound.first(iter);
    for (unsigned j = 0; j < bound.getSize(); j++) {
      jam();
      c_dataBuffer[j] = *iter.data;
      bound.next(iter);
    }
    searchToScan(frag, c_dataBuffer, scan.m_boundCnt[idir], true, pos2);
    // committed read (same timeslice) and range not empty
    ndbrequire(pos2.m_loc != NullTupLoc);
  }
  out[0] = frag.m_tree.m_entryCount;
  out[2] = getEntriesBeforeOrAfter(frag, pos1, 0);
  out[3] = getEntriesBeforeOrAfter(frag, pos2, 1);
  if (pos1.m_loc == pos2.m_loc) {
    ndbrequire(pos2.m_pos >= pos1.m_pos);
    out[1] = pos2.m_pos - pos1.m_pos + 1;
  } else {
    Uint32 rem = out[2] + out[3];
    if (out[0] > rem) {
      out[1] = out[0] - rem;
    } else {
      // random guess one node apart
      out[1] = tree.m_maxOccup;
    }
  }
}

/*
 * Estimate number of entries strictly before or after given position.
 * Each branch to right direction wins parent node and the subtree on
 * the other side.  Subtree entries is estimated from depth and total
 * entries by assuming that the tree is perfectly balanced.
 */
Uint32
Dbtux::getEntriesBeforeOrAfter(Frag& frag, TreePos pos, unsigned idir)
{
  NodeHandle node(frag);
  selectNode(node, pos.m_loc);
  Uint16 path[MaxTreeDepth + 1];
  unsigned depth = getPathToNode(node, path);
  ndbrequire(depth != 0 && depth <= MaxTreeDepth);
  TreeHead& tree = frag.m_tree;
  Uint32 cnt = 0;
  Uint32 tot = tree.m_entryCount;
  unsigned i = 0;
  // contribution from levels above
  while (i + 1 < depth) {
    unsigned occup2 = (path[i] >> 8);
    unsigned side = (path[i + 1] & 0xFF);
    // subtree of this node has about half the entries
    tot = tot >= occup2 ? (tot - occup2) / 2 : 0;
    // branch to other side wins parent and a subtree
    if (side != idir) {
      cnt += occup2;
      cnt += tot;
    }
    i++;
  }
  // contribution from this node
  unsigned occup = (path[i] >> 8);
  ndbrequire(pos.m_pos < occup);
  if (idir == 0) {
    if (pos.m_pos != 0)
      cnt += pos.m_pos - 1;
  } else {
    cnt += occup - (pos.m_pos + 1);
  }
  // contribution from levels below
  tot = tot >= occup ? (tot - occup) / 2 : 0;
  cnt += tot;
  return cnt;
}

/*
 * Construct path to given node.  Returns depth.  Root node has path
 * 2 and depth 1.  In general the path is 2{0,1}* where 0,1 is the side
 * (left,right branch).  In addition the occupancy of each node is
 * returned in the upper 8 bits.
 */
unsigned
Dbtux::getPathToNode(NodeHandle node, Uint16* path)
{
  TupLoc loc = node.m_loc;
  unsigned i = MaxTreeDepth;
  while (loc != NullTupLoc) {
    jam();
    selectNode(node, loc);
    path[i] = node.getSide() | (node.getOccup() << 8);
    loc = node.getLink(2);
    ndbrequire(i != 0);
    i--;
  }
  unsigned depth = MaxTreeDepth - i;
  unsigned j = 0;
  while (j < depth) {
    path[j] = path[i + 1 + j];
    j++;
  }
  path[j] = 0xFFFF; // catch bug
  return depth;
}

--- New file ---
+++ storage/ndb/src/ndbapi/NdbIndexStat.cpp	05/09/15 02:30:17
/* Copyright (C) 2003 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

#include <ndb_global.h>
#include <AttributeHeader.hpp>
#include <NdbSqlUtil.hpp>
#include <NdbIndexStat.hpp>
#include <NdbTransaction.hpp>
#include <NdbIndexScanOperation.hpp>
#include "NdbDictionaryImpl.hpp"
#include <my_sys.h>

NdbIndexStat::NdbIndexStat(const NdbDictionary::Index* index) :
  m_index(index->m_impl),
  m_cache(NULL)
{
}

NdbIndexStat::~NdbIndexStat()
{
  delete [] m_cache;
  m_cache = NULL;
}

int
NdbIndexStat::alloc_cache(Uint32 entries)
{
  delete [] m_cache;
  m_cache = NULL;
  if (entries == 0) {
    return 0;
  }
  Uint32 i;
  Uint32 keysize = 0;
  for (i = 0; i < m_index.m_columns.size(); i++) {
    NdbColumnImpl* c = m_index.m_columns[i];
    keysize += 2;       // counting extra headers
    keysize += (c->m_attrSize * c->m_arraySize + 3 ) / 4;
  }
  Uint32 areasize = entries * (PointerSize + EntrySize + keysize);
  if (areasize > (1 << 16))
    areasize = (1 << 16);
  Uint32 cachesize = 2 * areasize;
  m_cache = new Uint32 [cachesize];
  if (m_cache == NULL) {
    set_error(4000);
    return -1;
  }
  m_areasize = areasize;
  m_seq = 0;
  Uint32 idir;
  for (idir = 0; idir <= 1; idir++) {
    Area& a = m_area[idir];
    a.m_data = &m_cache[idir * areasize];
    a.m_offset = a.m_data - &m_cache[0];
    a.m_free = areasize;
    a.m_entries = 0;
    a.m_idir = idir;
    a.pad1 = 0;
  }
#ifdef VM_TRACE
  memset(&m_cache[0], 0x3f, cachesize << 2);
#endif
  return 0;
}

#ifndef VM_TRACE
#define stat_verify()
#else
void
NdbIndexStat::stat_verify()
{
  Uint32 idir;
  for (idir = 0; idir <= 1; idir++) {
    Uint32 i;
    const Area& a = m_area[idir];
    assert(a.m_offset == idir * m_areasize);
    assert(a.m_data == &m_cache[a.m_offset]);
    Uint32 pointerwords = PointerSize * a.m_entries;
    Uint32 entrywords = 0;
    for (i = 0; i < a.m_entries; i++) {
      const Pointer& p = a.get_pointer(i);
      const Entry& e = a.get_entry(i);
      assert(a.get_pos(e) == p.m_pos);
      entrywords += EntrySize + e.m_keylen;
    }
    assert(a.m_free <= m_areasize);
    assert(pointerwords + a.m_free + entrywords == m_areasize);
    Uint32 off = pointerwords + a.m_free;
    for (i = 0; i < a.m_entries; i++) {
      assert(off < m_areasize);
      const Entry& e = *(const Entry*)&a.m_data[off];
      off += EntrySize + e.m_keylen;
    }
    assert(off == m_areasize);
    for (i = 0; i < a.m_entries; i++) {
      const Entry& e = a.get_entry(i);
      const Uint32* entrykey = (const Uint32*)&e + EntrySize;
      Uint32 n = 0;
      while (n + 2 <= e.m_keylen) {
        Uint32 t = entrykey[n++];
        assert(t == 2 * idir || t == 2 * idir + 1 || t == 4);
        AttributeHeader ah = *(const AttributeHeader*)&entrykey[n++];
        n += ah.getDataSize();
      }
      assert(n == e.m_keylen);
    }
    for (i = 0; i + 1 < a.m_entries; i++) {
      const Entry& e1 = a.get_entry(i);
      const Entry& e2 = a.get_entry(i + 1);
      const Uint32* entrykey1 = (const Uint32*)&e1 + EntrySize;
      const Uint32* entrykey2 = (const Uint32*)&e2 + EntrySize;
      int ret = stat_cmpkey(a, entrykey1, e1.m_keylen, entrykey2, e2.m_keylen);
      assert(ret == -1);
    }
  }
}
#endif

// compare keys
int
NdbIndexStat::stat_cmpkey(const Area& a, const Uint32* key1, Uint32 keylen1, const Uint32* key2, Uint32 keylen2)
{
  const Uint32 idir = a.m_idir;
  const int jdir = 1 - 2 * int(idir);
  Uint32 i1 = 0, i2 = 0;
  Uint32 t1 = 4, t2 = 4; //BoundEQ
  int ret = 0;
  Uint32 k = 0;
  while (k < m_index.m_columns.size()) {
    NdbColumnImpl* c = m_index.m_columns[k];
    Uint32 n = c->m_attrSize * c->m_arraySize;
    // absence of keypart is treated specially
    bool havekp1 = (i1 + 2 <= keylen1);
    bool havekp2 = (i2 + 2 <= keylen2);
    AttributeHeader ah1;
    AttributeHeader ah2;
    if (havekp1) {
      t1 = key1[i1++];
      assert(t1 == 2 * idir || t1 == 2 * idir + 1 || t1 == 4);
      ah1 = *(const AttributeHeader*)&key1[i1++];
    }
    if (havekp2) {
      t2 = key2[i2++];
      assert(t2 == 2 * idir || t2 == 2 * idir + 1 || t2 == 4);
      ah2 = *(const AttributeHeader*)&key2[i2++];
    }
    if (havekp1) {
      if (havekp2) {
        if (! ah1.isNULL()) {
          if (! ah2.isNULL()) {
            const NdbSqlUtil::Type& sqlType = NdbSqlUtil::getType(c->m_type);
            ret = (*sqlType.m_cmp)(c->m_cs, &key1[i1], n, &key2[i2], n, true);
            if (ret != 0)
              break;
          } else {
            ret = +1;
            break;
          }
        } else if (! ah2.isNULL()) {
          ret = -1;
          break;
        }
      } else {
        ret = +jdir;
        break;
      }
    } else {
      if (havekp2) {
        ret = -jdir;
        break;
      } else {
        // no more keyparts on either side
        break;
      }
    }
    i1 += ah1.getDataSize();
    i2 += ah2.getDataSize();
    k++;
  }
  if (ret == 0) {
    // strict bound is greater as start key and less as end key
    int s1 = t1 & 1;
    int s2 = t2 & 1;
    ret = (s1 - s2) * jdir;
  }
  return ret;
}

// find first key >= given key
int
NdbIndexStat::stat_search(const Area& a, const Uint32* key, Uint32 keylen, Uint32* idx, bool* match)
{
  // points at minus/plus infinity
  int lo = -1;
  int hi = a.m_entries;
  // loop invariant: key(lo) < key < key(hi)
  while (hi - lo > 1) {
    // observe lo < j < hi
    int j = (hi + lo) / 2;
    Entry& e = a.get_entry(j);
    const Uint32* key2 = (Uint32*)&e + EntrySize;
    Uint32 keylen2 = e.m_keylen;
    int ret = stat_cmpkey(a, key, keylen, key2, keylen2);
    // observe the loop invariant if ret != 0
    if (ret < 0)
      hi = j;
    else if (ret > 0)
      lo = j;
    else {
      *idx = j;
      *match = true;
      return 0;
    }
  }
  // hi - lo == 1 and key(lo) < key < key(hi)
  *idx = hi;
  *match = false;
  return 0;
}

// find oldest entry
int
NdbIndexStat::stat_oldest(const Area& a)
{
  Uint32 i, k, m;
  bool found = false;
  for (i = 0; i < a.m_entries; i++) {
    Pointer& p = a.get_pointer(i);
    Entry& e = a.get_entry(i);
    Uint32 m2 = m_seq >= p.m_seq ? m_seq - p.m_seq : p.m_seq - m_seq;
    if (! found || m < m2) {
      m = m2;
      k = i;
      found = true;
    }
  }
  assert(found);
  return k;
}

// delete entry
int
NdbIndexStat::stat_delete(Area& a, Uint32 k)
{
  Uint32 i;
  NdbIndexStat::Entry& e = a.get_entry(k);
  Uint32 entrylen = EntrySize + e.m_keylen;
  Uint32 pos = a.get_pos(e);
  // adjust pointers to entries after
  for (i = 0; i < a.m_entries; i++) {
    Pointer& p = a.get_pointer(i);
    if (p.m_pos < pos) {
      p.m_pos += entrylen;
    }
  }
  // compact entry area
  unsigned firstpos = a.get_firstpos();
  for (i = pos; i > firstpos; i--) {
    a.m_data[i + entrylen - 1] = a.m_data[i - 1];
  }
  // compact pointer area
  for (i = k; i + 1 < a.m_entries; i++) {
    NdbIndexStat::Pointer& p = a.get_pointer(i);
    NdbIndexStat::Pointer& q = a.get_pointer(i + 1);
    p = q;
  }
  a.m_free += PointerSize + entrylen;
  a.m_entries--;
  stat_verify();
  return 0;
}

// update or insert stat values
int
NdbIndexStat::stat_update(const Uint32* key1, Uint32 keylen1, const Uint32* key2, Uint32 keylen2, const float pct[2])
{
  const Uint32* const key[2] = { key1, key2 };
  const Uint32 keylen[2] = { keylen1, keylen2 };
  Uint32 idir;
  for (idir = 0; idir <= 1; idir++) {
    Area& a = m_area[idir];
    Uint32 k;
    bool match;
    stat_search(a, key[idir], keylen[idir], &k, &match);
    Uint16 seq = m_seq++;
    if (match) {
      // update old entry
      NdbIndexStat::Pointer& p = a.get_pointer(k);
      NdbIndexStat::Entry& e = a.get_entry(k);
      e.m_pct = pct[idir];
      p.m_seq = seq;
    } else {
      Uint32 entrylen = NdbIndexStat::EntrySize + keylen[idir];
      Uint32 need = NdbIndexStat::PointerSize + entrylen;
      while (need > a.m_free) {
        Uint32 j = stat_oldest(a);
        if (j < k)
          k--;
        stat_delete(a, j);
      }
      // insert pointer
      Uint32 i;
      for (i = a.m_entries; i > k; i--) {
        NdbIndexStat::Pointer& p1 = a.get_pointer(i);
        NdbIndexStat::Pointer& p2 = a.get_pointer(i - 1);
        p1 = p2;
      }
      NdbIndexStat::Pointer& p = a.get_pointer(k);
      // insert entry
      Uint32 firstpos = a.get_firstpos();
      p.m_pos = firstpos - entrylen;
      NdbIndexStat::Entry& e = a.get_entry(k);
      e.m_pct = pct[idir];
      e.m_keylen = keylen[idir];
      Uint32* entrykey = (Uint32*)&e + EntrySize;
      for (i = 0; i < keylen[idir]; i++) {
        entrykey[i] = key[idir][i];
      }
      p.m_seq = seq;
      // total
      a.m_free -= PointerSize + entrylen;
      a.m_entries++;
    }
  }
  stat_verify();
  return 0;
}

int
NdbIndexStat::stat_select(const Uint32* key1, Uint32 keylen1, const Uint32* key2, Uint32 keylen2, float pct[2])
{
  const Uint32* const key[2] = { key1, key2 };
  const Uint32 keylen[2] = { keylen1, keylen2 };
  Uint32 idir;
  for (idir = 0; idir <= 1; idir++) {
    Area& a = m_area[idir];
    Uint32 k;
    bool match;
    stat_search(a, key[idir], keylen[idir], &k, &match);
    if (match) {
      NdbIndexStat::Entry& e = a.get_entry(k);
      pct[idir] = e.m_pct;
    } else if (k == 0) {
      NdbIndexStat::Entry& e = a.get_entry(k);
      if (idir == 0)
        pct[idir] = e.m_pct / 2;
      else
        pct[idir] = e.m_pct + (1 - e.m_pct) / 2;
    } else if (k == a.m_entries) {
      NdbIndexStat::Entry& e = a.get_entry(k - 1);
      if (idir == 0)
        pct[idir] = e.m_pct + (1 - e.m_pct) / 2;
      else
        pct[idir] = e.m_pct / 2;
    } else {
      NdbIndexStat::Entry& e1 = a.get_entry(k - 1);
      NdbIndexStat::Entry& e2 = a.get_entry(k);
      pct[idir] = (e1.m_pct + e2.m_pct) / 2;
    }
  }
  return 0;
}

int
NdbIndexStat::records_in_range(NdbDictionary::Index* index, NdbIndexScanOperation* op, Uint64 table_rows, Uint64* count, int flags)
{
  DBUG_ENTER("NdbIndexStat::records_in_range");
  Uint64 rows;
  Uint32 key1[1000], keylen1;
  Uint32 key2[1000], keylen2;

  if (m_cache == NULL)
    flags |= RR_UseDb | RR_NoUpdate;
  else if (m_area[0].m_entries == 0 || m_area[1].m_entries == 0)
    flags |= RR_UseDb;

  if ((flags & (RR_UseDb | RR_NoUpdate)) != RR_UseDb | RR_NoUpdate) {
    // get start and end key - assume bound is ordered, wellformed
    Uint32 bound[1000];
    Uint32 boundlen = op->getKeyFromSCANTABREQ(bound, 1000);

    keylen1 = keylen2 = 0;
    Uint32 n = 0;
    while (n < boundlen) {
      Uint32 t = bound[n];
      AttributeHeader ah(bound[n + 1]);
      Uint32 sz = 2 + ah.getDataSize();
      t &= 0xFFFF;      // may contain length
      assert(t <= 4);
      bound[n] = t;
      if (t == 0 || t == 1 || t == 4) {
        memcpy(&key1[keylen1], &bound[n], sz << 2);
        keylen1 += sz;
      }
      if (t == 2 || t == 3 || t == 4) {
        memcpy(&key2[keylen2], &bound[n], sz << 2);
        keylen2 += sz;
      }
      n += sz;
    }
  }

  if (flags & RR_UseDb) {
    Uint32 out[4] = { 0, 0, 0, 0 };  // rows, in, before, after
    float tot[4] = { 0, 0, 0, 0 };   // totals of above
    int cnt, ret;
    bool forceSend = true;
    NdbTransaction* trans = op->m_transConnection;
    if (op->interpret_exit_last_row() == -1 ||
        op->getValue(NdbDictionary::Column::RECORDS_IN_RANGE, (char*)out) == 0) {
      DBUG_PRINT("error", ("op:%d", op->getNdbError().code));
      DBUG_RETURN(-1);
    }
    if (trans->execute(NdbTransaction::NoCommit,
                       NdbTransaction::AbortOnError, forceSend) == -1) {
      DBUG_PRINT("error", ("trans:%d op:%d", trans->getNdbError().code,
                           op->getNdbError().code));
      DBUG_RETURN(-1);
    }
    cnt = 0;
    while ((ret = op->nextResult(true, forceSend)) == 0) {
      DBUG_PRINT("info", ("frag rows=%u in=%u before=%u after=%u [error=%d]",
                          out[0], out[1], out[2], out[3],
                          (int)(out[1] + out[2] + out[3]) - (int)out[0]));
      unsigned i;
      for (i = 0; i < 4; i++)
        tot[i] += (float)out[i];
      cnt++;
    }
    if (ret == -1) {
      DBUG_PRINT("error", ("trans:%d op:%d", trans->getNdbError().code,
                           op->getNdbError().code));
      DBUG_RETURN(-1);
    }
    op->close(forceSend);
    rows = (Uint64)tot[1];
    if (cnt != 0 && ! (flags & RR_NoUpdate)) {
      float pct[2];
      pct[0] = 100 * tot[2] / tot[0];
      pct[1] = 100 * tot[3] / tot[0];
      DBUG_PRINT("info", ("update stat pct"
                          " before=%.2f after=%.2f",
                          pct[0], pct[1]));
      stat_update(key1, keylen1, key2, keylen2, pct);
    }
  } else {
      float pct[2];
      stat_select(key1, keylen1, key2, keylen2, pct);
      float diff = 100.0 - (pct[0] + pct[1]);
      float trows = (float)table_rows;
      DBUG_PRINT("info", ("select stat pct"
                          " before=%.2f after=%.2f in=%.2f table_rows=%.2f",
                          pct[0], pct[1], diff, trows));
      rows = 0;
      if (diff >= 0)
        rows = (Uint64)(diff * trows / 100);
      if (rows == 0)
        rows = 1;
  }

  *count = rows;
  DBUG_PRINT("value", ("rows=%llu flags=%o", rows, flags));
  DBUG_RETURN(0);
}

void
NdbIndexStat::set_error(int code)
{
  m_error.code = code;
}

const NdbError&
NdbIndexStat::getNdbError() const
{
  return m_error;
}


--- 1.7/storage/ndb/src/kernel/blocks/dbtux/Makefile.am	2005-04-27 04:04:32 +02:00
+++ 1.8/storage/ndb/src/kernel/blocks/dbtux/Makefile.am	2005-09-15 02:31:41 +02:00
@@ -9,6 +9,7 @@
 			DbtuxScan.cpp \
 			DbtuxSearch.cpp \
 			DbtuxCmp.cpp \
+			DbtuxStat.cpp \
 			DbtuxDebug.cpp
 
 INCLUDES_LOC = -I$(top_srcdir)/storage/ndb/src/kernel/blocks/dbtup

--- 1.15/storage/ndb/src/ndbapi/Makefile.am	2005-09-09 15:26:43 +02:00
+++ 1.16/storage/ndb/src/ndbapi/Makefile.am	2005-09-15 02:31:41 +02:00
@@ -35,6 +35,7 @@
         DictCache.cpp \
         ndb_cluster_connection.cpp \
 	NdbBlob.cpp \
+	NdbIndexStat.cpp \
         SignalSender.cpp
 
 INCLUDES_LOC = -I$(top_srcdir)/storage/ndb/src/mgmapi
Thread
bk commit into 5.1 tree (pekka:1.1893)pekka15 Sep