List:Commits« Previous MessageNext Message »
From:Jorgen Loland Date:January 26 2012 1:10pm
Subject:bzr push into mysql-trunk branch (jorgen.loland:3530 to 3531) WL#5957
View as plain text  
 3531 Jorgen Loland	2012-01-26
      WL#5957: Range optimizer: use statistics instead of index 
               dives if there are many ranges
      
      For queries of the type
      
      SELECT * FROM t1 WHERE a IN(<list of values>)
      
      the range optimizer will estimate the cost of reading rows 
      that qualify. There are two possibilities: 
       1) If there is a unique index on 'a', the row estimate is 1
       2) Otherwise, two dives into the index, one at range start 
          and one at range end, are done and the number of rows in 
          the range is used as estimate.
      
      However, when there are a great number of values in IN(), the
      time used to estimate the number of rows may easily exceed the
      time it takes to execute the query. This WL is for reducing 
      query optimization time by replacing one index inspection per 
      range with already calculated index statistics for equality 
      ranges. 
      
      Since the index statistics are faster in use but less accurate 
      than inspecting the index, different users may have different 
      criteria for when to use which. A SESSION variable 
      (eq-range-index-dive-limit) is therefore added as well: If 
      the number of equality ranges is equal to or higher than this
      value, index statistics is used for the equality ranges. The 
      default is that index statistics is used if there are 10 or 
      more equality ranges.
     @ include/my_base.h
        Document HA_READ_MBR*
        Make enumeration out of key range flags
        Add key range flag USE_INDEX_STATISTICS
     @ mysql-test/r/mysqld--help-notwin.result
        eq-range-index-dive-limit variable has been added
     @ mysql-test/r/mysqld--help-win.result
        eq-range-index-dive-limit variable has been added
     @ mysql-test/suite/opt_trace/r/bugs_no_prot_all.result
        Update opt trace output with new info from WL5957
     @ mysql-test/suite/opt_trace/r/bugs_no_prot_none.result
        Update opt trace output with new info from WL5957
     @ mysql-test/suite/opt_trace/r/charset.result
        Update opt trace output with new info from WL5957
     @ mysql-test/suite/opt_trace/r/eq_range_statistics.result
        Tests for WL5957
     @ mysql-test/suite/opt_trace/r/general2_no_prot.result
        Update opt trace output with new info from WL5957
     @ mysql-test/suite/opt_trace/r/general_no_prot_all.result
        Update opt trace output with new info from WL5957
     @ mysql-test/suite/opt_trace/r/general_no_prot_none.result
        Update opt trace output with new info from WL5957
     @ mysql-test/suite/opt_trace/r/range_no_prot.result
        Update opt trace output with new info from WL5957
     @ mysql-test/suite/opt_trace/r/subquery_no_prot.result
        Update opt trace output with new info from WL5957
     @ mysql-test/suite/opt_trace/t/eq_range_statistics.test
        Tests for WL5957
     @ mysql-test/suite/sys_vars/r/eq_range_index_dive_limit_basic.result
        Tests for WL5957
     @ mysql-test/suite/sys_vars/t/eq_range_index_dive_limit_basic.test
        Tests for WL5957
     @ sql/handler.cc
        handler::multi_range_read_info_const() will use existing index
        statistics instead of index dives for equality ranges if the
        optimizer has specified to use index statistics.
     @ sql/opt_range.cc
        Update lots of documentation
        Add SEL_ARG::use_index_statistics, set to true if the user 
        requested the use of index statistics instead of index dives
        when estimating the number of rows in the equality ranges.
        Add function eq_ranges_exceeds_limit(): check if there are
        equal or more equality range than specified by 
        SESSION variable eq-range-index-dive-limit.
        sel_arg_range_seq_next(): Add EQ_RANGE range flag to equality ranges, 
        add USE_INDEX_STATISTICS flag to equality ranges if index statistics is
        requested by the user. Also simplify logic by calling helper functions
        and documenting the function.
        Rename step_down_to() => push_range_to_stack()
     @ sql/sql_class.h
        Add session variable eq_range_index_dive_limit
     @ sql/sys_vars.cc
        Add session variable eq_range_index_dive_limit

    added:
      mysql-test/suite/opt_trace/r/eq_range_statistics.result
      mysql-test/suite/opt_trace/t/eq_range_statistics.test
      mysql-test/suite/sys_vars/r/eq_range_index_dive_limit_basic.result
      mysql-test/suite/sys_vars/t/eq_range_index_dive_limit_basic.test
    modified:
      include/my_base.h
      mysql-test/r/mysqld--help-notwin.result
      mysql-test/r/mysqld--help-win.result
      mysql-test/suite/opt_trace/r/bugs_no_prot_all.result
      mysql-test/suite/opt_trace/r/bugs_no_prot_none.result
      mysql-test/suite/opt_trace/r/charset.result
      mysql-test/suite/opt_trace/r/general2_no_prot.result
      mysql-test/suite/opt_trace/r/general_no_prot_all.result
      mysql-test/suite/opt_trace/r/general_no_prot_none.result
      mysql-test/suite/opt_trace/r/range_no_prot.result
      mysql-test/suite/opt_trace/r/subquery_no_prot.result
      sql/handler.cc
      sql/opt_range.cc
      sql/sql_class.h
      sql/sys_vars.cc
 3530 Roy Lyseng	2012-01-20
      Bug#13596176: Missing row on select with nested in clause when matr=on
                    and bnl=off + MyISAM
      
      This is a case where Materialize-scan semi-join strategy is selected,
      and ref access is used to look up a row in the outer table.
      The referenced field in the outer table belongs in a multiple equality
      together with three fields from the inner tables.
      
      setup_sj_materialization() sets up objects to copy fields from the
      materialized tables back to original field objects. For a multiple
      equality, a "best" field is picked for this, the remaining fields
      are not copied. This best field coincides with fields used by regular
      conditions, but not necessarily with fields used for ref access.
      
      The reason for this is that before fields are substituted within
      regular conditions (see substitute_for_best_equal_field()), multiple
      equality objects are sorted so that fields come in table order.
      This sorting has not been done when calling set_access_methods(),
      which sets up ref access.
      
      The solution to the problem is to execute set_access_methods() after
      the multiple equalities have been sorted, and use the same algorithm
      as setup_sj_materialization() uses to pick the proper field for
      ref access.
      
      A pleasant side effect of this fix is also that
      make_cond_for_table_from_pred() now does a better job in replacing
      equality predicates that are also covered by ref accesses.
      
      mysql-test/include/subquery_sj.inc
        Added test case for bug#13596176.
      
      mysql-test/r/derived.result
      mysql-test/r/greedy_optimizer.result
      mysql-test/r/join_cache_bka.result
      mysql-test/r/join_cache_bka_nixbnl.result
      mysql-test/r/join_cache_bkaunique.result
      mysql-test/r/join_cache_bnl.result
      mysql-test/r/join_cache_nojb.result
      mysql-test/r/join_nested.result
      mysql-test/r/join_nested_bka.result
      mysql-test/r/join_nested_bka_nixbnl.result
      mysql-test/suite/opt_trace/r/general2_no_prot.result
      mysql-test/suite/opt_trace/r/general2_ps_prot.result
        Updated plans. Mostly these are removed "Using where" and
        "Using index condition", which means that a condition has been
        correctly identified as ref access and removed.
      
      mysql-test/r/subquery_sj_all.result
      mysql-test/r/subquery_sj_all_bka.result
      mysql-test/r/subquery_sj_all_bka_nixbnl.result
      mysql-test/r/subquery_sj_all_bkaunique.result
      mysql-test/r/subquery_sj_dupsweed.result
      mysql-test/r/subquery_sj_dupsweed_bka.result
      mysql-test/r/subquery_sj_dupsweed_bka_nixbnl.result
      mysql-test/r/subquery_sj_dupsweed_bkaunique.result
      mysql-test/r/subquery_sj_firstmatch.result
      mysql-test/r/subquery_sj_firstmatch_bka.result
      mysql-test/r/subquery_sj_firstmatch_bka_nixbnl.result
      mysql-test/r/subquery_sj_firstmatch_bkaunique.result
      mysql-test/r/subquery_sj_loosescan.result
      mysql-test/r/subquery_sj_loosescan_bka.result
      mysql-test/r/subquery_sj_loosescan_bka_nixbnl.result
      mysql-test/r/subquery_sj_loosescan_bkaunique.result
      mysql-test/r/subquery_sj_mat.result
      mysql-test/r/subquery_sj_mat_bka.result
      mysql-test/r/subquery_sj_mat_bka_nixbnl.result
      mysql-test/r/subquery_sj_mat_bkaunique.result
      mysql-test/r/subquery_sj_none.result
      mysql-test/r/subquery_sj_none_bka.result
      mysql-test/r/subquery_sj_none_bka_nixbnl.result
      mysql-test/r/subquery_sj_none_bkaunique.result
        Added test case results for bug#13596176, plus some updated plans.
      
      sql/sql_optimizer.cc
        Added function get_best_field() that picks the "best" field from
        a multiple equality.
        Calls set_access_methods() after multiple equalities have been sorted.
        Calls drop_unused_derived_keys() after ref access has been set up.
        Calls update_depend_map() after ref access has been set up.
        Calls initialize_tables() after ref access has been set up.
      
      sql/sql_optimizer.h
        Added definition for function get_best_field().
      
      sql/sql_select.cc
        Calls get_best_field() in create_ref_for_key() and
        setup_sj_materialization() to get the "best" field.
        Call to set_access_methods() and update_depend_map() moved to a more
        appropriate place.

    modified:
      mysql-test/include/subquery_sj.inc
      mysql-test/r/derived.result
      mysql-test/r/greedy_optimizer.result
      mysql-test/r/join_cache_bka.result
      mysql-test/r/join_cache_bka_nixbnl.result
      mysql-test/r/join_cache_bkaunique.result
      mysql-test/r/join_cache_bnl.result
      mysql-test/r/join_cache_nojb.result
      mysql-test/r/join_nested.result
      mysql-test/r/join_nested_bka.result
      mysql-test/r/join_nested_bka_nixbnl.result
      mysql-test/r/subquery_mat.result
      mysql-test/r/subquery_mat_all.result
      mysql-test/r/subquery_sj_all.result
      mysql-test/r/subquery_sj_all_bka.result
      mysql-test/r/subquery_sj_all_bka_nixbnl.result
      mysql-test/r/subquery_sj_all_bkaunique.result
      mysql-test/r/subquery_sj_dupsweed.result
      mysql-test/r/subquery_sj_dupsweed_bka.result
      mysql-test/r/subquery_sj_dupsweed_bka_nixbnl.result
      mysql-test/r/subquery_sj_dupsweed_bkaunique.result
      mysql-test/r/subquery_sj_firstmatch.result
      mysql-test/r/subquery_sj_firstmatch_bka.result
      mysql-test/r/subquery_sj_firstmatch_bka_nixbnl.result
      mysql-test/r/subquery_sj_firstmatch_bkaunique.result
      mysql-test/r/subquery_sj_loosescan.result
      mysql-test/r/subquery_sj_loosescan_bka.result
      mysql-test/r/subquery_sj_loosescan_bka_nixbnl.result
      mysql-test/r/subquery_sj_loosescan_bkaunique.result
      mysql-test/r/subquery_sj_mat.result
      mysql-test/r/subquery_sj_mat_bka.result
      mysql-test/r/subquery_sj_mat_bka_nixbnl.result
      mysql-test/r/subquery_sj_mat_bkaunique.result
      mysql-test/r/subquery_sj_mat_nosj.result
      mysql-test/r/subquery_sj_none.result
      mysql-test/r/subquery_sj_none_bka.result
      mysql-test/r/subquery_sj_none_bka_nixbnl.result
      mysql-test/r/subquery_sj_none_bkaunique.result
      mysql-test/suite/opt_trace/r/general2_no_prot.result
      mysql-test/suite/opt_trace/r/general2_ps_prot.result
      sql/sql_optimizer.cc
      sql/sql_optimizer.h
      sql/sql_select.cc
=== modified file 'include/my_base.h'
--- a/include/my_base.h	2011-10-27 04:20:52 +0000
+++ b/include/my_base.h	2012-01-26 13:09:59 +0000
@@ -78,11 +78,11 @@ enum ha_rkey_function {
   HA_READ_PREFIX,                 /* Key which as same prefix */
   HA_READ_PREFIX_LAST,            /* Last key with the same prefix */
   HA_READ_PREFIX_LAST_OR_PREV,    /* Last or prev key with the same prefix */
-  HA_READ_MBR_CONTAIN,
-  HA_READ_MBR_INTERSECT,
-  HA_READ_MBR_WITHIN,
-  HA_READ_MBR_DISJOINT,
-  HA_READ_MBR_EQUAL
+  HA_READ_MBR_CONTAIN,            /* Minimum Bounding Rectangle contains */
+  HA_READ_MBR_INTERSECT,          /* Minimum Bounding Rectangle intersect */
+  HA_READ_MBR_WITHIN,             /* Minimum Bounding Rectangle within */
+  HA_READ_MBR_DISJOINT,           /* Minimum Bounding Rectangle disjoint */
+  HA_READ_MBR_EQUAL               /* Minimum Bounding Rectangle equal */
 };
 
 	/* Key algorithm types */
@@ -522,41 +522,40 @@ enum data_file_type {
 
 /* For key ranges */
 
-/* from -inf */
-#define NO_MIN_RANGE	1
-
-/* to +inf */
-#define NO_MAX_RANGE	2
-
-/*  X < key, i.e. not including the left endpoint */
-#define NEAR_MIN	4
-
-/* X > key, i.e. not including the right endpoint */
-#define NEAR_MAX	8
-
-/* 
-  This flag means that index is a unique index, and the interval is 
-  equivalent to "AND(keypart_i = const_i)", where all of const_i are not NULLs.
-*/
-#define UNIQUE_RANGE	16
-
-/* 
-  This flag means that the interval is equivalent to 
-  "AND(keypart_i = const_i)", where not all key parts may be used but all of 
-  const_i are not NULLs.
-*/
-#define EQ_RANGE	32
-
-/*
-  This flag has the same meaning as UNIQUE_RANGE, except that for at least
-  one keypart the condition is "keypart IS NULL". 
-*/
-#define NULL_RANGE	64
-
-#define GEOM_FLAG      128
+enum key_range_flag {
+  NO_MIN_RANGE=      1 << 0,                    ///< from -inf
+  NO_MAX_RANGE=      1 << 1,                    ///< to +inf
+  /*  X < key, i.e. not including the left endpoint */
+  NEAR_MIN=          1 << 2,
+  /* X > key, i.e. not including the right endpoint */
+  NEAR_MAX=          1 << 3,
+  /*
+    This flag means that index is a unique index, and the interval is
+    equivalent to "AND(keypart_i = const_i)", where all of const_i are
+    not NULLs.
+  */
+  UNIQUE_RANGE=      1 << 4,
+  /*
+    This flag means that the interval is equivalent to 
+    "AND(keypart_i = const_i)", where not all key parts may be used 
+    but all of const_i are not NULLs.
+  */
+  EQ_RANGE=          1 << 5,
+  /*
+    This flag has the same meaning as UNIQUE_RANGE, except that for at
+    least one keypart the condition is "keypart IS NULL".
+  */
+  NULL_RANGE=        1 << 6,
+  GEOM_FLAG=         1 << 7,                     ///< GIS
+  /* Deprecated, currently used only by NDB at row retrieval */
+  SKIP_RANGE=        1 << 8,
+  /* 
+    Used together with EQ_RANGE to indicate that index statistics
+    should be used instead of sampling the index.
+  */
+  USE_INDEX_STATISTICS= 1 << 9
+};
 
-/* Deprecated, currently used only by NDB at row retrieval */
-#define SKIP_RANGE     256
 
 typedef struct st_key_range
 {

=== modified file 'mysql-test/r/mysqld--help-notwin.result'
--- a/mysql-test/r/mysqld--help-notwin.result	2012-01-18 13:53:00 +0000
+++ b/mysql-test/r/mysqld--help-notwin.result	2012-01-26 13:09:59 +0000
@@ -151,6 +151,10 @@ The following options may be given as th
  --div-precision-increment=# 
  Precision of the result of '/' operator will be increased
  on that value
+ --eq-range-index-dive-limit=# 
+ The optimizer will use existing index statistics instead
+ of doing index dives for equality ranges if the number of
+ equality ranges is larger than or equal to this number.
  --event-scheduler[=name] 
  Enable the event scheduler. Possible values are ON, OFF,
  and DISABLED (keep the event scheduler completely
@@ -902,6 +906,7 @@ delayed-insert-timeout 300
 delayed-queue-size 1000
 disconnect-slave-event-count 0
 div-precision-increment 4
+eq-range-index-dive-limit 10
 event-scheduler OFF
 expire-logs-days 0
 external-locking FALSE

=== modified file 'mysql-test/r/mysqld--help-win.result'
--- a/mysql-test/r/mysqld--help-win.result	2012-01-18 13:53:00 +0000
+++ b/mysql-test/r/mysqld--help-win.result	2012-01-26 13:09:59 +0000
@@ -151,6 +151,10 @@ The following options may be given as th
  --div-precision-increment=# 
  Precision of the result of '/' operator will be increased
  on that value
+ --eq-range-index-dive-limit=# 
+ The optimizer will use existing index statistics instead
+ of doing index dives for equality ranges if the number of
+ equality ranges is larger than or equal to this number.
  --event-scheduler[=name] 
  Enable the event scheduler. Possible values are ON, OFF,
  and DISABLED (keep the event scheduler completely
@@ -910,6 +914,7 @@ delayed-insert-timeout 300
 delayed-queue-size 1000
 disconnect-slave-event-count 0
 div-precision-increment 4
+eq-range-index-dive-limit 10
 event-scheduler OFF
 expire-logs-days 0
 external-locking FALSE

=== modified file 'mysql-test/suite/opt_trace/r/bugs_no_prot_all.result'
--- a/mysql-test/suite/opt_trace/r/bugs_no_prot_all.result	2012-01-18 13:53:00 +0000
+++ b/mysql-test/suite/opt_trace/r/bugs_no_prot_all.result	2012-01-26 13:09:59 +0000
@@ -2178,6 +2178,7 @@ SELECT 1 FROM t1 WHERE 1 LIKE
                                 "range_scan_alternatives": [
                                   {
                                     "index": "a",
+                                    "index_dives_for_eq_ranges": true,
                                     "ranges": [
                                       "1 <= a <= 1"
                                     ] /* ranges */,
@@ -2190,6 +2191,7 @@ SELECT 1 FROM t1 WHERE 1 LIKE
                                   },
                                   {
                                     "index": "a_2",
+                                    "index_dives_for_eq_ranges": true,
                                     "ranges": [
                                       "1 <= a <= 1"
                                     ] /* ranges */,

=== modified file 'mysql-test/suite/opt_trace/r/bugs_no_prot_none.result'
--- a/mysql-test/suite/opt_trace/r/bugs_no_prot_none.result	2011-11-29 08:41:50 +0000
+++ b/mysql-test/suite/opt_trace/r/bugs_no_prot_none.result	2012-01-26 13:09:59 +0000
@@ -1679,6 +1679,7 @@ SELECT 1 FROM t1 WHERE 1 LIKE
                                 "range_scan_alternatives": [
                                   {
                                     "index": "a",
+                                    "index_dives_for_eq_ranges": true,
                                     "ranges": [
                                       "1 <= a <= 1"
                                     ] /* ranges */,
@@ -1691,6 +1692,7 @@ SELECT 1 FROM t1 WHERE 1 LIKE
                                   },
                                   {
                                     "index": "a_2",
+                                    "index_dives_for_eq_ranges": true,
                                     "ranges": [
                                       "1 <= a <= 1"
                                     ] /* ranges */,

=== modified file 'mysql-test/suite/opt_trace/r/charset.result'
--- a/mysql-test/suite/opt_trace/r/charset.result	2011-11-18 14:51:40 +0000
+++ b/mysql-test/suite/opt_trace/r/charset.result	2012-01-26 13:09:59 +0000
@@ -425,6 +425,7 @@ select * from t1 where c < 'ÁÂÃÄÅ'	
                     "range_scan_alternatives": [
                       {
                         "index": "PRIMARY",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "c <= ÁÂÃÄ"
                         ] /* ranges */,
@@ -628,6 +629,7 @@ select `col
                     "range_scan_alternatives": [
                       {
                         "index": "index\n1\ta",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "NULL < col\n1\ta < 6"
                         ] /* ranges */,

=== added file 'mysql-test/suite/opt_trace/r/eq_range_statistics.result'
--- a/mysql-test/suite/opt_trace/r/eq_range_statistics.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/opt_trace/r/eq_range_statistics.result	2012-01-26 13:09:59 +0000
@@ -0,0 +1,1244 @@
+SET optimizer_trace_max_mem_size=1048576;
+SET optimizer_trace="enabled=on,end_marker=on,one_line=off";
+SET eq_range_index_dive_limit=default;
+SELECT @@eq_range_index_dive_limit;
+@@eq_range_index_dive_limit
+10
+CREATE TABLE t1 (
+a INT, 
+b INT, 
+KEY (a,b)
+);
+INSERT INTO t1 VALUES (1,1), (2,2), (3,3);
+INSERT INTO t1 VALUES (4,1), (4,2), (4,3);
+INSERT INTO t1 VALUES (5,1), (5,2), (5,3);
+SHOW INDEX FROM t1;
+Table	Non_unique	Key_name	Seq_in_index	Column_name	Collation	Cardinality	Sub_part	Packed	Null	Index_type	Comment	Index_comment
+t1	1	a	1	a	A	NULL	NULL	NULL	YES	BTREE		
+t1	1	a	2	b	A	NULL	NULL	NULL	YES	BTREE		
+ANALYZE TABLE t1;
+Table	Op	Msg_type	Msg_text
+test.t1	analyze	status	OK
+SHOW INDEX FROM t1;
+Table	Non_unique	Key_name	Seq_in_index	Column_name	Collation	Cardinality	Sub_part	Packed	Null	Index_type	Comment	Index_comment
+t1	1	a	1	a	A	4	NULL	NULL	YES	BTREE		
+t1	1	a	2	b	A	9	NULL	NULL	YES	BTREE		
+#####
+# Apply knowledge about the statistics (each index value for 
+# the first key part has an estimate of 2 rows) to ensure that 
+# index statistics kicks in correctly.
+#####
+# Index dives are done, giving correct estimate of 3 records
+EXPLAIN SELECT * FROM t1 WHERE a IN (1,2,3);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	range	a	a	5	NULL	3	Using where; Using index
+SET eq_range_index_dive_limit=3;
+SELECT @@eq_range_index_dive_limit;
+@@eq_range_index_dive_limit
+3
+# Index statistics kicks in, giving incorrect estimate of 3x2=6 records
+EXPLAIN SELECT * FROM t1 WHERE a IN (1,2,3);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	range	a	a	5	NULL	6	Using where; Using index
+#####
+# Below: A number of tests to verify that the number of equality ranges
+# are counted correctly
+#####
+
+# 2 equality ranges: should not use index statistics
+EXPLAIN SELECT * FROM t1 WHERE a=5 OR a>10 OR a IN (1);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	range	a	a	5	NULL	5	Using where; Using index
+SELECT * FROM information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+EXPLAIN SELECT * FROM t1 WHERE a=5 OR a>10 OR a IN (1)	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where ((`test`.`t1`.`a` = 5) or (`test`.`t1`.`a` > 10) or (`test`.`t1`.`a` = 1))"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+          {
+            "condition_processing": {
+              "condition": "WHERE",
+              "original_condition": "((`test`.`t1`.`a` = 5) or (`test`.`t1`.`a` > 10) or (`test`.`t1`.`a` = 1))",
+              "steps": [
+                {
+                  "transformation": "equality_propagation",
+                  "resulting_condition": "(multiple equal(5, `test`.`t1`.`a`) or (`test`.`t1`.`a` > 10) or multiple equal(1, `test`.`t1`.`a`))"
+                },
+                {
+                  "transformation": "constant_propagation",
+                  "resulting_condition": "(multiple equal(5, `test`.`t1`.`a`) or (`test`.`t1`.`a` > 10) or multiple equal(1, `test`.`t1`.`a`))"
+                },
+                {
+                  "transformation": "trivial_condition_removal",
+                  "resulting_condition": "(multiple equal(5, `test`.`t1`.`a`) or (`test`.`t1`.`a` > 10) or multiple equal(1, `test`.`t1`.`a`))"
+                }
+              ] /* steps */
+            } /* condition_processing */
+          },
+          {
+            "table_dependencies": [
+              {
+                "database": "test",
+                "table": "t1",
+                "row_may_be_null": false,
+                "map_bit": 0,
+                "depends_on_map_bits": [
+                ] /* depends_on_map_bits */
+              }
+            ] /* table_dependencies */
+          },
+          {
+            "ref_optimizer_key_uses": [
+            ] /* ref_optimizer_key_uses */
+          },
+          {
+            "rows_estimation": [
+              {
+                "database": "test",
+                "table": "t1",
+                "range_analysis": {
+                  "table_scan": {
+                    "rows": 9,
+                    "cost": 5.9198
+                  } /* table_scan */,
+                  "potential_range_indices": [
+                    {
+                      "index": "a",
+                      "usable": true,
+                      "key_parts": [
+                        "a",
+                        "b"
+                      ] /* key_parts */
+                    }
+                  ] /* potential_range_indices */,
+                  "best_covering_index_scan": {
+                    "index": "a",
+                    "cost": 3.0581,
+                    "chosen": true
+                  } /* best_covering_index_scan */,
+                  "setup_range_conditions": [
+                  ] /* setup_range_conditions */,
+                  "group_index_range": {
+                    "chosen": false,
+                    "cause": "not_group_by_or_distinct"
+                  } /* group_index_range */,
+                  "analyzing_range_alternatives": {
+                    "range_scan_alternatives": [
+                      {
+                        "index": "a",
+                        "index_dives_for_eq_ranges": true,
+                        "ranges": [
+                          "1 <= a <= 1",
+                          "5 <= a <= 5",
+                          "10 < a"
+                        ] /* ranges */,
+                        "index_only": true,
+                        "rows": 5,
+                        "cost": 2.139,
+                        "rowid_ordered": false,
+                        "chosen": true
+                      }
+                    ] /* range_scan_alternatives */,
+                    "analyzing_roworder_intersect": {
+                      "usable": false,
+                      "cause": "too_few_roworder_scans"
+                    } /* analyzing_roworder_intersect */
+                  } /* analyzing_range_alternatives */,
+                  "chosen_range_access_summary": {
+                    "range_access_plan": {
+                      "type": "range_scan",
+                      "index": "a",
+                      "rows": 5,
+                      "ranges": [
+                        "1 <= a <= 1",
+                        "5 <= a <= 5",
+                        "10 < a"
+                      ] /* ranges */
+                    } /* range_access_plan */,
+                    "rows_for_plan": 5,
+                    "cost_for_plan": 2.139,
+                    "chosen": true
+                  } /* chosen_range_access_summary */
+                } /* range_analysis */
+              }
+            ] /* rows_estimation */
+          },
+          {
+            "considered_execution_plans": [
+              {
+                "database": "test",
+                "table": "t1",
+                "best_access_path": {
+                  "considered_access_paths": [
+                    {
+                      "access_type": "range",
+                      "rows": 5,
+                      "cost": 2.139,
+                      "chosen": true
+                    }
+                  ] /* considered_access_paths */
+                } /* best_access_path */,
+                "cost_for_plan": 3.139,
+                "rows_for_plan": 5,
+                "chosen": true
+              }
+            ] /* considered_execution_plans */
+          },
+          {
+            "attaching_conditions_to_tables": {
+              "original_condition": "((`test`.`t1`.`a` = 5) or (`test`.`t1`.`a` > 10) or (`test`.`t1`.`a` = 1))",
+              "attached_conditions_computation": [
+              ] /* attached_conditions_computation */,
+              "attached_conditions_summary": [
+                {
+                  "database": "test",
+                  "table": "t1",
+                  "attached": "((`test`.`t1`.`a` = 5) or (`test`.`t1`.`a` > 10) or (`test`.`t1`.`a` = 1))"
+                }
+              ] /* attached_conditions_summary */
+            } /* attaching_conditions_to_tables */
+          },
+          {
+            "refine_plan": [
+              {
+                "database": "test",
+                "table": "t1",
+                "access_type": "range"
+              }
+            ] /* refine_plan */
+          }
+        ] /* steps */
+      } /* join_optimization */
+    },
+    {
+      "join_explain": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_explain */
+    }
+  ] /* steps */
+}	0	0
+
+# 3 equality ranges: should use index statistics
+EXPLAIN SELECT * FROM t1 WHERE a=5 OR a>10 OR a IN (1,2);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	range	a	a	5	NULL	7	Using where; Using index
+SELECT * FROM information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+EXPLAIN SELECT * FROM t1 WHERE a=5 OR a>10 OR a IN (1,2)	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where ((`test`.`t1`.`a` = 5) or (`test`.`t1`.`a` > 10) or (`test`.`t1`.`a` in (1,2)))"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+          {
+            "condition_processing": {
+              "condition": "WHERE",
+              "original_condition": "((`test`.`t1`.`a` = 5) or (`test`.`t1`.`a` > 10) or (`test`.`t1`.`a` in (1,2)))",
+              "steps": [
+                {
+                  "transformation": "equality_propagation",
+                  "resulting_condition": "(multiple equal(5, `test`.`t1`.`a`) or (`test`.`t1`.`a` > 10) or (`test`.`t1`.`a` in (1,2)))"
+                },
+                {
+                  "transformation": "constant_propagation",
+                  "resulting_condition": "(multiple equal(5, `test`.`t1`.`a`) or (`test`.`t1`.`a` > 10) or (`test`.`t1`.`a` in (1,2)))"
+                },
+                {
+                  "transformation": "trivial_condition_removal",
+                  "resulting_condition": "(multiple equal(5, `test`.`t1`.`a`) or (`test`.`t1`.`a` > 10) or (`test`.`t1`.`a` in (1,2)))"
+                }
+              ] /* steps */
+            } /* condition_processing */
+          },
+          {
+            "table_dependencies": [
+              {
+                "database": "test",
+                "table": "t1",
+                "row_may_be_null": false,
+                "map_bit": 0,
+                "depends_on_map_bits": [
+                ] /* depends_on_map_bits */
+              }
+            ] /* table_dependencies */
+          },
+          {
+            "ref_optimizer_key_uses": [
+            ] /* ref_optimizer_key_uses */
+          },
+          {
+            "rows_estimation": [
+              {
+                "database": "test",
+                "table": "t1",
+                "range_analysis": {
+                  "table_scan": {
+                    "rows": 9,
+                    "cost": 5.9198
+                  } /* table_scan */,
+                  "potential_range_indices": [
+                    {
+                      "index": "a",
+                      "usable": true,
+                      "key_parts": [
+                        "a",
+                        "b"
+                      ] /* key_parts */
+                    }
+                  ] /* potential_range_indices */,
+                  "best_covering_index_scan": {
+                    "index": "a",
+                    "cost": 3.0581,
+                    "chosen": true
+                  } /* best_covering_index_scan */,
+                  "setup_range_conditions": [
+                  ] /* setup_range_conditions */,
+                  "group_index_range": {
+                    "chosen": false,
+                    "cause": "not_group_by_or_distinct"
+                  } /* group_index_range */,
+                  "analyzing_range_alternatives": {
+                    "range_scan_alternatives": [
+                      {
+                        "index": "a",
+                        "index_dives_for_eq_ranges": false,
+                        "ranges": [
+                          "1 <= a <= 1",
+                          "2 <= a <= 2",
+                          "5 <= a <= 5",
+                          "10 < a"
+                        ] /* ranges */,
+                        "index_only": true,
+                        "rows": 7,
+                        "cost": 2.6035,
+                        "rowid_ordered": false,
+                        "chosen": true
+                      }
+                    ] /* range_scan_alternatives */,
+                    "analyzing_roworder_intersect": {
+                      "usable": false,
+                      "cause": "too_few_roworder_scans"
+                    } /* analyzing_roworder_intersect */
+                  } /* analyzing_range_alternatives */,
+                  "chosen_range_access_summary": {
+                    "range_access_plan": {
+                      "type": "range_scan",
+                      "index": "a",
+                      "rows": 7,
+                      "ranges": [
+                        "1 <= a <= 1",
+                        "2 <= a <= 2",
+                        "5 <= a <= 5",
+                        "10 < a"
+                      ] /* ranges */
+                    } /* range_access_plan */,
+                    "rows_for_plan": 7,
+                    "cost_for_plan": 2.6035,
+                    "chosen": true
+                  } /* chosen_range_access_summary */
+                } /* range_analysis */
+              }
+            ] /* rows_estimation */
+          },
+          {
+            "considered_execution_plans": [
+              {
+                "database": "test",
+                "table": "t1",
+                "best_access_path": {
+                  "considered_access_paths": [
+                    {
+                      "access_type": "range",
+                      "rows": 7,
+                      "cost": 2.6035,
+                      "chosen": true
+                    }
+                  ] /* considered_access_paths */
+                } /* best_access_path */,
+                "cost_for_plan": 4.0035,
+                "rows_for_plan": 7,
+                "chosen": true
+              }
+            ] /* considered_execution_plans */
+          },
+          {
+            "attaching_conditions_to_tables": {
+              "original_condition": "((`test`.`t1`.`a` = 5) or (`test`.`t1`.`a` > 10) or (`test`.`t1`.`a` in (1,2)))",
+              "attached_conditions_computation": [
+              ] /* attached_conditions_computation */,
+              "attached_conditions_summary": [
+                {
+                  "database": "test",
+                  "table": "t1",
+                  "attached": "((`test`.`t1`.`a` = 5) or (`test`.`t1`.`a` > 10) or (`test`.`t1`.`a` in (1,2)))"
+                }
+              ] /* attached_conditions_summary */
+            } /* attaching_conditions_to_tables */
+          },
+          {
+            "refine_plan": [
+              {
+                "database": "test",
+                "table": "t1",
+                "access_type": "range"
+              }
+            ] /* refine_plan */
+          }
+        ] /* steps */
+      } /* join_optimization */
+    },
+    {
+      "join_explain": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_explain */
+    }
+  ] /* steps */
+}	0	0
+
+# 3 equality ranges: should use index statistics
+EXPLAIN SELECT * FROM t1 WHERE a=5 AND (b=2 OR b=3 OR b=4);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	range	a	a	10	NULL	3	Using where; Using index
+SELECT * FROM information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+EXPLAIN SELECT * FROM t1 WHERE a=5 AND (b=2 OR b=3 OR b=4)	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where ((`test`.`t1`.`a` = 5) and ((`test`.`t1`.`b` = 2) or (`test`.`t1`.`b` = 3) or (`test`.`t1`.`b` = 4)))"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+          {
+            "condition_processing": {
+              "condition": "WHERE",
+              "original_condition": "((`test`.`t1`.`a` = 5) and ((`test`.`t1`.`b` = 2) or (`test`.`t1`.`b` = 3) or (`test`.`t1`.`b` = 4)))",
+              "steps": [
+                {
+                  "transformation": "equality_propagation",
+                  "resulting_condition": "((multiple equal(2, `test`.`t1`.`b`) or multiple equal(3, `test`.`t1`.`b`) or multiple equal(4, `test`.`t1`.`b`)) and multiple equal(5, `test`.`t1`.`a`))"
+                },
+                {
+                  "transformation": "constant_propagation",
+                  "resulting_condition": "((multiple equal(2, `test`.`t1`.`b`) or multiple equal(3, `test`.`t1`.`b`) or multiple equal(4, `test`.`t1`.`b`)) and multiple equal(5, `test`.`t1`.`a`))"
+                },
+                {
+                  "transformation": "trivial_condition_removal",
+                  "resulting_condition": "((multiple equal(2, `test`.`t1`.`b`) or multiple equal(3, `test`.`t1`.`b`) or multiple equal(4, `test`.`t1`.`b`)) and multiple equal(5, `test`.`t1`.`a`))"
+                }
+              ] /* steps */
+            } /* condition_processing */
+          },
+          {
+            "table_dependencies": [
+              {
+                "database": "test",
+                "table": "t1",
+                "row_may_be_null": false,
+                "map_bit": 0,
+                "depends_on_map_bits": [
+                ] /* depends_on_map_bits */
+              }
+            ] /* table_dependencies */
+          },
+          {
+            "ref_optimizer_key_uses": [
+              {
+                "database": "test",
+                "table": "t1",
+                "field": "a",
+                "equals": "5",
+                "null_rejecting": false
+              }
+            ] /* ref_optimizer_key_uses */
+          },
+          {
+            "rows_estimation": [
+              {
+                "database": "test",
+                "table": "t1",
+                "range_analysis": {
+                  "table_scan": {
+                    "rows": 9,
+                    "cost": 5.9198
+                  } /* table_scan */,
+                  "potential_range_indices": [
+                    {
+                      "index": "a",
+                      "usable": true,
+                      "key_parts": [
+                        "a",
+                        "b"
+                      ] /* key_parts */
+                    }
+                  ] /* potential_range_indices */,
+                  "best_covering_index_scan": {
+                    "index": "a",
+                    "cost": 3.0581,
+                    "chosen": true
+                  } /* best_covering_index_scan */,
+                  "setup_range_conditions": [
+                  ] /* setup_range_conditions */,
+                  "group_index_range": {
+                    "chosen": false,
+                    "cause": "not_group_by_or_distinct"
+                  } /* group_index_range */,
+                  "analyzing_range_alternatives": {
+                    "range_scan_alternatives": [
+                      {
+                        "index": "a",
+                        "index_dives_for_eq_ranges": false,
+                        "ranges": [
+                          "5 <= a <= 5 AND 2 <= b <= 2",
+                          "5 <= a <= 5 AND 3 <= b <= 3",
+                          "5 <= a <= 5 AND 4 <= b <= 4"
+                        ] /* ranges */,
+                        "index_only": true,
+                        "rows": 3,
+                        "cost": 1.6745,
+                        "rowid_ordered": false,
+                        "chosen": true
+                      }
+                    ] /* range_scan_alternatives */,
+                    "analyzing_roworder_intersect": {
+                      "usable": false,
+                      "cause": "too_few_roworder_scans"
+                    } /* analyzing_roworder_intersect */
+                  } /* analyzing_range_alternatives */,
+                  "chosen_range_access_summary": {
+                    "range_access_plan": {
+                      "type": "range_scan",
+                      "index": "a",
+                      "rows": 3,
+                      "ranges": [
+                        "5 <= a <= 5 AND 2 <= b <= 2",
+                        "5 <= a <= 5 AND 3 <= b <= 3",
+                        "5 <= a <= 5 AND 4 <= b <= 4"
+                      ] /* ranges */
+                    } /* range_access_plan */,
+                    "rows_for_plan": 3,
+                    "cost_for_plan": 1.6745,
+                    "chosen": true
+                  } /* chosen_range_access_summary */
+                } /* range_analysis */
+              }
+            ] /* rows_estimation */
+          },
+          {
+            "considered_execution_plans": [
+              {
+                "database": "test",
+                "table": "t1",
+                "best_access_path": {
+                  "considered_access_paths": [
+                    {
+                      "access_type": "ref",
+                      "index": "a",
+                      "rows": 3,
+                      "cost": 1.6645,
+                      "chosen": true
+                    },
+                    {
+                      "access_type": "range",
+                      "rows": 3,
+                      "cost": 1.6745,
+                      "chosen": false
+                    }
+                  ] /* considered_access_paths */
+                } /* best_access_path */,
+                "cost_for_plan": 1.6645,
+                "rows_for_plan": 3,
+                "chosen": true
+              }
+            ] /* considered_execution_plans */
+          },
+          {
+            "attaching_conditions_to_tables": {
+              "original_condition": "((`test`.`t1`.`a` = 5) and ((`test`.`t1`.`b` = 2) or (`test`.`t1`.`b` = 3) or (`test`.`t1`.`b` = 4)))",
+              "attached_conditions_computation": [
+                {
+                  "access_type_changed": {
+                    "database": "test",
+                    "table": "t1",
+                    "index": "a",
+                    "old_type": "ref",
+                    "new_type": "range",
+                    "cause": "uses_more_keyparts"
+                  } /* access_type_changed */
+                }
+              ] /* attached_conditions_computation */,
+              "attached_conditions_summary": [
+                {
+                  "database": "test",
+                  "table": "t1",
+                  "attached": "((`test`.`t1`.`a` = 5) and ((`test`.`t1`.`b` = 2) or (`test`.`t1`.`b` = 3) or (`test`.`t1`.`b` = 4)))"
+                }
+              ] /* attached_conditions_summary */
+            } /* attaching_conditions_to_tables */
+          },
+          {
+            "refine_plan": [
+              {
+                "database": "test",
+                "table": "t1",
+                "access_type": "range"
+              }
+            ] /* refine_plan */
+          }
+        ] /* steps */
+      } /* join_optimization */
+    },
+    {
+      "join_explain": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_explain */
+    }
+  ] /* steps */
+}	0	0
+
+# 2 equality ranges: should not use index statistics
+EXPLAIN SELECT * FROM t1 WHERE a=5 AND (b=2 OR b=3 OR b>4);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	range	a	a	10	NULL	3	Using where; Using index
+SELECT * FROM information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+EXPLAIN SELECT * FROM t1 WHERE a=5 AND (b=2 OR b=3 OR b>4)	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where ((`test`.`t1`.`a` = 5) and ((`test`.`t1`.`b` = 2) or (`test`.`t1`.`b` = 3) or (`test`.`t1`.`b` > 4)))"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+          {
+            "condition_processing": {
+              "condition": "WHERE",
+              "original_condition": "((`test`.`t1`.`a` = 5) and ((`test`.`t1`.`b` = 2) or (`test`.`t1`.`b` = 3) or (`test`.`t1`.`b` > 4)))",
+              "steps": [
+                {
+                  "transformation": "equality_propagation",
+                  "resulting_condition": "((multiple equal(2, `test`.`t1`.`b`) or multiple equal(3, `test`.`t1`.`b`) or (`test`.`t1`.`b` > 4)) and multiple equal(5, `test`.`t1`.`a`))"
+                },
+                {
+                  "transformation": "constant_propagation",
+                  "resulting_condition": "((multiple equal(2, `test`.`t1`.`b`) or multiple equal(3, `test`.`t1`.`b`) or (`test`.`t1`.`b` > 4)) and multiple equal(5, `test`.`t1`.`a`))"
+                },
+                {
+                  "transformation": "trivial_condition_removal",
+                  "resulting_condition": "((multiple equal(2, `test`.`t1`.`b`) or multiple equal(3, `test`.`t1`.`b`) or (`test`.`t1`.`b` > 4)) and multiple equal(5, `test`.`t1`.`a`))"
+                }
+              ] /* steps */
+            } /* condition_processing */
+          },
+          {
+            "table_dependencies": [
+              {
+                "database": "test",
+                "table": "t1",
+                "row_may_be_null": false,
+                "map_bit": 0,
+                "depends_on_map_bits": [
+                ] /* depends_on_map_bits */
+              }
+            ] /* table_dependencies */
+          },
+          {
+            "ref_optimizer_key_uses": [
+              {
+                "database": "test",
+                "table": "t1",
+                "field": "a",
+                "equals": "5",
+                "null_rejecting": false
+              }
+            ] /* ref_optimizer_key_uses */
+          },
+          {
+            "rows_estimation": [
+              {
+                "database": "test",
+                "table": "t1",
+                "range_analysis": {
+                  "table_scan": {
+                    "rows": 9,
+                    "cost": 5.9198
+                  } /* table_scan */,
+                  "potential_range_indices": [
+                    {
+                      "index": "a",
+                      "usable": true,
+                      "key_parts": [
+                        "a",
+                        "b"
+                      ] /* key_parts */
+                    }
+                  ] /* potential_range_indices */,
+                  "best_covering_index_scan": {
+                    "index": "a",
+                    "cost": 3.0581,
+                    "chosen": true
+                  } /* best_covering_index_scan */,
+                  "setup_range_conditions": [
+                  ] /* setup_range_conditions */,
+                  "group_index_range": {
+                    "chosen": false,
+                    "cause": "not_group_by_or_distinct"
+                  } /* group_index_range */,
+                  "analyzing_range_alternatives": {
+                    "range_scan_alternatives": [
+                      {
+                        "index": "a",
+                        "index_dives_for_eq_ranges": true,
+                        "ranges": [
+                          "5 <= a <= 5 AND 2 <= b <= 2",
+                          "5 <= a <= 5 AND 3 <= b <= 3",
+                          "5 <= a <= 5 AND 4 < b"
+                        ] /* ranges */,
+                        "index_only": true,
+                        "rows": 3,
+                        "cost": 1.6745,
+                        "rowid_ordered": false,
+                        "chosen": true
+                      }
+                    ] /* range_scan_alternatives */,
+                    "analyzing_roworder_intersect": {
+                      "usable": false,
+                      "cause": "too_few_roworder_scans"
+                    } /* analyzing_roworder_intersect */
+                  } /* analyzing_range_alternatives */,
+                  "chosen_range_access_summary": {
+                    "range_access_plan": {
+                      "type": "range_scan",
+                      "index": "a",
+                      "rows": 3,
+                      "ranges": [
+                        "5 <= a <= 5 AND 2 <= b <= 2",
+                        "5 <= a <= 5 AND 3 <= b <= 3",
+                        "5 <= a <= 5 AND 4 < b"
+                      ] /* ranges */
+                    } /* range_access_plan */,
+                    "rows_for_plan": 3,
+                    "cost_for_plan": 1.6745,
+                    "chosen": true
+                  } /* chosen_range_access_summary */
+                } /* range_analysis */
+              }
+            ] /* rows_estimation */
+          },
+          {
+            "considered_execution_plans": [
+              {
+                "database": "test",
+                "table": "t1",
+                "best_access_path": {
+                  "considered_access_paths": [
+                    {
+                      "access_type": "ref",
+                      "index": "a",
+                      "rows": 3,
+                      "cost": 1.6645,
+                      "chosen": true
+                    },
+                    {
+                      "access_type": "range",
+                      "rows": 3,
+                      "cost": 1.6745,
+                      "chosen": false
+                    }
+                  ] /* considered_access_paths */
+                } /* best_access_path */,
+                "cost_for_plan": 1.6645,
+                "rows_for_plan": 3,
+                "chosen": true
+              }
+            ] /* considered_execution_plans */
+          },
+          {
+            "attaching_conditions_to_tables": {
+              "original_condition": "((`test`.`t1`.`a` = 5) and ((`test`.`t1`.`b` = 2) or (`test`.`t1`.`b` = 3) or (`test`.`t1`.`b` > 4)))",
+              "attached_conditions_computation": [
+                {
+                  "access_type_changed": {
+                    "database": "test",
+                    "table": "t1",
+                    "index": "a",
+                    "old_type": "ref",
+                    "new_type": "range",
+                    "cause": "uses_more_keyparts"
+                  } /* access_type_changed */
+                }
+              ] /* attached_conditions_computation */,
+              "attached_conditions_summary": [
+                {
+                  "database": "test",
+                  "table": "t1",
+                  "attached": "((`test`.`t1`.`a` = 5) and ((`test`.`t1`.`b` = 2) or (`test`.`t1`.`b` = 3) or (`test`.`t1`.`b` > 4)))"
+                }
+              ] /* attached_conditions_summary */
+            } /* attaching_conditions_to_tables */
+          },
+          {
+            "refine_plan": [
+              {
+                "database": "test",
+                "table": "t1",
+                "access_type": "range"
+              }
+            ] /* refine_plan */
+          }
+        ] /* steps */
+      } /* join_optimization */
+    },
+    {
+      "join_explain": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_explain */
+    }
+  ] /* steps */
+}	0	0
+
+# 2 equality ranges: should not use index statistics
+EXPLAIN SELECT * FROM t1 WHERE a=5 AND (b=2 OR b=3 OR b IS NULL);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	range	a	a	10	NULL	3	Using where; Using index
+SELECT * FROM information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+EXPLAIN SELECT * FROM t1 WHERE a=5 AND (b=2 OR b=3 OR b IS NULL)	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where ((`test`.`t1`.`a` = 5) and ((`test`.`t1`.`b` = 2) or (`test`.`t1`.`b` = 3) or isnull(`test`.`t1`.`b`)))"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+          {
+            "condition_processing": {
+              "condition": "WHERE",
+              "original_condition": "((`test`.`t1`.`a` = 5) and ((`test`.`t1`.`b` = 2) or (`test`.`t1`.`b` = 3) or isnull(`test`.`t1`.`b`)))",
+              "steps": [
+                {
+                  "transformation": "equality_propagation",
+                  "resulting_condition": "((multiple equal(2, `test`.`t1`.`b`) or multiple equal(3, `test`.`t1`.`b`) or isnull(`test`.`t1`.`b`)) and multiple equal(5, `test`.`t1`.`a`))"
+                },
+                {
+                  "transformation": "constant_propagation",
+                  "resulting_condition": "((multiple equal(2, `test`.`t1`.`b`) or multiple equal(3, `test`.`t1`.`b`) or isnull(`test`.`t1`.`b`)) and multiple equal(5, `test`.`t1`.`a`))"
+                },
+                {
+                  "transformation": "trivial_condition_removal",
+                  "resulting_condition": "((multiple equal(2, `test`.`t1`.`b`) or multiple equal(3, `test`.`t1`.`b`) or isnull(`test`.`t1`.`b`)) and multiple equal(5, `test`.`t1`.`a`))"
+                }
+              ] /* steps */
+            } /* condition_processing */
+          },
+          {
+            "table_dependencies": [
+              {
+                "database": "test",
+                "table": "t1",
+                "row_may_be_null": false,
+                "map_bit": 0,
+                "depends_on_map_bits": [
+                ] /* depends_on_map_bits */
+              }
+            ] /* table_dependencies */
+          },
+          {
+            "ref_optimizer_key_uses": [
+              {
+                "database": "test",
+                "table": "t1",
+                "field": "a",
+                "equals": "5",
+                "null_rejecting": false
+              }
+            ] /* ref_optimizer_key_uses */
+          },
+          {
+            "rows_estimation": [
+              {
+                "database": "test",
+                "table": "t1",
+                "range_analysis": {
+                  "table_scan": {
+                    "rows": 9,
+                    "cost": 5.9198
+                  } /* table_scan */,
+                  "potential_range_indices": [
+                    {
+                      "index": "a",
+                      "usable": true,
+                      "key_parts": [
+                        "a",
+                        "b"
+                      ] /* key_parts */
+                    }
+                  ] /* potential_range_indices */,
+                  "best_covering_index_scan": {
+                    "index": "a",
+                    "cost": 3.0581,
+                    "chosen": true
+                  } /* best_covering_index_scan */,
+                  "setup_range_conditions": [
+                  ] /* setup_range_conditions */,
+                  "group_index_range": {
+                    "chosen": false,
+                    "cause": "not_group_by_or_distinct"
+                  } /* group_index_range */,
+                  "analyzing_range_alternatives": {
+                    "range_scan_alternatives": [
+                      {
+                        "index": "a",
+                        "index_dives_for_eq_ranges": true,
+                        "ranges": [
+                          "5 <= a <= 5 AND NULL <= b <= NULL",
+                          "5 <= a <= 5 AND 2 <= b <= 2",
+                          "5 <= a <= 5 AND 3 <= b <= 3"
+                        ] /* ranges */,
+                        "index_only": true,
+                        "rows": 3,
+                        "cost": 1.6745,
+                        "rowid_ordered": false,
+                        "chosen": true
+                      }
+                    ] /* range_scan_alternatives */,
+                    "analyzing_roworder_intersect": {
+                      "usable": false,
+                      "cause": "too_few_roworder_scans"
+                    } /* analyzing_roworder_intersect */
+                  } /* analyzing_range_alternatives */,
+                  "chosen_range_access_summary": {
+                    "range_access_plan": {
+                      "type": "range_scan",
+                      "index": "a",
+                      "rows": 3,
+                      "ranges": [
+                        "5 <= a <= 5 AND NULL <= b <= NULL",
+                        "5 <= a <= 5 AND 2 <= b <= 2",
+                        "5 <= a <= 5 AND 3 <= b <= 3"
+                      ] /* ranges */
+                    } /* range_access_plan */,
+                    "rows_for_plan": 3,
+                    "cost_for_plan": 1.6745,
+                    "chosen": true
+                  } /* chosen_range_access_summary */
+                } /* range_analysis */
+              }
+            ] /* rows_estimation */
+          },
+          {
+            "considered_execution_plans": [
+              {
+                "database": "test",
+                "table": "t1",
+                "best_access_path": {
+                  "considered_access_paths": [
+                    {
+                      "access_type": "ref",
+                      "index": "a",
+                      "rows": 3,
+                      "cost": 1.6645,
+                      "chosen": true
+                    },
+                    {
+                      "access_type": "range",
+                      "rows": 3,
+                      "cost": 1.6745,
+                      "chosen": false
+                    }
+                  ] /* considered_access_paths */
+                } /* best_access_path */,
+                "cost_for_plan": 1.6645,
+                "rows_for_plan": 3,
+                "chosen": true
+              }
+            ] /* considered_execution_plans */
+          },
+          {
+            "attaching_conditions_to_tables": {
+              "original_condition": "((`test`.`t1`.`a` = 5) and ((`test`.`t1`.`b` = 2) or (`test`.`t1`.`b` = 3) or isnull(`test`.`t1`.`b`)))",
+              "attached_conditions_computation": [
+                {
+                  "access_type_changed": {
+                    "database": "test",
+                    "table": "t1",
+                    "index": "a",
+                    "old_type": "ref",
+                    "new_type": "range",
+                    "cause": "uses_more_keyparts"
+                  } /* access_type_changed */
+                }
+              ] /* attached_conditions_computation */,
+              "attached_conditions_summary": [
+                {
+                  "database": "test",
+                  "table": "t1",
+                  "attached": "((`test`.`t1`.`a` = 5) and ((`test`.`t1`.`b` = 2) or (`test`.`t1`.`b` = 3) or isnull(`test`.`t1`.`b`)))"
+                }
+              ] /* attached_conditions_summary */
+            } /* attaching_conditions_to_tables */
+          },
+          {
+            "refine_plan": [
+              {
+                "database": "test",
+                "table": "t1",
+                "access_type": "range"
+              }
+            ] /* refine_plan */
+          }
+        ] /* steps */
+      } /* join_optimization */
+    },
+    {
+      "join_explain": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_explain */
+    }
+  ] /* steps */
+}	0	0
+
+# 0 equality ranges: should not use index statistics
+EXPLAIN SELECT * FROM t1 WHERE a>5 AND (b=2 OR b=3 OR b=4);
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	range	a	a	5	NULL	1	Using where; Using index
+SELECT * FROM information_schema.OPTIMIZER_TRACE;
+QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
+EXPLAIN SELECT * FROM t1 WHERE a>5 AND (b=2 OR b=3 OR b=4)	{
+  "steps": [
+    {
+      "join_preparation": {
+        "select#": 1,
+        "steps": [
+          {
+            "expanded_query": "/* select#1 */ select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` from `test`.`t1` where ((`test`.`t1`.`a` > 5) and ((`test`.`t1`.`b` = 2) or (`test`.`t1`.`b` = 3) or (`test`.`t1`.`b` = 4)))"
+          }
+        ] /* steps */
+      } /* join_preparation */
+    },
+    {
+      "join_optimization": {
+        "select#": 1,
+        "steps": [
+          {
+            "condition_processing": {
+              "condition": "WHERE",
+              "original_condition": "((`test`.`t1`.`a` > 5) and ((`test`.`t1`.`b` = 2) or (`test`.`t1`.`b` = 3) or (`test`.`t1`.`b` = 4)))",
+              "steps": [
+                {
+                  "transformation": "equality_propagation",
+                  "resulting_condition": "((`test`.`t1`.`a` > 5) and (multiple equal(2, `test`.`t1`.`b`) or multiple equal(3, `test`.`t1`.`b`) or multiple equal(4, `test`.`t1`.`b`)))"
+                },
+                {
+                  "transformation": "constant_propagation",
+                  "resulting_condition": "((`test`.`t1`.`a` > 5) and (multiple equal(2, `test`.`t1`.`b`) or multiple equal(3, `test`.`t1`.`b`) or multiple equal(4, `test`.`t1`.`b`)))"
+                },
+                {
+                  "transformation": "trivial_condition_removal",
+                  "resulting_condition": "((`test`.`t1`.`a` > 5) and (multiple equal(2, `test`.`t1`.`b`) or multiple equal(3, `test`.`t1`.`b`) or multiple equal(4, `test`.`t1`.`b`)))"
+                }
+              ] /* steps */
+            } /* condition_processing */
+          },
+          {
+            "table_dependencies": [
+              {
+                "database": "test",
+                "table": "t1",
+                "row_may_be_null": false,
+                "map_bit": 0,
+                "depends_on_map_bits": [
+                ] /* depends_on_map_bits */
+              }
+            ] /* table_dependencies */
+          },
+          {
+            "ref_optimizer_key_uses": [
+            ] /* ref_optimizer_key_uses */
+          },
+          {
+            "rows_estimation": [
+              {
+                "database": "test",
+                "table": "t1",
+                "range_analysis": {
+                  "table_scan": {
+                    "rows": 9,
+                    "cost": 5.9198
+                  } /* table_scan */,
+                  "potential_range_indices": [
+                    {
+                      "index": "a",
+                      "usable": true,
+                      "key_parts": [
+                        "a",
+                        "b"
+                      ] /* key_parts */
+                    }
+                  ] /* potential_range_indices */,
+                  "best_covering_index_scan": {
+                    "index": "a",
+                    "cost": 3.0581,
+                    "chosen": true
+                  } /* best_covering_index_scan */,
+                  "setup_range_conditions": [
+                  ] /* setup_range_conditions */,
+                  "group_index_range": {
+                    "chosen": false,
+                    "cause": "not_group_by_or_distinct"
+                  } /* group_index_range */,
+                  "analyzing_range_alternatives": {
+                    "range_scan_alternatives": [
+                      {
+                        "index": "a",
+                        "index_dives_for_eq_ranges": true,
+                        "ranges": [
+                          "5 < a AND 2 <= b <= 2",
+                          "5 < a AND 3 <= b <= 3",
+                          "5 < a AND 4 <= b <= 4"
+                        ] /* ranges */,
+                        "index_only": true,
+                        "rows": 1,
+                        "cost": 2.21,
+                        "rowid_ordered": false,
+                        "chosen": true
+                      }
+                    ] /* range_scan_alternatives */,
+                    "analyzing_roworder_intersect": {
+                      "usable": false,
+                      "cause": "too_few_roworder_scans"
+                    } /* analyzing_roworder_intersect */
+                  } /* analyzing_range_alternatives */,
+                  "chosen_range_access_summary": {
+                    "range_access_plan": {
+                      "type": "range_scan",
+                      "index": "a",
+                      "rows": 1,
+                      "ranges": [
+                        "5 < a AND 2 <= b <= 2",
+                        "5 < a AND 3 <= b <= 3",
+                        "5 < a AND 4 <= b <= 4"
+                      ] /* ranges */
+                    } /* range_access_plan */,
+                    "rows_for_plan": 1,
+                    "cost_for_plan": 2.21,
+                    "chosen": true
+                  } /* chosen_range_access_summary */
+                } /* range_analysis */
+              }
+            ] /* rows_estimation */
+          },
+          {
+            "considered_execution_plans": [
+              {
+                "database": "test",
+                "table": "t1",
+                "best_access_path": {
+                  "considered_access_paths": [
+                    {
+                      "access_type": "range",
+                      "rows": 1,
+                      "cost": 2.21,
+                      "chosen": true
+                    }
+                  ] /* considered_access_paths */
+                } /* best_access_path */,
+                "cost_for_plan": 2.41,
+                "rows_for_plan": 1,
+                "chosen": true
+              }
+            ] /* considered_execution_plans */
+          },
+          {
+            "attaching_conditions_to_tables": {
+              "original_condition": "((`test`.`t1`.`a` > 5) and ((`test`.`t1`.`b` = 2) or (`test`.`t1`.`b` = 3) or (`test`.`t1`.`b` = 4)))",
+              "attached_conditions_computation": [
+              ] /* attached_conditions_computation */,
+              "attached_conditions_summary": [
+                {
+                  "database": "test",
+                  "table": "t1",
+                  "attached": "((`test`.`t1`.`a` > 5) and ((`test`.`t1`.`b` = 2) or (`test`.`t1`.`b` = 3) or (`test`.`t1`.`b` = 4)))"
+                }
+              ] /* attached_conditions_summary */
+            } /* attaching_conditions_to_tables */
+          },
+          {
+            "refine_plan": [
+              {
+                "database": "test",
+                "table": "t1",
+                "access_type": "range"
+              }
+            ] /* refine_plan */
+          }
+        ] /* steps */
+      } /* join_optimization */
+    },
+    {
+      "join_explain": {
+        "select#": 1,
+        "steps": [
+        ] /* steps */
+      } /* join_explain */
+    }
+  ] /* steps */
+}	0	0
+DROP TABLE t1;
+SET eq_range_index_dive_limit=default;

=== modified file 'mysql-test/suite/opt_trace/r/general2_no_prot.result'
--- a/mysql-test/suite/opt_trace/r/general2_no_prot.result	2012-01-20 15:30:14 +0000
+++ b/mysql-test/suite/opt_trace/r/general2_no_prot.result	2012-01-26 13:09:59 +0000
@@ -331,6 +331,7 @@ TRACE
                     "range_scan_alternatives": [
                       {
                         "index": "a",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "12 < a"
                         ] /* ranges */,
@@ -4057,6 +4058,7 @@ select replace(t3._field_140, "\r","^M")
                     "range_scan_alternatives": [
                       {
                         "index": "PRIMARY",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "1 <= seq_0_id <= 1"
                         ] /* ranges */,
@@ -4144,6 +4146,7 @@ select replace(t3._field_140, "\r","^M")
                     "range_scan_alternatives": [
                       {
                         "index": "PRIMARY",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "1 <= seq_0_id <= 1"
                         ] /* ranges */,
@@ -4241,6 +4244,7 @@ select replace(t3._field_140, "\r","^M")
                     "range_scan_alternatives": [
                       {
                         "index": "PRIMARY",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "1 <= seq_0_id <= 1"
                         ] /* ranges */,

=== modified file 'mysql-test/suite/opt_trace/r/general_no_prot_all.result'
--- a/mysql-test/suite/opt_trace/r/general_no_prot_all.result	2011-11-22 13:47:46 +0000
+++ b/mysql-test/suite/opt_trace/r/general_no_prot_all.result	2012-01-26 13:09:59 +0000
@@ -1536,6 +1536,7 @@ explain SELECT c FROM t5 where c+1 in (s
                     "range_scan_alternatives": [
                       {
                         "index": "d",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "NULL <= d <= NULL"
                         ] /* ranges */,
@@ -1911,6 +1912,7 @@ explain SELECT c FROM t5 where c+1 in (s
                           "range_scan_alternatives": [
                             {
                               "index": "d",
+                              "index_dives_for_eq_ranges": true,
                               "ranges": [
                                 "NULL <= d <= NULL"
                               ] /* ranges */,
@@ -6740,6 +6742,7 @@ update t6 set d=5 where d is NULL	{
           "range_scan_alternatives": [
             {
               "index": "d",
+              "index_dives_for_eq_ranges": true,
               "ranges": [
                 "NULL <= d <= NULL"
               ] /* ranges */,
@@ -6804,6 +6807,7 @@ delete from t6 where d=5	{
           "range_scan_alternatives": [
             {
               "index": "d",
+              "index_dives_for_eq_ranges": true,
               "ranges": [
                 "5 <= d <= 5"
               ] /* ranges */,
@@ -6928,6 +6932,7 @@ insert into t6 select * from t6 where d>
                     "range_scan_alternatives": [
                       {
                         "index": "d",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "7 < d"
                         ] /* ranges */,
@@ -7115,6 +7120,7 @@ update t5, t6 set t6.d=t6.d+t5.c+4-t5.c-
                     "range_scan_alternatives": [
                       {
                         "index": "d",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "7000 < d"
                         ] /* ranges */,
@@ -7319,6 +7325,7 @@ delete t6 from t5, t6 where d>7000	{
                     "range_scan_alternatives": [
                       {
                         "index": "d",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "7000 < d"
                         ] /* ranges */,
@@ -7743,6 +7750,7 @@ select * from t6 where d in (select f1()
                     "range_scan_alternatives": [
                       {
                         "index": "d",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "3 <= d <= 3"
                         ] /* ranges */,
@@ -8436,6 +8444,7 @@ select d into res from t6 where d in (se
                     "range_scan_alternatives": [
                       {
                         "index": "d",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "3 <= d <= 3"
                         ] /* ranges */,
@@ -9152,6 +9161,7 @@ insert into t2 select d,100,200 from t6 
                     "range_scan_alternatives": [
                       {
                         "index": "d",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "NULL < d"
                         ] /* ranges */,
@@ -10045,6 +10055,7 @@ select d into res from t6 where d in (se
                     "range_scan_alternatives": [
                       {
                         "index": "d",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "3 <= d <= 3"
                         ] /* ranges */,

=== modified file 'mysql-test/suite/opt_trace/r/general_no_prot_none.result'
--- a/mysql-test/suite/opt_trace/r/general_no_prot_none.result	2011-11-30 11:36:14 +0000
+++ b/mysql-test/suite/opt_trace/r/general_no_prot_none.result	2012-01-26 13:09:59 +0000
@@ -1583,6 +1583,7 @@ explain SELECT c FROM t5 where c+1 in (s
                           "range_scan_alternatives": [
                             {
                               "index": "d",
+                              "index_dives_for_eq_ranges": true,
                               "ranges": [
                                 "NULL <= d <= NULL"
                               ] /* ranges */,
@@ -1872,6 +1873,7 @@ explain SELECT c FROM t5 where c+1 in (s
                           "range_scan_alternatives": [
                             {
                               "index": "d",
+                              "index_dives_for_eq_ranges": true,
                               "ranges": [
                                 "NULL <= d <= NULL"
                               ] /* ranges */,
@@ -5996,6 +5998,7 @@ update t6 set d=5 where d is NULL	{
           "range_scan_alternatives": [
             {
               "index": "d",
+              "index_dives_for_eq_ranges": true,
               "ranges": [
                 "NULL <= d <= NULL"
               ] /* ranges */,
@@ -6060,6 +6063,7 @@ delete from t6 where d=5	{
           "range_scan_alternatives": [
             {
               "index": "d",
+              "index_dives_for_eq_ranges": true,
               "ranges": [
                 "5 <= d <= 5"
               ] /* ranges */,
@@ -6184,6 +6188,7 @@ insert into t6 select * from t6 where d>
                     "range_scan_alternatives": [
                       {
                         "index": "d",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "7 < d"
                         ] /* ranges */,
@@ -6371,6 +6376,7 @@ update t5, t6 set t6.d=t6.d+t5.c+4-t5.c-
                     "range_scan_alternatives": [
                       {
                         "index": "d",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "7000 < d"
                         ] /* ranges */,
@@ -6575,6 +6581,7 @@ delete t6 from t5, t6 where d>7000	{
                     "range_scan_alternatives": [
                       {
                         "index": "d",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "7000 < d"
                         ] /* ranges */,
@@ -8399,6 +8406,7 @@ insert into t2 select d,100,200 from t6 
                     "range_scan_alternatives": [
                       {
                         "index": "d",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "NULL < d"
                         ] /* ranges */,

=== modified file 'mysql-test/suite/opt_trace/r/range_no_prot.result'
--- a/mysql-test/suite/opt_trace/r/range_no_prot.result	2011-12-01 14:12:10 +0000
+++ b/mysql-test/suite/opt_trace/r/range_no_prot.result	2012-01-26 13:09:59 +0000
@@ -157,6 +157,7 @@ EXPLAIN SELECT * FROM t1 WHERE key2 < 5 
                     "range_scan_alternatives": [
                       {
                         "index": "i2",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "key2 < 5",
                           "1020 < key2"
@@ -529,6 +530,7 @@ EXPLAIN SELECT * FROM t1 WHERE key1 < 3 
                           "range_scan_alternatives": [
                             {
                               "index": "i1",
+                              "index_dives_for_eq_ranges": true,
                               "ranges": [
                                 "key1 < 3"
                               ] /* ranges */,
@@ -546,6 +548,7 @@ EXPLAIN SELECT * FROM t1 WHERE key1 < 3 
                           "range_scan_alternatives": [
                             {
                               "index": "i2",
+                              "index_dives_for_eq_ranges": true,
                               "ranges": [
                                 "1020 < key2"
                               ] /* ranges */,
@@ -1233,6 +1236,7 @@ GROUP BY key2	{
                       {
                         "index": "i2_1",
                         "covering": true,
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "1 <= key2 <= 1",
                           "2 <= key2 <= 2",
@@ -1246,6 +1250,7 @@ GROUP BY key2	{
                       {
                         "index": "i2_2",
                         "covering": true,
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "1 <= key2 <= 1",
                           "2 <= key2 <= 2",
@@ -1283,6 +1288,7 @@ GROUP BY key2	{
                     "range_scan_alternatives": [
                       {
                         "index": "i2_1",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "1 <= key2 <= 1",
                           "2 <= key2 <= 2",
@@ -1298,6 +1304,7 @@ GROUP BY key2	{
                       },
                       {
                         "index": "i2_2",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "1 <= key2 <= 1",
                           "2 <= key2 <= 2",
@@ -1540,6 +1547,7 @@ EXPLAIN SELECT * FROM t2 WHERE key2 = 1 
                     "range_scan_alternatives": [
                       {
                         "index": "i2_1",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "1 <= key2 <= 1"
                         ] /* ranges */,
@@ -1551,6 +1559,7 @@ EXPLAIN SELECT * FROM t2 WHERE key2 = 1 
                       },
                       {
                         "index": "i2_2",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "1 <= key2 <= 1"
                         ] /* ranges */,
@@ -1787,6 +1796,7 @@ EXPLAIN SELECT * FROM t1 WHERE key2=10 O
                           "range_scan_alternatives": [
                             {
                               "index": "i2",
+                              "index_dives_for_eq_ranges": true,
                               "ranges": [
                                 "10 <= key2 <= 10"
                               ] /* ranges */,
@@ -1804,6 +1814,7 @@ EXPLAIN SELECT * FROM t1 WHERE key2=10 O
                           "range_scan_alternatives": [
                             {
                               "index": "i3",
+                              "index_dives_for_eq_ranges": true,
                               "ranges": [
                                 "3 <= key3 <= 3"
                               ] /* ranges */,
@@ -2217,6 +2228,7 @@ EXPLAIN SELECT * FROM t2 WHERE key1a = 5
                     "range_scan_alternatives": [
                       {
                         "index": "PRIMARY",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "5 <= key1a <= 5 AND key1b < 10"
                         ] /* ranges */,
@@ -2228,6 +2240,7 @@ EXPLAIN SELECT * FROM t2 WHERE key1a = 5
                       },
                       {
                         "index": "i1b",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "key1b < 10 AND 5 <= key1a <= 5"
                         ] /* ranges */,
@@ -2435,6 +2448,7 @@ EXPLAIN SELECT * FROM t2 WHERE (key1a = 
                     "range_scan_alternatives": [
                       {
                         "index": "PRIMARY",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "4 <= key1a <= 4 AND 3 < key1b < 7",
                           "5 <= key1a <= 5 AND 2 < key1b < 10"
@@ -2447,6 +2461,7 @@ EXPLAIN SELECT * FROM t2 WHERE (key1a = 
                       },
                       {
                         "index": "i1b",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "2 < key1b <= 3 AND 5 <= key1a <= 5",
                           "3 < key1b < 7",
@@ -2650,6 +2665,7 @@ EXPLAIN SELECT * FROM t2 WHERE (key1b < 
                     "range_scan_alternatives": [
                       {
                         "index": "PRIMARY",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "4 <= key1a <= 4 AND 7 < key1b < 10",
                           "5 <= key1a <= 5 AND 7 < key1b < 10"
@@ -2662,6 +2678,7 @@ EXPLAIN SELECT * FROM t2 WHERE (key1b < 
                       },
                       {
                         "index": "i1b",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "7 < key1b < 10 AND 4 <= key1a <= 4",
                           "7 < key1b < 10 AND 5 <= key1a <= 5"
@@ -2882,6 +2899,7 @@ EXPLAIN SELECT * FROM t1 WHERE (key1 > 1
                           "range_scan_alternatives": [
                             {
                               "index": "i1",
+                              "index_dives_for_eq_ranges": true,
                               "ranges": [
                                 "1 < key1"
                               ] /* ranges */,
@@ -2900,6 +2918,7 @@ EXPLAIN SELECT * FROM t1 WHERE (key1 > 1
                           "range_scan_alternatives": [
                             {
                               "index": "i2",
+                              "index_dives_for_eq_ranges": true,
                               "ranges": [
                                 "2 < key2"
                               ] /* ranges */,
@@ -3129,6 +3148,7 @@ WHERE t1.key1=t2.key1a AND t1.key2 > 102
                     "range_scan_alternatives": [
                       {
                         "index": "i2",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "1020 < key2"
                         ] /* ranges */,
@@ -3384,6 +3404,7 @@ EXPLAIN SELECT * FROM t1 WHERE cola = 'f
                     "range_scan_alternatives": [
                       {
                         "index": "cola",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "foo <= cola <= foo"
                         ] /* ranges */,
@@ -3395,6 +3416,7 @@ EXPLAIN SELECT * FROM t1 WHERE cola = 'f
                       },
                       {
                         "index": "colb",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "bar <= colb <= bar"
                         ] /* ranges */,
@@ -3637,6 +3659,7 @@ EXPLAIN SELECT * FROM t1 WHERE cola = 'f
                     "range_scan_alternatives": [
                       {
                         "index": "cola",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "f\no <= cola <= f\no"
                         ] /* ranges */,
@@ -4510,6 +4533,7 @@ EXPLAIN SELECT * FROM t1 WHERE i1 > '2' 
                     "range_scan_alternatives": [
                       {
                         "index": "k1",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "2 < i1"
                         ] /* ranges */,
@@ -4521,6 +4545,7 @@ EXPLAIN SELECT * FROM t1 WHERE i1 > '2' 
                       },
                       {
                         "index": "k2",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "2 < i1"
                         ] /* ranges */,
@@ -4662,6 +4687,7 @@ EXPLAIN SELECT * FROM t1 WHERE i1 > '2' 
                     "range_scan_alternatives": [
                       {
                         "index": "k2",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "2 < i1"
                         ] /* ranges */,
@@ -4807,6 +4833,7 @@ EXPLAIN SELECT DISTINCT i1 FROM t1 WHERE
                       {
                         "index": "k1",
                         "covering": true,
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "1 <= i1"
                         ] /* ranges */,
@@ -4816,6 +4843,7 @@ EXPLAIN SELECT DISTINCT i1 FROM t1 WHERE
                       {
                         "index": "k2",
                         "covering": true,
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "1 <= i1"
                         ] /* ranges */,
@@ -4846,6 +4874,7 @@ EXPLAIN SELECT DISTINCT i1 FROM t1 WHERE
                     "range_scan_alternatives": [
                       {
                         "index": "k1",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "1 <= i1"
                         ] /* ranges */,
@@ -4858,6 +4887,7 @@ EXPLAIN SELECT DISTINCT i1 FROM t1 WHERE
                       },
                       {
                         "index": "k2",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "1 <= i1"
                         ] /* ranges */,
@@ -4969,6 +4999,7 @@ EXPLAIN SELECT DISTINCT i1 FROM t1 WHERE
                     "range_scan_alternatives": [
                       {
                         "index": "k1",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "1 <= i1"
                         ] /* ranges */,
@@ -5203,6 +5234,7 @@ EXPLAIN SELECT v FROM t1 WHERE i1 = 1 AN
                     "range_scan_alternatives": [
                       {
                         "index": "PRIMARY",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "pk < 3"
                         ] /* ranges */,
@@ -5215,6 +5247,7 @@ EXPLAIN SELECT v FROM t1 WHERE i1 = 1 AN
                       },
                       {
                         "index": "i1_idx",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "1 <= i1 <= 1"
                         ] /* ranges */,
@@ -5227,6 +5260,7 @@ EXPLAIN SELECT v FROM t1 WHERE i1 = 1 AN
                       },
                       {
                         "index": "v_idx",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "a <= v <= a AND 1 <= i1 <= 1"
                         ] /* ranges */,
@@ -5507,6 +5541,7 @@ EXPLAIN SELECT v FROM t1 WHERE i1 = 1 AN
                     "range_scan_alternatives": [
                       {
                         "index": "PRIMARY",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "pk < 3"
                         ] /* ranges */,
@@ -5518,6 +5553,7 @@ EXPLAIN SELECT v FROM t1 WHERE i1 = 1 AN
                       },
                       {
                         "index": "v_idx",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "a <= v <= a AND 1 <= i1 <= 1"
                         ] /* ranges */,
@@ -5529,6 +5565,7 @@ EXPLAIN SELECT v FROM t1 WHERE i1 = 1 AN
                       },
                       {
                         "index": "i1_i2_idx",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "1 <= i2 <= 1 AND 1 <= i1 <= 1"
                         ] /* ranges */,
@@ -5782,6 +5819,7 @@ EXPLAIN SELECT MAX(b), a FROM t1 WHERE b
                       {
                         "index": "PRIMARY",
                         "covering": true,
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "1 <= a <= 1 AND b < 2"
                         ] /* ranges */,
@@ -5816,6 +5854,7 @@ EXPLAIN SELECT MAX(b), a FROM t1 WHERE b
                     "range_scan_alternatives": [
                       {
                         "index": "PRIMARY",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "1 <= a <= 1 AND b < 2"
                         ] /* ranges */,
@@ -5828,6 +5867,7 @@ EXPLAIN SELECT MAX(b), a FROM t1 WHERE b
                       },
                       {
                         "index": "b",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "b < 2"
                         ] /* ranges */,
@@ -6079,6 +6119,7 @@ EXPLAIN SELECT * FROM t1 WHERE c1 = '1' 
                     "range_scan_alternatives": [
                       {
                         "index": "k1",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "1 <= c1 <= 1"
                         ] /* ranges */,
@@ -6090,6 +6131,7 @@ EXPLAIN SELECT * FROM t1 WHERE c1 = '1' 
                       },
                       {
                         "index": "k2",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "1 <= c1 <= 1"
                         ] /* ranges */,

=== modified file 'mysql-test/suite/opt_trace/r/subquery_no_prot.result'
--- a/mysql-test/suite/opt_trace/r/subquery_no_prot.result	2012-01-10 18:58:10 +0000
+++ b/mysql-test/suite/opt_trace/r/subquery_no_prot.result	2012-01-26 13:09:59 +0000
@@ -1269,6 +1269,7 @@ field4,field5,field6	{
                     "range_scan_alternatives": [
                       {
                         "index": "col_int_key",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "NULL < col_int_key < 214",
                           "214 < col_int_key"
@@ -1985,6 +1986,7 @@ table2.`col_int_nokey` <> any ( select 5
                     "range_scan_alternatives": [
                       {
                         "index": "PRIMARY",
+                        "index_dives_for_eq_ranges": true,
                         "ranges": [
                           "18 <= pk <= 18",
                           "192 <= pk <= 192"

=== added file 'mysql-test/suite/opt_trace/t/eq_range_statistics.test'
--- a/mysql-test/suite/opt_trace/t/eq_range_statistics.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/opt_trace/t/eq_range_statistics.test	2012-01-26 13:09:59 +0000
@@ -0,0 +1,83 @@
+# Tests for eq_range_index_dive_limit variable: 
+#   test that the number of ranges are counted correctly and 
+#   index statistics kicks in when more than 
+#   eq_range_index_dive_limit equality ranges are in the 
+#   predicate
+
+--source include/have_optimizer_trace.inc
+
+SET optimizer_trace_max_mem_size=1048576; # 1MB
+SET optimizer_trace="enabled=on,end_marker=on,one_line=off";
+
+SET eq_range_index_dive_limit=default;
+SELECT @@eq_range_index_dive_limit;
+
+CREATE TABLE t1 (
+       a INT, 
+       b INT, 
+       KEY (a,b)
+);
+
+INSERT INTO t1 VALUES (1,1), (2,2), (3,3);
+INSERT INTO t1 VALUES (4,1), (4,2), (4,3);
+INSERT INTO t1 VALUES (5,1), (5,2), (5,3);
+
+SHOW INDEX FROM t1;
+ANALYZE TABLE t1;      
+SHOW INDEX FROM t1;
+
+--echo #####
+--echo # Apply knowledge about the statistics (each index value for 
+--echo # the first key part has an estimate of 2 rows) to ensure that 
+--echo # index statistics kicks in correctly.
+--echo #####
+
+--echo # Index dives are done, giving correct estimate of 3 records
+EXPLAIN SELECT * FROM t1 WHERE a IN (1,2,3);
+
+SET eq_range_index_dive_limit=3;
+SELECT @@eq_range_index_dive_limit;
+
+--echo # Index statistics kicks in, giving incorrect estimate of 3x2=6 records
+EXPLAIN SELECT * FROM t1 WHERE a IN (1,2,3);
+
+--echo #####
+--echo # Below: A number of tests to verify that the number of equality ranges
+--echo # are counted correctly
+--echo #####
+
+# The limit is 3
+
+--echo
+--echo # 2 equality ranges: should not use index statistics
+EXPLAIN SELECT * FROM t1 WHERE a=5 OR a>10 OR a IN (1);
+SELECT * FROM information_schema.OPTIMIZER_TRACE;
+
+--echo
+--echo # 3 equality ranges: should use index statistics
+EXPLAIN SELECT * FROM t1 WHERE a=5 OR a>10 OR a IN (1,2);
+SELECT * FROM information_schema.OPTIMIZER_TRACE;
+
+--echo
+--echo # 3 equality ranges: should use index statistics
+EXPLAIN SELECT * FROM t1 WHERE a=5 AND (b=2 OR b=3 OR b=4);
+SELECT * FROM information_schema.OPTIMIZER_TRACE;
+
+--echo
+--echo # 2 equality ranges: should not use index statistics
+EXPLAIN SELECT * FROM t1 WHERE a=5 AND (b=2 OR b=3 OR b>4);
+SELECT * FROM information_schema.OPTIMIZER_TRACE;
+
+--echo
+--echo # 2 equality ranges: should not use index statistics
+EXPLAIN SELECT * FROM t1 WHERE a=5 AND (b=2 OR b=3 OR b IS NULL);
+SELECT * FROM information_schema.OPTIMIZER_TRACE;
+
+--echo
+--echo # 0 equality ranges: should not use index statistics
+EXPLAIN SELECT * FROM t1 WHERE a>5 AND (b=2 OR b=3 OR b=4);
+SELECT * FROM information_schema.OPTIMIZER_TRACE;
+
+DROP TABLE t1;
+
+SET eq_range_index_dive_limit=default;

=== added file 'mysql-test/suite/sys_vars/r/eq_range_index_dive_limit_basic.result'
--- a/mysql-test/suite/sys_vars/r/eq_range_index_dive_limit_basic.result	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/sys_vars/r/eq_range_index_dive_limit_basic.result	2012-01-26 13:09:59 +0000
@@ -0,0 +1,96 @@
+SET @start_global_value = @@global.eq_range_index_dive_limit;
+SELECT @start_global_value;
+@start_global_value
+10
+select @@global.eq_range_index_dive_limit;
+@@global.eq_range_index_dive_limit
+10
+select @@session.eq_range_index_dive_limit;
+@@session.eq_range_index_dive_limit
+10
+show global variables like 'eq_range_index_dive_limit';
+Variable_name	Value
+eq_range_index_dive_limit	10
+show session variables like 'eq_range_index_dive_limit';
+Variable_name	Value
+eq_range_index_dive_limit	10
+select * 
+from information_schema.global_variables 
+where variable_name='eq_range_index_dive_limit';
+VARIABLE_NAME	VARIABLE_VALUE
+EQ_RANGE_INDEX_DIVE_LIMIT	10
+select * 
+from information_schema.session_variables 
+where variable_name='eq_range_index_dive_limit';
+VARIABLE_NAME	VARIABLE_VALUE
+EQ_RANGE_INDEX_DIVE_LIMIT	10
+set global eq_range_index_dive_limit=10;
+select @@global.eq_range_index_dive_limit;
+@@global.eq_range_index_dive_limit
+10
+set session eq_range_index_dive_limit=10;
+select @@session.eq_range_index_dive_limit;
+@@session.eq_range_index_dive_limit
+10
+set global eq_range_index_dive_limit=0;
+select @@global.eq_range_index_dive_limit;
+@@global.eq_range_index_dive_limit
+0
+set session eq_range_index_dive_limit=0;
+select @@session.eq_range_index_dive_limit;
+@@session.eq_range_index_dive_limit
+0
+set global eq_range_index_dive_limit=4294967295;
+select @@global.eq_range_index_dive_limit;
+@@global.eq_range_index_dive_limit
+4294967295
+set session eq_range_index_dive_limit=4294967295;
+select @@session.eq_range_index_dive_limit;
+@@session.eq_range_index_dive_limit
+4294967295
+set session eq_range_index_dive_limit=default;
+select @@session.eq_range_index_dive_limit;
+@@session.eq_range_index_dive_limit
+4294967295
+set global eq_range_index_dive_limit=default;
+select @@global.eq_range_index_dive_limit;
+@@global.eq_range_index_dive_limit
+10
+set session eq_range_index_dive_limit=default;
+select @@session.eq_range_index_dive_limit;
+@@session.eq_range_index_dive_limit
+10
+set global eq_range_index_dive_limit=-1;
+Warnings:
+Warning	1292	Truncated incorrect eq_range_index_dive_limit value: '-1'
+select @@global.eq_range_index_dive_limit;
+@@global.eq_range_index_dive_limit
+0
+set session eq_range_index_dive_limit=-1;
+Warnings:
+Warning	1292	Truncated incorrect eq_range_index_dive_limit value: '-1'
+select @@session.eq_range_index_dive_limit;
+@@session.eq_range_index_dive_limit
+0
+set global eq_range_index_dive_limit=4294967296;
+Warnings:
+Warning	1292	Truncated incorrect eq_range_index_dive_limit value: '4294967296'
+select @@global.eq_range_index_dive_limit;
+@@global.eq_range_index_dive_limit
+4294967295
+set session eq_range_index_dive_limit=4294967296;
+Warnings:
+Warning	1292	Truncated incorrect eq_range_index_dive_limit value: '4294967296'
+select @@session.eq_range_index_dive_limit;
+@@session.eq_range_index_dive_limit
+4294967295
+set global eq_range_index_dive_limit=1.1;
+ERROR 42000: Incorrect argument type to variable 'eq_range_index_dive_limit'
+set global eq_range_index_dive_limit=1e1;
+ERROR 42000: Incorrect argument type to variable 'eq_range_index_dive_limit'
+set global eq_range_index_dive_limit="foobar";
+ERROR 42000: Incorrect argument type to variable 'eq_range_index_dive_limit'
+SET @@global.eq_range_index_dive_limit = @start_global_value;
+SELECT @@global.eq_range_index_dive_limit;
+@@global.eq_range_index_dive_limit
+10

=== added file 'mysql-test/suite/sys_vars/t/eq_range_index_dive_limit_basic.test'
--- a/mysql-test/suite/sys_vars/t/eq_range_index_dive_limit_basic.test	1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/sys_vars/t/eq_range_index_dive_limit_basic.test	2012-01-26 13:09:59 +0000
@@ -0,0 +1,71 @@
+SET @start_global_value = @@global.eq_range_index_dive_limit;
+SELECT @start_global_value;
+
+#
+# exists as global and session
+#
+select @@global.eq_range_index_dive_limit;
+select @@session.eq_range_index_dive_limit;
+show global variables like 'eq_range_index_dive_limit';
+show session variables like 'eq_range_index_dive_limit';
+
+select * 
+from information_schema.global_variables 
+where variable_name='eq_range_index_dive_limit';
+
+select * 
+from information_schema.session_variables 
+where variable_name='eq_range_index_dive_limit';
+
+#
+# show that it's writable
+#
+set global eq_range_index_dive_limit=10;
+select @@global.eq_range_index_dive_limit;
+set session eq_range_index_dive_limit=10;
+select @@session.eq_range_index_dive_limit;
+
+set global eq_range_index_dive_limit=0;
+select @@global.eq_range_index_dive_limit;
+set session eq_range_index_dive_limit=0;
+select @@session.eq_range_index_dive_limit;
+
+set global eq_range_index_dive_limit=4294967295;
+select @@global.eq_range_index_dive_limit;
+set session eq_range_index_dive_limit=4294967295;
+select @@session.eq_range_index_dive_limit;
+
+set session eq_range_index_dive_limit=default;
+select @@session.eq_range_index_dive_limit;
+set global eq_range_index_dive_limit=default;
+select @@global.eq_range_index_dive_limit;
+set session eq_range_index_dive_limit=default;
+select @@session.eq_range_index_dive_limit;
+
+#
+# Incorrect assignments
+#
+
+# Allowed value range: (0, UINT_MAX32)
+# Value lower than allowed range
+set global eq_range_index_dive_limit=-1;
+select @@global.eq_range_index_dive_limit;
+set session eq_range_index_dive_limit=-1;
+select @@session.eq_range_index_dive_limit;
+
+# Value higher than allowed range
+set global eq_range_index_dive_limit=4294967296;
+select @@global.eq_range_index_dive_limit;
+set session eq_range_index_dive_limit=4294967296;
+select @@session.eq_range_index_dive_limit;
+
+# Incompatible value types
+--error ER_WRONG_TYPE_FOR_VAR
+set global eq_range_index_dive_limit=1.1;
+--error ER_WRONG_TYPE_FOR_VAR
+set global eq_range_index_dive_limit=1e1;
+--error ER_WRONG_TYPE_FOR_VAR
+set global eq_range_index_dive_limit="foobar";
+
+SET @@global.eq_range_index_dive_limit = @start_global_value;
+SELECT @@global.eq_range_index_dive_limit;

=== modified file 'sql/handler.cc'
--- a/sql/handler.cc	2012-01-06 09:03:53 +0000
+++ b/sql/handler.cc	2012-01-26 13:09:59 +0000
@@ -37,6 +37,7 @@
 #include "probes_mysql.h"
 #include <mysql/psi/mysql_table.h>
 #include "debug_sync.h"         // DEBUG_SYNC
+#include <my_bit.h>
 
 #ifdef WITH_PARTITION_STORAGE_ENGINE
 #include "ha_partition.h"
@@ -4583,8 +4584,35 @@ handler::multi_range_read_info_const(uin
       min_endp= range.start_key.length? &range.start_key : NULL;
       max_endp= range.end_key.length? &range.end_key : NULL;
     }
-    if ((range.range_flag & UNIQUE_RANGE) && !(range.range_flag & NULL_RANGE))
+    /*
+      Get the number of rows in the range. This is done by calling
+      records_in_range() unless:
+
+        1) The range is an equality range and the index is unique.
+           There cannot be more than one matching row, so 1 is
+           assumed. Note that it is possible that the correct number
+           is actually 0, so the row estimate may be too high in this
+           case. Also note: ranges of the form "x IS NULL" may have more
+           than 1 mathing row so records_in_range() is called for these.
+        2) a) The range is an equality range but the index is either 
+              not unique or all of the keyparts are not used. 
+           b) The user has requested that index statistics should be used
+              for equality ranges to avoid the incurred overhead of 
+              index dives in records_in_range().
+           c) Index statistics is available.
+           Ranges of the form "x IS NULL" will not use index statistics 
+           because the number of rows with this value are likely to be 
+           very different than the values in the index statistics.
+    */
+    const int keyparts_used= my_count_bits(range.start_key.keypart_map);
+    if ((range.range_flag & UNIQUE_RANGE) &&                        // 1)
+        !(range.range_flag & NULL_RANGE))
       rows= 1; /* there can be at most one row */
+    else if ((range.range_flag & EQ_RANGE) &&                       // 2a)
+             (range.range_flag & USE_INDEX_STATISTICS) &&           // 2b)
+             table->key_info[keyno].rec_per_key[keyparts_used-1] && // 2c)
+             !(range.range_flag & NULL_RANGE))
+      rows= table->key_info[keyno].rec_per_key[keyparts_used-1];
     else
     {
       if (HA_POS_ERROR == (rows= this->records_in_range(keyno, min_endp, 

=== modified file 'sql/opt_range.cc'
--- a/sql/opt_range.cc	2012-01-12 13:22:52 +0000
+++ b/sql/opt_range.cc	2012-01-26 13:09:59 +0000
@@ -213,7 +213,7 @@ class RANGE_OPT_PARAM;
         +---+       +---+   +---+       +---+
 
   In this tree,
-    * node0->prev == node7->next == NULL
+    * node1->prev == node7->next == NULL
     * node1->left == node1->right ==
       node3->left == ... node7->right == &null_element
 
@@ -789,6 +789,12 @@ public:
   /* Number of SEL_ARG objects allocated by SEL_ARG::clone_tree operations */
   uint alloced_sel_args; 
   bool force_default_mrr;
+  /** 
+    Whether index statistics or index dives should be used when
+    estimating the number of rows in an equality range. If true, index
+    statistics is used for these indexes.
+  */
+  bool use_index_statistics;
 };
 
 class PARAM : public RANGE_OPT_PARAM
@@ -897,6 +903,8 @@ bool get_quick_keys(PARAM *param,QUICK_R
                     SEL_ARG *key_tree, uchar *min_key,uint min_key_flag,
                     uchar *max_key,uint max_key_flag);
 static bool eq_tree(SEL_ARG* a,SEL_ARG *b);
+static bool eq_ranges_exceeds_limit(SEL_ARG *keypart_root, uint* count, 
+                                    uint limit);
 
 static SEL_ARG null_element(SEL_ARG::IMPOSSIBLE);
 static bool null_part_in_key(KEY_PART *key_part, const uchar *key,
@@ -5446,6 +5454,7 @@ static TRP_RANGE *get_key_scans_params(P
       if (found_records != HA_POS_ERROR &&
           param->thd->opt_trace.is_started())
       {
+        trace_idx.add("index_dives_for_eq_ranges", !param->use_index_statistics);
         Opt_trace_array trace_range(&param->thd->opt_trace, "ranges");
 
         const KEY &cur_key= param->table->key_info[keynr];
@@ -8359,6 +8368,12 @@ typedef struct st_range_seq_entry 
   
   /* Number of key parts */
   uint min_key_parts, max_key_parts;
+  /**
+    Pointer into the R-B tree for this keypart. It points to the
+    currently active range for the keypart, so calling next on it will
+    get to the next range. sel_arg_range_seq_next() uses this to avoid
+    reparsing the R-B range trees each time a new range is fetched.
+  */
   SEL_ARG *key_tree;
 } RANGE_SEQ_ENTRY;
 
@@ -8373,9 +8388,50 @@ typedef struct st_sel_arg_range_seq
   PARAM *param;
   SEL_ARG *start; /* Root node of the traversed SEL_ARG* graph */
   
+  /**
+    Stack of ranges for the curr_kp first keyparts. Used by
+    sel_arg_range_seq_next() so that if the next range is equal to the
+    previous one for the first x keyparts, stack[x-1] can be
+    accumulated with the new range in keyparts > x to quickly form
+    the next range to return.
+
+    Notation used below: "x:y" means a range where
+    "column_in_keypart_0=x" and "column_in_keypart_1=y". For
+    simplicity, only equality (no BETWEEN, < etc) is considered in the
+    example but the same principle applies to other range predicate
+    operators too.
+
+    Consider a query with these range predicates: 
+      (kp0=1 and kp1=2 and kp2=3) or
+      (kp0=1 and kp1=2 and kp2=4) or
+      (kp0=1 and kp1=3 and kp2=5) or
+      (kp0=1 and kp1=3 and kp2=6)
+
+    1) sel_arg_range_seq_next() is called the first time
+       - traverse the R-B tree (see SEL_ARG) to find the first range
+       - returns range "1:2:3"
+       - values in stack after this: stack[1, 1:2, 1:2:3]
+    2) sel_arg_range_seq_next() is called second time
+       - keypart 2 has another range, so the next range in 
+         keypart 2 is appended to stack[1] and saved 
+         in stack[2]
+       - returns range "1:2:4"
+       - values in stack after this: stack[1, 1:2, 1:2:4]
+    3) sel_arg_range_seq_next() is called the third time
+       - no more ranges in keypart 2, but keypart 1 has 
+         another range, so the next range in keypart 1 is
+         appended to stack[0] and saved in stack[1]. The first
+         range in keypart 2 is then appended to stack[1] and
+         saved in stack[2]
+       - returns range "1:3:5"
+       - values in stack after this: stack[1, 1:3, 1:3:5]
+    4) sel_arg_range_seq_next() is called the fourth time
+       - keypart 2 has another range, see 2)
+       - returns range "1:3:6"
+       - values in stack after this: stack[1, 1:3, 1:3:6]
+   */
   RANGE_SEQ_ENTRY stack[MAX_REF_PARTS];
-  int i; /* Index of last used element in the above array */
-  
+  int curr_kp; /* Index of last used element in the above array */
   bool at_start; /* TRUE <=> The traversal has just started */
 } SEL_ARG_RANGE_SEQ;
 
@@ -8405,16 +8461,16 @@ range_seq_t sel_arg_range_seq_init(void 
   seq->stack[0].max_key= (uchar*)seq->param->max_key;
   seq->stack[0].max_key_flag= 0;
   seq->stack[0].max_key_parts= 0;
-  seq->i= 0;
+  seq->curr_kp= 0;
   return init_param;
 }
 
 
-static void step_down_to(SEL_ARG_RANGE_SEQ *arg, SEL_ARG *key_tree)
+static void push_range_to_stack(SEL_ARG_RANGE_SEQ *arg, SEL_ARG *key_tree)
 {
 
-  RANGE_SEQ_ENTRY *cur= &arg->stack[arg->i+1];
-  RANGE_SEQ_ENTRY *prev= &arg->stack[arg->i];
+  RANGE_SEQ_ENTRY *cur= &arg->stack[arg->curr_kp+1];
+  RANGE_SEQ_ENTRY *prev= &arg->stack[arg->curr_kp];
   
   cur->key_tree= key_tree;
   cur->min_key= prev->min_key;
@@ -8438,12 +8494,13 @@ static void step_down_to(SEL_ARG_RANGE_S
 
   if (key_tree->is_null_interval())
     cur->min_key_flag |= NULL_RANGE;
-  (arg->i)++;
+  (arg->curr_kp)++;
 }
 
 
 /*
   Range sequence interface, SEL_ARG* implementation: get the next interval
+  in the R-B tree
   
   SYNOPSIS
     sel_arg_range_seq_next()
@@ -8473,67 +8530,118 @@ uint sel_arg_range_seq_next(range_seq_t 
 
   if (seq->at_start)
   {
+    /*
+      This is the first time sel_arg_range_seq_next is called.
+      seq->start points to the root of the R-B tree for the first
+      keypart
+    */
     key_tree= seq->start;
     seq->at_start= FALSE;
-    goto walk_up_n_right;
-  }
 
-  key_tree= seq->stack[seq->i].key_tree;
-  /* Ok, we're at some "full tuple" position in the tree */
- 
-  /* Step down if we can */
-  if (key_tree->next)
-  {
-    DBUG_ASSERT(key_tree->next != &null_element);
-    //step down; (update the tuple, we'll step right and stay there)
-    seq->i--;
-    step_down_to(seq, key_tree->next);
-    key_tree= key_tree->next;
-    seq->param->is_ror_scan= FALSE;
-    goto walk_right_n_up;
+    /*
+      Move to the first range for the first keypart. Save this range
+      in seq->stack[0] and carry on to ranges in the next keypart if
+      any
+    */
+    key_tree= key_tree->first();
+    push_range_to_stack(seq, key_tree);
   }
-
-  /* Ok, can't step down, walk left until we can step down */
-  while (1)
+  else
   {
-    if (seq->i == 1) // can't step left
-      return 1;
+    /*
+      This is not the first time sel_arg_range_seq_next is called, so
+      seq->stack is populated with the range the last call to this
+      function found. seq->stack[current_keypart].key_tree points to a
+      leaf in the R-B tree of the last keypart that was part of the
+      former range. This is the starting point for finding the next
+      range. @see SEL_ARG_RANGE_SEQ::stack
+    */
+    key_tree= seq->stack[seq->curr_kp].key_tree;
 
-    /* Step left */
-    seq->i--;
-    key_tree= seq->stack[seq->i].key_tree;
-
-    /* Step down if we can */
-    if (key_tree->next)
-    {
-      DBUG_ASSERT(key_tree->next != &null_element);
-      // Step down; update the tuple
-      seq->i--;
-      step_down_to(seq, key_tree->next);
-      key_tree= key_tree->next;
-      seq->param->is_ror_scan= FALSE;
-      break;
+    // See if there are more ranges in this or any of the previous keyparts
+    while (true)
+    {
+      if (key_tree->next)
+      {
+        /* This keypart has more ranges */
+        DBUG_ASSERT(key_tree->next != &null_element);
+        key_tree= key_tree->next;
+
+        seq->curr_kp--;
+        /*
+          save the next range for this keypart and carry on to ranges in
+          the next keypart if any
+        */
+        push_range_to_stack(seq, key_tree);
+        seq->param->is_ror_scan= FALSE;
+        break;
+      }
+
+      if (seq->curr_kp == 1) 
+      {
+        // There are no more ranges for the first keypart: we're done
+        return 1;
+      }
+
+      /* 
+         No more ranges for the current keypart. Step back to the
+         previous keypart
+      */
+      seq->curr_kp--;
+      key_tree= seq->stack[seq->curr_kp].key_tree;
     }
   }
 
   /*
-    Ok, we've stepped down from the path to previous tuple.
-    Walk right-up while we can
+    Add range info for the next keypart if
+      1) there is a range predicate for a later keypart
+      2) the range predicate is for the next keypart in the index: a
+         range predicate on keypartX+1 can only be used if there is a
+         range predicate on keypartX.
+      3) the range predicate on the next keypart is usable
   */
-walk_right_n_up:
-  while (key_tree->next_key_part && key_tree->next_key_part != &null_element && 
-         key_tree->next_key_part->part == key_tree->part + 1 &&
-         key_tree->next_key_part->type == SEL_ARG::KEY_RANGE)
-  {
-    {
-      RANGE_SEQ_ENTRY *cur= &seq->stack[seq->i];
-      uint min_key_length= cur->min_key - seq->param->min_key;
-      uint max_key_length= cur->max_key - seq->param->max_key;
-      uint len= cur->min_key - cur[-1].min_key;
-      if (!(min_key_length == max_key_length &&
-            !memcmp(cur[-1].min_key, cur[-1].max_key, len) &&
-            !key_tree->min_flag && !key_tree->max_flag))
+  while (key_tree->next_key_part &&                              // 1)
+         key_tree->next_key_part != &null_element &&             // 1)
+         key_tree->next_key_part->part == key_tree->part + 1 &&  // 2)
+         key_tree->next_key_part->type == SEL_ARG::KEY_RANGE)    // 3)
+  {
+    {
+      RANGE_SEQ_ENTRY *cur= &seq->stack[seq->curr_kp];
+      const uint min_key_length= cur->min_key - seq->param->min_key;
+      const uint max_key_length= cur->max_key - seq->param->max_key;
+      const uint len= cur->min_key - cur[-1].min_key;
+      
+      if ((min_key_length != max_key_length ||
+           memcmp(cur[-1].min_key, cur[-1].max_key, len) ||
+           key_tree->min_flag || key_tree->max_flag))
       {
+        /* 
+          The range predicate up to and including the one in key_tree
+          is usable by range access but does not allow subranges made
+          up from predicates in later keyparts. This may e.g. be
+          because the predicate operator is "<". Since there are range
+          predicates on more keyparts, we use those to more closely
+          specify the start and stop locations for the range. Example:
+
+                "SELECT * FROM t1 WHERE a >= 2 AND b >= 3":
+
+                t1 content:
+                -----------
+                1 1
+                2 1     <- 1)
+                2 2
+                2 3     <- 2)
+                2 4
+                3 1
+                3 2
+                3 3
+
+          The predicate cannot be translated into something like
+             "(a=2 and b>=3) or (a=3 and b>=3) or ..."
+          I.e., it cannot be divided into subranges, but by storing
+          min/max key below we can at least start the scan from 2)
+          instead of 1)
+        */
         SEL_ARG *store_key_part= key_tree->next_key_part;
         seq->param->is_ror_scan= FALSE;
         if (!key_tree->min_flag)
@@ -8553,31 +8661,27 @@ walk_right_n_up:
     }
   
     /*
-      Ok, current atomic interval is in form "t.field=const" and there is
-      next_key_part interval. Step right, and walk up from there.
-    */
-    key_tree= key_tree->next_key_part;
-
-walk_up_n_right:
-    while (key_tree->prev)
-    {
-      DBUG_ASSERT(key_tree->prev != &null_element);
-      /* Step up */
-      key_tree= key_tree->prev;
-    }
-    step_down_to(seq, key_tree);
+      There are usable range predicates for the next keypart and the
+      range predicate for the current keypart allows us to make use of
+      them. Move to the first range predicate for the next keypart.
+      Push this range predicate to seq->stack and move on to the next
+      keypart (if any). @see SEL_ARG_RANGE_SEQ::stack
+    */
+    key_tree= key_tree->next_key_part->first();
+    push_range_to_stack(seq, key_tree);
   }
 
-  /* Ok got a tuple */
-  RANGE_SEQ_ENTRY *cur= &seq->stack[seq->i];
-  uint min_key_length= cur->min_key - seq->param->min_key;
+  // We now have a full range predicate in seq->stack[seq->curr_kp]
+  RANGE_SEQ_ENTRY *cur= &seq->stack[seq->curr_kp];
+  PARAM *param= seq->param;
+  uint min_key_length= cur->min_key - param->min_key;
 
   if (cur->min_key_flag & GEOM_FLAG)
   {
     range->range_flag= cur->min_key_flag;
 
     /* Here minimum contains also function code bits, and maximum is +inf */
-    range->start_key.key=    seq->param->min_key;
+    range->start_key.key=    param->min_key;
     range->start_key.length= min_key_length;
     range->start_key.flag=  (ha_rkey_function) (cur->min_key_flag ^ GEOM_FLAG);
   }
@@ -8585,27 +8689,51 @@ walk_up_n_right:
   {
     range->range_flag= cur->min_key_flag | cur->max_key_flag;
     
-    range->start_key.key=    seq->param->min_key;
-    range->start_key.length= cur->min_key - seq->param->min_key;
+    range->start_key.key=    param->min_key;
+    range->start_key.length= cur->min_key - param->min_key;
     range->start_key.keypart_map= make_prev_keypart_map(cur->min_key_parts);
     range->start_key.flag= (cur->min_key_flag & NEAR_MIN ? HA_READ_AFTER_KEY : 
                                                            HA_READ_KEY_EXACT);
 
-    range->end_key.key=    seq->param->max_key;
-    range->end_key.length= cur->max_key - seq->param->max_key;
+    range->end_key.key=    param->max_key;
+    range->end_key.length= cur->max_key - param->max_key;
     range->end_key.keypart_map= make_prev_keypart_map(cur->max_key_parts);
     range->end_key.flag= (cur->max_key_flag & NEAR_MAX ? HA_READ_BEFORE_KEY : 
                                                          HA_READ_AFTER_KEY);
 
-    if (!(cur->min_key_flag & ~NULL_RANGE) && !cur->max_key_flag &&
-        (uint)key_tree->part+1 == seq->param->table->key_info[seq->real_keyno].key_parts &&
-        (seq->param->table->key_info[seq->real_keyno].flags & HA_NOSAME) ==
-        HA_NOSAME &&
-        range->start_key.length == range->end_key.length &&
-        !memcmp(seq->param->min_key,seq->param->max_key,range->start_key.length))
-      range->range_flag= UNIQUE_RANGE | (cur->min_key_flag & NULL_RANGE);
-      
-    if (seq->param->is_ror_scan)
+    /* 
+      This is an equality range (keypart_0=X and ... and keypart_n=Z) if 
+        1) There are no flags indicating open range (e.g., 
+           "keypart_x > y") or GIS.
+        2) The lower bound and the upper bound of the range has the
+           same value (min_key == max_key).
+     */
+    if (!(cur->min_key_flag & (NO_MIN_RANGE | NO_MAX_RANGE | 
+                               NEAR_MIN | NEAR_MAX | GEOM_FLAG)) &&       // 1)
+        !(cur->max_key_flag & (NO_MIN_RANGE | NO_MAX_RANGE | 
+                               NEAR_MIN | NEAR_MAX | GEOM_FLAG)) &&       // 1)
+        range->start_key.length == range->end_key.length &&               // 2)
+        !memcmp(param->min_key, param->max_key, range->start_key.length)) // 2)
+    {
+      range->range_flag= EQ_RANGE;
+      /*
+        Use statistics instead of index dives for estimates of rows in
+        this range if the user requested it
+      */
+      if (param->use_index_statistics)
+        range->range_flag|= USE_INDEX_STATISTICS;
+
+      /* 
+        An equality range is a unique range (0 or 1 rows in the range)
+        if the index is unique (1) and all keyparts are used (2).
+      */
+      if (param->table->key_info[seq->real_keyno].flags & HA_NOSAME &&    // 1)
+          (uint)key_tree->part+1 == param->table->
+                                    key_info[seq->real_keyno].key_parts)  // 2)
+        range->range_flag|= UNIQUE_RANGE | (cur->min_key_flag & NULL_RANGE);
+    }
+
+    if (param->is_ror_scan)
     {
       /*
         If we get here, the condition on the key was converted to form
@@ -8616,11 +8744,15 @@ walk_up_n_right:
           uncovered "tail" of KeyX parts is either empty or is identical to
           first members of clustered primary key.
       */
-      if (!(!(cur->min_key_flag & ~NULL_RANGE) && !cur->max_key_flag &&
+      if (!(!(cur->min_key_flag & (NO_MIN_RANGE | NO_MAX_RANGE | 
+                                   NEAR_MIN | NEAR_MAX | GEOM_FLAG)) && 
+            !(cur->max_key_flag & (NO_MIN_RANGE | NO_MAX_RANGE | 
+                                   NEAR_MIN | NEAR_MAX | GEOM_FLAG)) && 
             (range->start_key.length == range->end_key.length) &&
-            !memcmp(range->start_key.key, range->end_key.key, range->start_key.length) &&
-            is_key_scan_ror(seq->param, seq->real_keyno, key_tree->part + 1)))
-        seq->param->is_ror_scan= FALSE;
+            !memcmp(range->start_key.key, range->end_key.key, 
+                    range->start_key.length) &&
+            is_key_scan_ror(param, seq->real_keyno, key_tree->part + 1)))
+        param->is_ror_scan= FALSE;
     }
   }
 
@@ -8687,6 +8819,16 @@ ha_rows check_quick_select(PARAM *param,
   param->range_count=0;
   param->max_key_part=0;
 
+  /* 
+    If there are more equality ranges than specified by the
+    eq_range_index_dive_limit variable we switches from using index
+    dives to use statistics.
+  */ 
+  uint range_count= 0;
+  param->use_index_statistics= 
+    eq_ranges_exceeds_limit(tree, &range_count, 
+                            param->thd->variables.eq_range_index_dive_limit);
+
   param->is_ror_scan= TRUE;
   if (file->index_flags(keynr, 0, TRUE) & HA_KEY_SCAN_NOT_ROR)
     param->is_ror_scan= FALSE;
@@ -10923,10 +11065,10 @@ get_best_group_min_max(PARAM *param, SEL
                                                    cur_index_tree, TRUE,
                                                    &mrr_flags, &mrr_bufsize,
                                                    &dummy_cost);
-
 #ifdef OPTIMIZER_TRACE
       if (unlikely(cur_index_tree && trace->is_started()))
       {
+        trace_idx.add("index_dives_for_eq_ranges", !param->use_index_statistics);
         Opt_trace_array trace_range(trace, "ranges");
 
         const KEY_PART_INFO *key_part= cur_index_info->key_part;
@@ -12661,6 +12803,63 @@ void QUICK_GROUP_MIN_MAX_SELECT::add_key
 }
 
 
+
+/**
+  Traverse the R-B range tree for this and later keyparts to see if
+  there are at least as many equality ranges as defined by the limit.
+
+  @param keypart_root   The root of a R-B tree of ranges for a given keypart.
+  @param count[in,out]  The number of equality ranges found so far
+  @param limit          The number of ranges 
+
+  @retval true if 'limit' or more equality ranges have been found in the 
+          range R-B trees
+  @retval false otherwise         
+
+*/
+static bool eq_ranges_exceeds_limit(SEL_ARG *keypart_root, uint* count, uint limit)
+{
+  if (limit == UINT_MAX32)
+    return false;
+  if (limit == 0)
+    return true;
+  
+  for(SEL_ARG *keypart_range= keypart_root->first(); 
+      keypart_range; keypart_range= keypart_range->next)
+  {
+    /*
+      This is an equality range predicate and should be counted if:
+      1) the range for this keypart does not have a min/max flag 
+         (which indicates <, <= etc), and
+      2) the lower and upper range boundaries have the same value
+         (it's not a "x BETWEEN a AND b")
+      
+      Note, however, that if this is an "x IS NULL" condition we don't
+      count it because the number of NULL-values is likely to be off
+      the index statistics we plan to use.
+    */
+    if (!keypart_range->min_flag && !keypart_range->max_flag && // 1)
+        !keypart_range->cmp_max_to_min(keypart_range) &&        // 2)
+        !keypart_range->is_null_interval())                     // "x IS NULL"
+    {
+      /* 
+         Count predicates in the next keypart, but only if that keypart
+         is the next in the index. 
+      */
+      if (keypart_range->next_key_part && 
+          keypart_range->next_key_part->part == keypart_range->part + 1)
+        eq_ranges_exceeds_limit(keypart_range->next_key_part, count, limit);
+      else
+        // We've found a path of equlity predicates down to a keypart leaf
+        (*count)++; 
+
+      if (*count >= limit)
+        return true;
+    }
+  }
+  return false;
+}
+
 #ifndef DBUG_OFF
 
 static void print_sel_tree(PARAM *param, SEL_TREE *tree, key_map *tree_map,

=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h	2012-01-02 05:52:38 +0000
+++ b/sql/sql_class.h	2012-01-26 13:09:59 +0000
@@ -455,6 +455,7 @@ typedef struct system_variables
   ha_rows max_join_size;
   ulong auto_increment_increment, auto_increment_offset;
   ulong bulk_insert_buff_size;
+  uint  eq_range_index_dive_limit;
   ulong join_buff_size;
   ulong lock_wait_timeout;
   ulong max_allowed_packet;

=== modified file 'sql/sys_vars.cc'
--- a/sql/sys_vars.cc	2011-12-19 18:05:58 +0000
+++ b/sql/sys_vars.cc	2012-01-26 13:09:59 +0000
@@ -2110,6 +2110,14 @@ static Sys_var_ulong Sys_div_precincreme
        SESSION_VAR(div_precincrement), CMD_LINE(REQUIRED_ARG),
        VALID_RANGE(0, DECIMAL_MAX_SCALE), DEFAULT(4), BLOCK_SIZE(1));
 
+static Sys_var_uint Sys_eq_range_index_dive_limit(
+       "eq_range_index_dive_limit",
+       "The optimizer will use existing index statistics instead of "
+       "doing index dives for equality ranges if the number of equality "
+       "ranges is larger than or equal to this number.",
+       SESSION_VAR(eq_range_index_dive_limit), CMD_LINE(REQUIRED_ARG),
+       VALID_RANGE(0, UINT_MAX32), DEFAULT(10), BLOCK_SIZE(1));
+
 static Sys_var_ulong Sys_range_alloc_block_size(
        "range_alloc_block_size",
        "Allocation block size for storing ranges during optimization",

No bundle (reason: useless for push emails).
Thread
bzr push into mysql-trunk branch (jorgen.loland:3530 to 3531) WL#5957Jorgen Loland30 Jan