From e5a0c0281e0c501e66b82b625ebcd92a9c485ce0 Mon Sep 17 00:00:00 2001
From: Patrick Crews <patrick.crews@sun.com>
Date: Fri, 27 Feb 2009 15:00:49 +0200
Subject: [PATCH 1/2] Bug#41893:  Removal of trailing space noise causing
 Pushbuild failure.

---
 mysql-test/r/variables.result | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mysql-test/r/variables.result b/mysql-test/r/variables.result
index 04ccf3d688c..fbec38c9a9f 100644
--- a/mysql-test/r/variables.result
+++ b/mysql-test/r/variables.result
@@ -292,7 +292,7 @@ transaction_prealloc_size	20480
 set @@range_alloc_block_size=default;
 set @@query_alloc_block_size=default, @@query_prealloc_size=default;
 set transaction_alloc_block_size=default, @@transaction_prealloc_size=default;
-==+ Check the values not that they are reset +==
+==+ Check the values now that they are reset +==
 SHOW VARIABLES WHERE variable_name IN ('range_alloc_block_size',
 'query_alloc_block_size', 'query_prealloc_size',
 'transaction_alloc_block_size', 'transaction_prealloc_size');

From 15760fe9d8434dc9c960c123945b13890456bb5f Mon Sep 17 00:00:00 2001
From: Georgi Kodinov <kgeorge@mysql.com>
Date: Fri, 27 Feb 2009 15:25:06 +0200
Subject: [PATCH 2/2] Bug #41610: key_infix_len can be overwritten causing some
 group by queries to return no rows

The algorithm of determining the best key for loose index scan is doing a loop
over the available indexes and selects the one that has the best cost.
It retrieves the parameters of the current index into a set of variables.
If the cost of using the current index is lower than the best cost so far it
copies these variables into another set of variables that contain the
information for the best index so far.
After having checked all the indexes it uses these variables (outside of the
index loop) to create the table read plan object instance.
The was a single omission : the key_infix/key_infix_len variables were used
outside of the loop without being preserved in the loop for the best index
so far.
This causes these variables to get overwritten by the next index(es) checked.
Fixed by adding variables to hold the data for the current index, passing
the new variables to the function that assigns values to them and copying
the new variables into the existing ones when selecting a new current best
index.
To avoid further such problems moved the declarations of the variables used
to keep information about the current index inside the loop's compound
statement.

mysql-test/r/group_min_max.result:
  Bug #41610: test case
mysql-test/t/group_min_max.test:
  Bug #41610: test case
sql/opt_range.cc:
  Bug #41610: copy the infix data for the current best index
---
 mysql-test/r/group_min_max.result | 15 ++++++++
 mysql-test/t/group_min_max.test   | 23 +++++++++++++
 sql/opt_range.cc                  | 57 +++++++++++++++++--------------
 3 files changed, 70 insertions(+), 25 deletions(-)

diff --git a/mysql-test/r/group_min_max.result b/mysql-test/r/group_min_max.result
index f2f488650d5..58be2af1bb8 100644
--- a/mysql-test/r/group_min_max.result
+++ b/mysql-test/r/group_min_max.result
@@ -2429,3 +2429,18 @@ id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
 Warnings:
 Note	1003	select sql_buffer_result `test`.`t1`.`a` AS `a`,(max(`test`.`t1`.`b`) + 1) AS `max(b)+1` from `test`.`t1` where (`test`.`t1`.`a` = 0) group by `test`.`t1`.`a`
 drop table t1;
+CREATE TABLE t1 (a int, b int, c int, d int,
+KEY foo (c,d,a,b), KEY bar (c,a,b,d));
+INSERT INTO t1 VALUES (1, 1, 1, 1), (1, 1, 1, 2), (1, 1, 1, 3), (1, 1, 1, 4);
+INSERT INTO t1 SELECT * FROM t1;
+INSERT INTO t1 SELECT * FROM t1;
+INSERT INTO t1 SELECT a,b,c+1,d FROM t1;
+EXPLAIN SELECT DISTINCT c FROM t1 WHERE d=4;
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	t1	range	NULL	foo	10	NULL	9	Using where; Using index for group-by
+SELECT DISTINCT c FROM t1 WHERE d=4;
+c
+1
+2
+DROP TABLE t1;
+End of 5.0 tests
diff --git a/mysql-test/t/group_min_max.test b/mysql-test/t/group_min_max.test
index f7aed7301fb..e124bf9e786 100644
--- a/mysql-test/t/group_min_max.test
+++ b/mysql-test/t/group_min_max.test
@@ -935,3 +935,26 @@ insert into t1 (a,b) select a, max(b)+1 from t1 where a = 0 group by a;
 select * from t1;
 explain extended select sql_buffer_result a, max(b)+1 from t1 where a = 0 group by a;
 drop table t1;
+
+
+#
+# Bug #41610: key_infix_len can be overwritten causing some group by queries
+# to return no rows
+#
+
+CREATE TABLE t1 (a int, b int, c int, d int,
+  KEY foo (c,d,a,b), KEY bar (c,a,b,d));
+
+INSERT INTO t1 VALUES (1, 1, 1, 1), (1, 1, 1, 2), (1, 1, 1, 3), (1, 1, 1, 4);
+INSERT INTO t1 SELECT * FROM t1;
+INSERT INTO t1 SELECT * FROM t1;
+INSERT INTO t1 SELECT a,b,c+1,d FROM t1;
+
+#Should be non-empty
+--ordered_result
+EXPLAIN SELECT DISTINCT c FROM t1 WHERE d=4;
+SELECT DISTINCT c FROM t1 WHERE d=4;
+
+DROP TABLE t1;
+
+--echo End of 5.0 tests
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index ebebfafb5d8..018fc8a9d44 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -7775,32 +7775,37 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
   */
   KEY *cur_index_info= table->key_info;
   KEY *cur_index_info_end= cur_index_info + table->s->keys;
-  KEY_PART_INFO *cur_part= NULL;
-  KEY_PART_INFO *end_part; /* Last part for loops. */
-  /* Last index part. */
-  KEY_PART_INFO *last_part= NULL;
-  KEY_PART_INFO *first_non_group_part= NULL;
-  KEY_PART_INFO *first_non_infix_part= NULL;
-  uint key_infix_parts= 0;
-  uint cur_group_key_parts= 0;
-  uint cur_group_prefix_len= 0;
   /* Cost-related variables for the best index so far. */
   double best_read_cost= DBL_MAX;
   ha_rows best_records= 0;
   SEL_ARG *best_index_tree= NULL;
   ha_rows best_quick_prefix_records= 0;
   uint best_param_idx= 0;
-  double cur_read_cost= DBL_MAX;
-  ha_rows cur_records;
+
+  const uint pk= param->table->s->primary_key;
   SEL_ARG *cur_index_tree= NULL;
   ha_rows cur_quick_prefix_records= 0;
   uint cur_param_idx=MAX_KEY;
-  key_map cur_used_key_parts;
-  uint pk= param->table->s->primary_key;
 
   for (uint cur_index= 0 ; cur_index_info != cur_index_info_end ;
        cur_index_info++, cur_index++)
   {
+    KEY_PART_INFO *cur_part;
+    KEY_PART_INFO *end_part; /* Last part for loops. */
+    /* Last index part. */
+    KEY_PART_INFO *last_part;
+    KEY_PART_INFO *first_non_group_part;
+    KEY_PART_INFO *first_non_infix_part;
+    uint key_infix_parts;
+    uint cur_group_key_parts= 0;
+    uint cur_group_prefix_len= 0;
+    double cur_read_cost;
+    ha_rows cur_records;
+    key_map used_key_parts_map;
+    uint cur_key_infix_len= 0;
+    byte cur_key_infix[MAX_KEY_LENGTH];
+    uint cur_used_key_parts;
+    
     /* Check (B1) - if current index is covering. */
     if (!table->used_keys.is_set(cur_index))
       goto next_index;
@@ -7879,7 +7884,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
     else if (join->select_distinct)
     {
       select_items_it.rewind();
-      cur_used_key_parts.clear_all();
+      used_key_parts_map.clear_all();
       uint max_key_part= 0;
       while ((item= select_items_it++))
       {
@@ -7890,13 +7895,13 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
           Check if this attribute was already present in the select list.
           If it was present, then its corresponding key part was alredy used.
         */
-        if (cur_used_key_parts.is_set(key_part_nr))
+        if (used_key_parts_map.is_set(key_part_nr))
           continue;
         if (key_part_nr < 1 || key_part_nr > join->fields_list.elements)
           goto next_index;
         cur_part= cur_index_info->key_part + key_part_nr - 1;
         cur_group_prefix_len+= cur_part->store_length;
-        cur_used_key_parts.set_bit(key_part_nr);
+        used_key_parts_map.set_bit(key_part_nr);
         ++cur_group_key_parts;
         max_key_part= max(max_key_part,key_part_nr);
       }
@@ -7908,7 +7913,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
       */
       ulonglong all_parts, cur_parts;
       all_parts= (1<<max_key_part) - 1;
-      cur_parts= cur_used_key_parts.to_ulonglong() >> 1;
+      cur_parts= used_key_parts_map.to_ulonglong() >> 1;
       if (all_parts != cur_parts)
         goto next_index;
     }
@@ -7958,7 +7963,8 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
                                                         &dummy);
         if (!get_constant_key_infix(cur_index_info, index_range_tree,
                                     first_non_group_part, min_max_arg_part,
-                                    last_part, thd, key_infix, &key_infix_len,
+                                    last_part, thd, cur_key_infix, 
+                                    &cur_key_infix_len,
                                     &first_non_infix_part))
           goto next_index;
       }
@@ -8010,9 +8016,9 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
     }
 
     /* If we got to this point, cur_index_info passes the test. */
-    key_infix_parts= key_infix_len ?
+    key_infix_parts= cur_key_infix_len ?
                      (first_non_infix_part - first_non_group_part) : 0;
-    used_key_parts= cur_group_key_parts + key_infix_parts;
+    cur_used_key_parts= cur_group_key_parts + key_infix_parts;
 
     /* Compute the cost of using this index. */
     if (tree)
@@ -8024,7 +8030,7 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
       cur_quick_prefix_records= check_quick_select(param, cur_param_idx,
                                                     cur_index_tree);
     }
-    cost_group_min_max(table, cur_index_info, used_key_parts,
+    cost_group_min_max(table, cur_index_info, cur_used_key_parts,
                        cur_group_key_parts, tree, cur_index_tree,
                        cur_quick_prefix_records, have_min, have_max,
                        &cur_read_cost, &cur_records);
@@ -8035,7 +8041,6 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
     */
     if (cur_read_cost < best_read_cost - (DBL_EPSILON * cur_read_cost))
     {
-      DBUG_ASSERT(tree != 0 || cur_param_idx == MAX_KEY);
       index_info= cur_index_info;
       index= cur_index;
       best_read_cost= cur_read_cost;
@@ -8045,11 +8050,13 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
       best_param_idx= cur_param_idx;
       group_key_parts= cur_group_key_parts;
       group_prefix_len= cur_group_prefix_len;
+      key_infix_len= cur_key_infix_len;
+      if (key_infix_len)
+        memcpy (key_infix, cur_key_infix, sizeof (key_infix));
+      used_key_parts= cur_used_key_parts;
     }
 
-  next_index:
-    cur_group_key_parts= 0;
-    cur_group_prefix_len= 0;
+  next_index:;
   }
   if (!index_info) /* No usable index found. */
     DBUG_RETURN(NULL);