← Back to team overview

maria-developers team mailing list archive

bzr commit into MariaDB 5.1, with Maria 1.5:maria branch (igor:2747)

 

#At lp:maria based on revid:igor@xxxxxxxxxxxx-20091026173514-biry7lgxqiz2zz7a

 2747 Igor Babaev	2009-10-29
      Replaced a lame implementation of the function sel_trees_must_be_ored
      that blocked building index merge plans for the queries with where
      conditions of the form 
      (key1|2_p1=c AND range(key1_p2)) OR (key1|2_p1=c AND range(key2_p2)).
      
      The problem was discovered by Sergey Petrunia when he reviewed the patch
      for WL#24.
      
      Added new test cases. One of them failed to produce an index merge plan
      before the patch was applied.
modified:
  mysql-test/r/range_vs_index_merge.result
  mysql-test/r/range_vs_index_merge_innodb.result
  mysql-test/t/range_vs_index_merge.test
  sql/opt_range.cc

=== modified file 'mysql-test/r/range_vs_index_merge.result'
--- a/mysql-test/r/range_vs_index_merge.result	2009-10-17 18:40:46 +0000
+++ b/mysql-test/r/range_vs_index_merge.result	2009-10-29 18:49:58 +0000
@@ -1076,6 +1076,68 @@ ID BETWEEN 3500 AND 3800) AND Country='U
         AND (Name LIKE 'Pho%' OR ID BETWEEN 4000 AND 4300);
 ID	Name	Country	Population
 3798	Phoenix	USA	1321045
+DROP INDEX Population ON City;
+DROP INDEX Name ON City;
+EXPLAIN
+SELECT * FROM City
+WHERE Country='USA' AND Population BETWEEN 101000 AND 102000 OR
+Country='USA' AND Name LIKE 'Pa%';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	City	index_merge	Country,CountryPopulation,CountryName	CountryPopulation,CountryName	7,38	NULL	10	Using sort_union(CountryPopulation,CountryName); Using where
+SELECT * FROM City USE INDEX()
+WHERE Country='USA' AND Population BETWEEN 101000 AND 102000 OR
+Country='USA' AND Name LIKE 'Pa%';
+ID	Name	Country	Population
+3932	Paterson	USA	149222
+3943	Pasadena	USA	141674
+3953	Pasadena	USA	133936
+3967	Paradise	USA	124682
+3986	Palmdale	USA	116670
+4030	Sandy	USA	101853
+4031	Athens-Clarke County	USA	101489
+4032	Cambridge	USA	101355
+SELECT * FROM City
+WHERE Country='USA' AND Population BETWEEN 101000 AND 102000 OR
+Country='USA' AND Name LIKE 'Pa%';
+ID	Name	Country	Population
+3932	Paterson	USA	149222
+3943	Pasadena	USA	141674
+3953	Pasadena	USA	133936
+3967	Paradise	USA	124682
+3986	Palmdale	USA	116670
+4030	Sandy	USA	101853
+4031	Athens-Clarke County	USA	101489
+4032	Cambridge	USA	101355
+EXPLAIN
+SELECT * FROM City
+WHERE Country='USA' AND 
+(Population BETWEEN 101000 AND 102000 OR Name LIKE 'Pa%');
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	City	index_merge	Country,CountryPopulation,CountryName	CountryPopulation,CountryName	7,38	NULL	10	Using sort_union(CountryPopulation,CountryName); Using where
+SELECT * FROM City
+WHERE Country='USA' AND 
+(Population BETWEEN 101000 AND 102000 OR Name LIKE 'Pa%');
+ID	Name	Country	Population
+3932	Paterson	USA	149222
+3943	Pasadena	USA	141674
+3953	Pasadena	USA	133936
+3967	Paradise	USA	124682
+3986	Palmdale	USA	116670
+4030	Sandy	USA	101853
+4031	Athens-Clarke County	USA	101489
+4032	Cambridge	USA	101355
+SELECT * FROM City
+WHERE Country='USA' AND 
+(Population BETWEEN 101000 AND 102000 OR Name LIKE 'Pa%');
+ID	Name	Country	Population
+3932	Paterson	USA	149222
+3943	Pasadena	USA	141674
+3953	Pasadena	USA	133936
+3967	Paradise	USA	124682
+3986	Palmdale	USA	116670
+4030	Sandy	USA	101853
+4031	Athens-Clarke County	USA	101489
+4032	Cambridge	USA	101355
 DROP DATABASE world;
 use test;
 CREATE TABLE t1 (

=== modified file 'mysql-test/r/range_vs_index_merge_innodb.result'
--- a/mysql-test/r/range_vs_index_merge_innodb.result	2009-10-17 18:40:46 +0000
+++ b/mysql-test/r/range_vs_index_merge_innodb.result	2009-10-29 18:49:58 +0000
@@ -1077,6 +1077,68 @@ ID BETWEEN 3500 AND 3800) AND Country='U
         AND (Name LIKE 'Pho%' OR ID BETWEEN 4000 AND 4300);
 ID	Name	Country	Population
 3798	Phoenix	USA	1321045
+DROP INDEX Population ON City;
+DROP INDEX Name ON City;
+EXPLAIN
+SELECT * FROM City
+WHERE Country='USA' AND Population BETWEEN 101000 AND 102000 OR
+Country='USA' AND Name LIKE 'Pa%';
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	City	index_merge	Country,CountryPopulation,CountryName	CountryPopulation,CountryName	7,38	NULL	8	Using sort_union(CountryPopulation,CountryName); Using where
+SELECT * FROM City USE INDEX()
+WHERE Country='USA' AND Population BETWEEN 101000 AND 102000 OR
+Country='USA' AND Name LIKE 'Pa%';
+ID	Name	Country	Population
+3932	Paterson	USA	149222
+3943	Pasadena	USA	141674
+3953	Pasadena	USA	133936
+3967	Paradise	USA	124682
+3986	Palmdale	USA	116670
+4030	Sandy	USA	101853
+4031	Athens-Clarke County	USA	101489
+4032	Cambridge	USA	101355
+SELECT * FROM City
+WHERE Country='USA' AND Population BETWEEN 101000 AND 102000 OR
+Country='USA' AND Name LIKE 'Pa%';
+ID	Name	Country	Population
+3932	Paterson	USA	149222
+3943	Pasadena	USA	141674
+3953	Pasadena	USA	133936
+3967	Paradise	USA	124682
+3986	Palmdale	USA	116670
+4030	Sandy	USA	101853
+4031	Athens-Clarke County	USA	101489
+4032	Cambridge	USA	101355
+EXPLAIN
+SELECT * FROM City
+WHERE Country='USA' AND 
+(Population BETWEEN 101000 AND 102000 OR Name LIKE 'Pa%');
+id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
+1	SIMPLE	City	index_merge	Country,CountryPopulation,CountryName	CountryPopulation,CountryName	7,38	NULL	8	Using sort_union(CountryPopulation,CountryName); Using where
+SELECT * FROM City
+WHERE Country='USA' AND 
+(Population BETWEEN 101000 AND 102000 OR Name LIKE 'Pa%');
+ID	Name	Country	Population
+3932	Paterson	USA	149222
+3943	Pasadena	USA	141674
+3953	Pasadena	USA	133936
+3967	Paradise	USA	124682
+3986	Palmdale	USA	116670
+4030	Sandy	USA	101853
+4031	Athens-Clarke County	USA	101489
+4032	Cambridge	USA	101355
+SELECT * FROM City
+WHERE Country='USA' AND 
+(Population BETWEEN 101000 AND 102000 OR Name LIKE 'Pa%');
+ID	Name	Country	Population
+3932	Paterson	USA	149222
+3943	Pasadena	USA	141674
+3953	Pasadena	USA	133936
+3967	Paradise	USA	124682
+3986	Palmdale	USA	116670
+4030	Sandy	USA	101853
+4031	Athens-Clarke County	USA	101489
+4032	Cambridge	USA	101355
 DROP DATABASE world;
 use test;
 CREATE TABLE t1 (

=== modified file 'mysql-test/t/range_vs_index_merge.test'
--- a/mysql-test/t/range_vs_index_merge.test	2009-10-17 18:40:46 +0000
+++ b/mysql-test/t/range_vs_index_merge.test	2009-10-29 18:49:58 +0000
@@ -570,6 +570,51 @@ SELECT * FROM City
         AND (Name LIKE 'Pho%' OR ID BETWEEN 4000 AND 4300);
 
 
+DROP INDEX Population ON City;
+DROP INDEX Name ON City;
+
+# The pattern of the WHERE condition used in the following query is
+#   (key1|2_p1=c AND range(key1_p2)) OR (key1|2_p1=c AND range(key2_p2))
+# We get an index merge retrieval over key1, key2 for it
+
+EXPLAIN
+SELECT * FROM City
+  WHERE Country='USA' AND Population BETWEEN 101000 AND 102000 OR
+        Country='USA' AND Name LIKE 'Pa%';
+
+# The following 2 queries check that the plans
+# for the previous plan is valid
+
+SELECT * FROM City USE INDEX()
+  WHERE Country='USA' AND Population BETWEEN 101000 AND 102000 OR
+        Country='USA' AND Name LIKE 'Pa%';
+
+SELECT * FROM City
+  WHERE Country='USA' AND Population BETWEEN 101000 AND 102000 OR
+        Country='USA' AND Name LIKE 'Pa%';
+
+
+# The pattern of the WHERE condition used in the following query is
+#   key1|2_p1=c AND (range(key1_p2) OR range(key2_p2))
+# We get an index merge retrieval over key1, key2 for it
+
+EXPLAIN
+SELECT * FROM City
+  WHERE Country='USA' AND 
+        (Population BETWEEN 101000 AND 102000 OR Name LIKE 'Pa%');
+
+# The following 2 queries check that the plans
+# for the previous plan is valid
+
+SELECT * FROM City
+  WHERE Country='USA' AND 
+        (Population BETWEEN 101000 AND 102000 OR Name LIKE 'Pa%');
+
+SELECT * FROM City
+  WHERE Country='USA' AND 
+        (Population BETWEEN 101000 AND 102000 OR Name LIKE 'Pa%');
+
+
 DROP DATABASE world;
 
 use test;

=== modified file 'sql/opt_range.cc'
--- a/sql/opt_range.cc	2009-10-12 04:59:34 +0000
+++ b/sql/opt_range.cc	2009-10-29 18:49:58 +0000
@@ -262,6 +262,11 @@ public:
   uint8 part;					// Which key part
   uint8 maybe_null;
   /* 
+    The ordinal number the least significant component encountered the ranges
+    of the SEL_ARG tree (the first component has number 1) 
+  */
+  uint max_part_no; 
+  /* 
     Number of children of this element in the RB-tree, plus 1 for this
     element itself.
   */
@@ -737,12 +742,12 @@ public:
   uint real_keynr[MAX_KEY];
   /* Number of SEL_ARG objects allocated by SEL_ARG::clone_tree operations */
   uint alloced_sel_args; 
+  KEY_PART *key[MAX_KEY]; /* First key parts of keys used in the query */
 };
 
 class PARAM : public RANGE_OPT_PARAM
 {
 public:
-  KEY_PART *key[MAX_KEY]; /* First key parts of keys used in the query */
   ha_rows quick_rows[MAX_KEY];
   longlong baseflag;
   uint max_key_part, range_count;
@@ -2172,6 +2177,7 @@ SEL_ARG::SEL_ARG(SEL_ARG &arg) :Sql_allo
   min_value=arg.min_value;
   max_value=arg.max_value;
   next_key_part=arg.next_key_part;
+  max_part_no= arg.max_part_no;
   use_count=1; elements=1;
 }
 
@@ -2189,7 +2195,7 @@ SEL_ARG::SEL_ARG(Field *f,const uchar *m
   :min_flag(0), max_flag(0), maybe_flag(0), maybe_null(f->real_maybe_null()),
    elements(1), use_count(1), field(f), min_value((uchar*) min_value_arg),
    max_value((uchar*) max_value_arg), next(0),prev(0),
-   next_key_part(0),color(BLACK),type(KEY_RANGE)
+   next_key_part(0), color(BLACK), type(KEY_RANGE), max_part_no(1)
 {
   left=right= &null_element;
 }
@@ -2202,6 +2208,7 @@ SEL_ARG::SEL_ARG(Field *field_,uint8 par
    field(field_), min_value(min_value_), max_value(max_value_),
    next(0),prev(0),next_key_part(0),color(BLACK),type(KEY_RANGE)
 {
+  max_part_no= part+1;
   left=right= &null_element;
 }
 
@@ -2244,6 +2251,7 @@ SEL_ARG *SEL_ARG::clone(RANGE_OPT_PARAM 
   increment_use_count(1);
   tmp->color= color;
   tmp->elements= this->elements;
+  tmp->max_part_no= max_part_no;
   return tmp;
 }
 
@@ -7000,14 +7008,14 @@ bool sel_trees_can_be_ored(RANGE_OPT_PAR
       ordable_keys       bitmap of SEL_ARG trees that can be ored
 
   DESCRIPTION
-    For two trees tree1 and tree2 and the bitmap ordable_keys containing
-    bits for indexes that have SEL_ARG trees in range parts of both trees
-    and these SEL_ARG trees can be ored the function checks if there are
-    indexes for which SEL_ARG trees must be ored. Two SEL_ARG trees for the
-    same index must be ored if all indexes for which SEL_SRG trees from the
-    range parts of tree1 anf tree2 that can be ored have the same field as 
-    their major index component. 
-
+    For two trees tree1 and tree2 the function checks whether they must be
+    ored. The function assumes that the bitmap ordable_keys contains bits for
+    those corresponding pairs of SEL_ARG trees from tree1 and tree2 that can
+    be ored.
+    We believe that tree1 and tree2 must be ored if any pair of SEL_ARG trees
+    r1 and r2, such that r1 is from tree1 and r2 is from tree2 and both
+    of them are marked in ordable_keys, can be merged.
+    
   NOTE
     The function sel_trees_must_be_ored as a rule is used in pair with the
     function sel_trees_can_be_ored.
@@ -7031,21 +7039,31 @@ bool sel_trees_must_be_ored(RANGE_OPT_PA
   if (!tmp.is_clear_all())
     DBUG_RETURN(FALSE);
 
-  Field *field= 0;
-  uint part;
-  int key_no;
-  key_map::Iterator it(oredable_keys);
-  while ((key_no= it++) != key_map::Iterator::BITMAP_END)
-  {
-    SEL_ARG *key1= tree1->keys[key_no];
-    if (!field)
+  int idx1, idx2;
+  key_map::Iterator it1(oredable_keys);
+  while ((idx1= it1++) != key_map::Iterator::BITMAP_END)
+  {
+    KEY_PART *key1_init= param->key[idx1]+tree1->keys[idx1]->part;
+    KEY_PART *key1_end= param->key[idx1]+tree1->keys[idx1]->max_part_no;
+    key_map::Iterator it2(oredable_keys);
+    while ((idx2= it2++) != key_map::Iterator::BITMAP_END)
     {
-      field= key1->field;
-      part= key1->part;
+      if (idx2 <= idx1)
+        continue;
+      
+      KEY_PART *key2_init= param->key[idx2]+tree2->keys[idx2]->part;
+      KEY_PART *key2_end= param->key[idx2]+tree2->keys[idx2]->max_part_no;
+      KEY_PART *part1, *part2;
+      for (part1= key1_init, part2= key2_init;
+           part1 < key1_end && part2 < key2_end;
+           part1++, part2++)
+      { 
+        if (!part1->field->eq(part2->field))
+          DBUG_RETURN(FALSE);
+      }
     }
-    else if (!field->eq(key1->field))
-      DBUG_RETURN(FALSE);
   }
+      
   DBUG_RETURN(TRUE);
 }  
 
@@ -7342,6 +7360,7 @@ and_all_keys(RANGE_OPT_PARAM *param, SEL
   if (!key1)
     return &null_element;			// Impossible ranges
   key1->use_count++;
+  key1->max_part_no= max(key2->max_part_no, key2->part+1);
   return key1;
 }
 
@@ -7441,6 +7460,7 @@ key_and(RANGE_OPT_PARAM *param, SEL_ARG 
   key1->use_count--;
   key2->use_count--;
   SEL_ARG *e1=key1->first(), *e2=key2->first(), *new_tree=0;
+  uint max_part_no= max(key1->max_part_no, key2->max_part_no);
 
   while (e1 && e2)
   {
@@ -7478,6 +7498,7 @@ key_and(RANGE_OPT_PARAM *param, SEL_ARG 
   key2->free_tree();
   if (!new_tree)
     return &null_element;			// Impossible range
+  new_tree->max_part_no= max_part_no;
   return new_tree;
 }
 
@@ -7557,6 +7578,8 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *
   bool key2_shared=key2->use_count != 0;
   key1->maybe_flag|=key2->maybe_flag;
 
+  uint max_part_no= max(key1->max_part_no, key2->max_part_no);
+
   for (key2=key2->first(); key2; )
   {
     SEL_ARG *tmp=key1->find_range(key2);	// Find key1.min <= key2.min
@@ -7749,6 +7772,7 @@ end:
     key2=next;
   }
   key1->use_count++;
+  key1->max_part_no= max_part_no;
   return key1;
 }