List:Commits« Previous MessageNext Message »
From:Martin Hansson Date:June 24 2010 2:39pm
Subject:bzr commit into mysql-next-mr-bugfixing branch (martin.hansson:3270)
Bug#26106
View as plain text  
#At file:///data0/martin/bzr/bug26106/n-mr-bf/ based on revid:martin.hansson@stripped

 3270 Martin Hansson	2010-06-24
      Bug#26106: Wrong plan may be chosen when there are several possible range and
      ref accesses
        
      For ref(const) access methods there are two kinds of statistics when a prefix
      of a key is matched: range access estimate and table statistics, which are not
      necessarily consistent with each other. This may make the optimizer choose a
      key which matches fewer key parts than some other key, which is obviously not
      correct.
        
      Fixed by giving preferrence to the keys that match the most parts, and
      choosing the best one among these.

    modified:
      mysql-test/r/range.result
      mysql-test/t/range.test
      sql/sql_select.cc
=== modified file 'mysql-test/r/range.result'
--- a/mysql-test/r/range.result	2010-05-11 08:27:53 +0000
+++ b/mysql-test/r/range.result	2010-06-24 14:39:23 +0000
@@ -1698,3 +1698,64 @@ Variable_name	Value
 Handler_read_key	4
 DROP TABLE t1, t2;
 End of 5.1 tests
+#
+# Bug #26106: Wrong plan may be chosen when there are several possible
+# range and ref accesses
+#
+CREATE TABLE t1( 
+a INT,
+b INT,
+KEY k ( a ),
+KEY l ( a, b )
+);
+INSERT INTO t1(a) VALUES (1);
+INSERT INTO t1 
+VALUES (2,3),(2,3),(2,3),(2,3),(2,3),(2,3),(2,3),(2,3),(2,3),(2,3);
+INSERT INTO t1 SELECT 3, 4 FROM t1 WHERE a = 2 AND b = 3;
+INSERT INTO t1 SELECT 4, 1 FROM t1 WHERE a = 2 AND b = 3;
+ANALYZE TABLE t1;
+Table	Op	Msg_type	Msg_text
+test.t1	analyze	status	OK
+INSERT INTO t1 VALUES (1, 2);
+INSERT INTO t1 SELECT a, b FROM t1 WHERE a=1 AND b=2;
+INSERT INTO t1 SELECT a, b FROM t1 WHERE a=1 AND b=2;
+INSERT INTO t1 SELECT a, b FROM t1 WHERE a=1 AND b=2;
+INSERT INTO t1 SELECT a, b FROM t1 WHERE a=1 AND b=2;
+INSERT INTO t1 SELECT a, b FROM t1 WHERE a=1 AND b=2;
+INSERT INTO t1 SELECT a, b FROM t1 WHERE a=1 AND b=2;
+INSERT INTO t1 SELECT a, b FROM t1 WHERE a=1 AND b=2;
+# This must use range over index l, not k.
+EXPLAIN SELECT * FROM t1 WHERE a = 1 AND b >= 2;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	range	k,l	l	10	NULL	138	Using where; Using index
+CREATE TABLE t2( 
+a INT, 
+b INT, 
+c INT,
+KEY k ( a ), 
+KEY l ( a, b ),
+KEY m ( b ), 
+KEY n ( a, c )
+);
+INSERT INTO t2(a) VALUES (1);
+INSERT INTO t2 
+VALUES (2,3,3),(2,3,3),(2,3,3),(2,3,3),(2,3,3),
+(2,3,3),(2,3,3),(2,3,3),(2,3,3),(2,3,3);
+INSERT INTO t2 SELECT 3, 4, 4 FROM t2 WHERE a = 2 AND b = 3;
+INSERT INTO t2 SELECT 4, 1, 1 FROM t2 WHERE a = 2 AND b = 3;
+ANALYZE TABLE t2;
+Table	Op	Msg_type	Msg_text
+test.t2	analyze	status	OK
+INSERT INTO t2 VALUES (1, 2, 2);
+INSERT INTO t2 SELECT a, b, c FROM t2 WHERE a=1 AND b=2;
+INSERT INTO t2 SELECT a, b, c FROM t2 WHERE a=1 AND b=2;
+INSERT INTO t2 SELECT a, b, c FROM t2 WHERE a=1 AND b=2;
+INSERT INTO t2 SELECT a, b, c FROM t2 WHERE a=1 AND b=2;
+INSERT INTO t2 SELECT a, b, c FROM t2 WHERE a=1 AND b=2;
+INSERT INTO t2 SELECT a, b, c FROM t2 WHERE a=1 AND b=2;
+INSERT INTO t2 VALUES (1, 1, 2);
+# This must use range over index l, not n.
+EXPLAIN SELECT * FROM t2 WHERE a = 1 AND b >= 2 AND c >= 2;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t2	ref	k,l,m,n	l	5	const	69	Using where
+DROP TABLE t1, t2;

=== modified file 'mysql-test/t/range.test'
--- a/mysql-test/t/range.test	2010-05-10 07:23:23 +0000
+++ b/mysql-test/t/range.test	2010-06-24 14:39:23 +0000
@@ -1355,3 +1355,62 @@ SHOW status LIKE 'handler_read_key';
 DROP TABLE t1, t2;
 
 --echo End of 5.1 tests
+
+--echo #
+--echo # Bug #26106: Wrong plan may be chosen when there are several possible
+--echo # range and ref accesses
+--echo #
+CREATE TABLE t1( 
+  a INT,
+  b INT,
+  KEY k ( a ),
+  KEY l ( a, b )
+); 
+
+INSERT INTO t1(a) VALUES (1);
+INSERT INTO t1 
+VALUES (2,3),(2,3),(2,3),(2,3),(2,3),(2,3),(2,3),(2,3),(2,3),(2,3);
+INSERT INTO t1 SELECT 3, 4 FROM t1 WHERE a = 2 AND b = 3;
+INSERT INTO t1 SELECT 4, 1 FROM t1 WHERE a = 2 AND b = 3;
+ANALYZE TABLE t1;
+INSERT INTO t1 VALUES (1, 2);
+INSERT INTO t1 SELECT a, b FROM t1 WHERE a=1 AND b=2; # 2
+INSERT INTO t1 SELECT a, b FROM t1 WHERE a=1 AND b=2; # 4
+INSERT INTO t1 SELECT a, b FROM t1 WHERE a=1 AND b=2; # 8
+INSERT INTO t1 SELECT a, b FROM t1 WHERE a=1 AND b=2; # 16
+INSERT INTO t1 SELECT a, b FROM t1 WHERE a=1 AND b=2; # 32
+INSERT INTO t1 SELECT a, b FROM t1 WHERE a=1 AND b=2; # 64
+INSERT INTO t1 SELECT a, b FROM t1 WHERE a=1 AND b=2; # 128
+--echo # This must use range over index l, not k.
+EXPLAIN SELECT * FROM t1 WHERE a = 1 AND b >= 2;
+
+CREATE TABLE t2( 
+  a INT, 
+  b INT, 
+  c INT,
+  KEY k ( a ), 
+  KEY l ( a, b ),
+  KEY m ( b ), 
+  KEY n ( a, c )
+); 
+
+INSERT INTO t2(a) VALUES (1);
+INSERT INTO t2 
+VALUES (2,3,3),(2,3,3),(2,3,3),(2,3,3),(2,3,3),
+       (2,3,3),(2,3,3),(2,3,3),(2,3,3),(2,3,3);
+INSERT INTO t2 SELECT 3, 4, 4 FROM t2 WHERE a = 2 AND b = 3;
+INSERT INTO t2 SELECT 4, 1, 1 FROM t2 WHERE a = 2 AND b = 3;
+ANALYZE TABLE t2;
+INSERT INTO t2 VALUES (1, 2, 2);
+INSERT INTO t2 SELECT a, b, c FROM t2 WHERE a=1 AND b=2; # 2
+INSERT INTO t2 SELECT a, b, c FROM t2 WHERE a=1 AND b=2; # 4
+INSERT INTO t2 SELECT a, b, c FROM t2 WHERE a=1 AND b=2; # 8
+INSERT INTO t2 SELECT a, b, c FROM t2 WHERE a=1 AND b=2; # 16
+INSERT INTO t2 SELECT a, b, c FROM t2 WHERE a=1 AND b=2; # 32
+INSERT INTO t2 SELECT a, b, c FROM t2 WHERE a=1 AND b=2; # 64
+INSERT INTO t2 VALUES (1, 1, 2);
+
+--echo # This must use range over index l, not n.
+EXPLAIN SELECT * FROM t2 WHERE a = 1 AND b >= 2 AND c >= 2;
+
+DROP TABLE t1, t2;

=== modified file 'sql/sql_select.cc'
--- a/sql/sql_select.cc	2010-06-24 12:17:37 +0000
+++ b/sql/sql_select.cc	2010-06-24 14:39:23 +0000
@@ -4320,6 +4320,13 @@ best_access_path(JOIN      *join,
   double best=              DBL_MAX;
   double best_time=         DBL_MAX;
   double records=           DBL_MAX;
+  /* Holds the current number of records from the range optimizer, if any */
+  double quick_records=     DBL_MAX;
+  /* 
+     When a key from the range optimizer matches more parts than a ref key, the
+     estimates from these sources are not comparable.
+  */
+  double best_quick_records= DBL_MAX;
   table_map best_ref_depends_map= 0;
   double tmp;
   ha_rows rec;
@@ -4349,6 +4356,12 @@ best_access_path(JOIN      *join,
       /* Calculate how many key segments of the current key we can use */
       start_key= keyuse;
 
+      /* 
+         True if we find some keys from the range optimizer that match more
+         key parts than the best ref key. We then choose the best range key.
+      */
+      bool quick_matches_more_parts= false;
+      
       do /* For each keypart */
       {
         uint keypart= keyuse->keypart;
@@ -4583,7 +4596,10 @@ best_access_path(JOIN      *join,
                 if (!found_ref && table->quick_keys.is_set(key) &&    // (1)
                     table->quick_key_parts[key] > max_key_part &&     // (2)
                     records < (double)table->quick_rows[key])         // (3)
-                  records= (double)table->quick_rows[key];
+                {
+                  records= quick_records= (double)table->quick_rows[key];
+                  quick_matches_more_parts= true;                  
+                }
 
                 tmp= records;
               }
@@ -4672,8 +4688,11 @@ best_access_path(JOIN      *join,
             tmp= best_time;                    // Do nothing
         }
       } /* not ft_key */
-      if (tmp < best_time - records/(double) TIME_FOR_COMPARE)
+      if (tmp < best_time - records/(double) TIME_FOR_COMPARE ||
+          (quick_matches_more_parts && 
+           quick_records < best_quick_records))
       {
+        best_quick_records = quick_records;
         best_time= tmp + records/(double) TIME_FOR_COMPARE;
         best= tmp;
         best_records= records;


Attachment: [text/bzr-bundle] bzr/martin.hansson@sun.com-20100624143923-4hkdi8ubgge6mud2.bundle
Thread
bzr commit into mysql-next-mr-bugfixing branch (martin.hansson:3270)Bug#26106Martin Hansson24 Jun