Commit 27191a30 authored by Igor Babaev's avatar Igor Babaev

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.
parent da47ee7e
...@@ -1076,6 +1076,68 @@ ID BETWEEN 3500 AND 3800) AND Country='USA' ...@@ -1076,6 +1076,68 @@ ID BETWEEN 3500 AND 3800) AND Country='USA'
AND (Name LIKE 'Pho%' OR ID BETWEEN 4000 AND 4300); AND (Name LIKE 'Pho%' OR ID BETWEEN 4000 AND 4300);
ID Name Country Population ID Name Country Population
3798 Phoenix USA 1321045 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; DROP DATABASE world;
use test; use test;
CREATE TABLE t1 ( CREATE TABLE t1 (
......
...@@ -1077,6 +1077,68 @@ ID BETWEEN 3500 AND 3800) AND Country='USA' ...@@ -1077,6 +1077,68 @@ ID BETWEEN 3500 AND 3800) AND Country='USA'
AND (Name LIKE 'Pho%' OR ID BETWEEN 4000 AND 4300); AND (Name LIKE 'Pho%' OR ID BETWEEN 4000 AND 4300);
ID Name Country Population ID Name Country Population
3798 Phoenix USA 1321045 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; DROP DATABASE world;
use test; use test;
CREATE TABLE t1 ( CREATE TABLE t1 (
......
...@@ -570,6 +570,51 @@ SELECT * FROM City ...@@ -570,6 +570,51 @@ SELECT * FROM City
AND (Name LIKE 'Pho%' OR ID BETWEEN 4000 AND 4300); 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; DROP DATABASE world;
use test; use test;
......
...@@ -261,6 +261,11 @@ public: ...@@ -261,6 +261,11 @@ public:
uint8 min_flag,max_flag,maybe_flag; uint8 min_flag,max_flag,maybe_flag;
uint8 part; // Which key part uint8 part; // Which key part
uint8 maybe_null; 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 Number of children of this element in the RB-tree, plus 1 for this
element itself. element itself.
...@@ -737,12 +742,12 @@ public: ...@@ -737,12 +742,12 @@ public:
uint real_keynr[MAX_KEY]; uint real_keynr[MAX_KEY];
/* Number of SEL_ARG objects allocated by SEL_ARG::clone_tree operations */ /* Number of SEL_ARG objects allocated by SEL_ARG::clone_tree operations */
uint alloced_sel_args; uint alloced_sel_args;
KEY_PART *key[MAX_KEY]; /* First key parts of keys used in the query */
}; };
class PARAM : public RANGE_OPT_PARAM class PARAM : public RANGE_OPT_PARAM
{ {
public: public:
KEY_PART *key[MAX_KEY]; /* First key parts of keys used in the query */
ha_rows quick_rows[MAX_KEY]; ha_rows quick_rows[MAX_KEY];
longlong baseflag; longlong baseflag;
uint max_key_part, range_count; uint max_key_part, range_count;
...@@ -2172,6 +2177,7 @@ SEL_ARG::SEL_ARG(SEL_ARG &arg) :Sql_alloc() ...@@ -2172,6 +2177,7 @@ SEL_ARG::SEL_ARG(SEL_ARG &arg) :Sql_alloc()
min_value=arg.min_value; min_value=arg.min_value;
max_value=arg.max_value; max_value=arg.max_value;
next_key_part=arg.next_key_part; next_key_part=arg.next_key_part;
max_part_no= arg.max_part_no;
use_count=1; elements=1; use_count=1; elements=1;
} }
...@@ -2189,7 +2195,7 @@ SEL_ARG::SEL_ARG(Field *f,const uchar *min_value_arg, ...@@ -2189,7 +2195,7 @@ SEL_ARG::SEL_ARG(Field *f,const uchar *min_value_arg,
:min_flag(0), max_flag(0), maybe_flag(0), maybe_null(f->real_maybe_null()), :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), elements(1), use_count(1), field(f), min_value((uchar*) min_value_arg),
max_value((uchar*) max_value_arg), next(0),prev(0), 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; left=right= &null_element;
} }
...@@ -2202,6 +2208,7 @@ SEL_ARG::SEL_ARG(Field *field_,uint8 part_, ...@@ -2202,6 +2208,7 @@ SEL_ARG::SEL_ARG(Field *field_,uint8 part_,
field(field_), min_value(min_value_), max_value(max_value_), field(field_), min_value(min_value_), max_value(max_value_),
next(0),prev(0),next_key_part(0),color(BLACK),type(KEY_RANGE) next(0),prev(0),next_key_part(0),color(BLACK),type(KEY_RANGE)
{ {
max_part_no= part+1;
left=right= &null_element; left=right= &null_element;
} }
...@@ -2244,6 +2251,7 @@ SEL_ARG *SEL_ARG::clone(RANGE_OPT_PARAM *param, SEL_ARG *new_parent, ...@@ -2244,6 +2251,7 @@ SEL_ARG *SEL_ARG::clone(RANGE_OPT_PARAM *param, SEL_ARG *new_parent,
increment_use_count(1); increment_use_count(1);
tmp->color= color; tmp->color= color;
tmp->elements= this->elements; tmp->elements= this->elements;
tmp->max_part_no= max_part_no;
return tmp; return tmp;
} }
...@@ -7000,14 +7008,14 @@ bool sel_trees_can_be_ored(RANGE_OPT_PARAM* param, ...@@ -7000,14 +7008,14 @@ bool sel_trees_can_be_ored(RANGE_OPT_PARAM* param,
ordable_keys bitmap of SEL_ARG trees that can be ored ordable_keys bitmap of SEL_ARG trees that can be ored
DESCRIPTION DESCRIPTION
For two trees tree1 and tree2 and the bitmap ordable_keys containing For two trees tree1 and tree2 the function checks whether they must be
bits for indexes that have SEL_ARG trees in range parts of both trees ored. The function assumes that the bitmap ordable_keys contains bits for
and these SEL_ARG trees can be ored the function checks if there are those corresponding pairs of SEL_ARG trees from tree1 and tree2 that can
indexes for which SEL_ARG trees must be ored. Two SEL_ARG trees for the be ored.
same index must be ored if all indexes for which SEL_SRG trees from the We believe that tree1 and tree2 must be ored if any pair of SEL_ARG trees
range parts of tree1 anf tree2 that can be ored have the same field as r1 and r2, such that r1 is from tree1 and r2 is from tree2 and both
their major index component. of them are marked in ordable_keys, can be merged.
NOTE NOTE
The function sel_trees_must_be_ored as a rule is used in pair with the The function sel_trees_must_be_ored as a rule is used in pair with the
function sel_trees_can_be_ored. function sel_trees_can_be_ored.
...@@ -7031,21 +7039,31 @@ bool sel_trees_must_be_ored(RANGE_OPT_PARAM* param, ...@@ -7031,21 +7039,31 @@ bool sel_trees_must_be_ored(RANGE_OPT_PARAM* param,
if (!tmp.is_clear_all()) if (!tmp.is_clear_all())
DBUG_RETURN(FALSE); DBUG_RETURN(FALSE);
Field *field= 0; int idx1, idx2;
uint part; key_map::Iterator it1(oredable_keys);
int key_no; while ((idx1= it1++) != key_map::Iterator::BITMAP_END)
key_map::Iterator it(oredable_keys);
while ((key_no= it++) != key_map::Iterator::BITMAP_END)
{ {
SEL_ARG *key1= tree1->keys[key_no]; KEY_PART *key1_init= param->key[idx1]+tree1->keys[idx1]->part;
if (!field) 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; if (idx2 <= idx1)
part= key1->part; 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); DBUG_RETURN(TRUE);
} }
...@@ -7342,6 +7360,7 @@ and_all_keys(RANGE_OPT_PARAM *param, SEL_ARG *key1, SEL_ARG *key2, ...@@ -7342,6 +7360,7 @@ and_all_keys(RANGE_OPT_PARAM *param, SEL_ARG *key1, SEL_ARG *key2,
if (!key1) if (!key1)
return &null_element; // Impossible ranges return &null_element; // Impossible ranges
key1->use_count++; key1->use_count++;
key1->max_part_no= max(key2->max_part_no, key2->part+1);
return key1; return key1;
} }
...@@ -7441,6 +7460,7 @@ key_and(RANGE_OPT_PARAM *param, SEL_ARG *key1, SEL_ARG *key2, uint clone_flag) ...@@ -7441,6 +7460,7 @@ key_and(RANGE_OPT_PARAM *param, SEL_ARG *key1, SEL_ARG *key2, uint clone_flag)
key1->use_count--; key1->use_count--;
key2->use_count--; key2->use_count--;
SEL_ARG *e1=key1->first(), *e2=key2->first(), *new_tree=0; 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) while (e1 && e2)
{ {
...@@ -7478,6 +7498,7 @@ key_and(RANGE_OPT_PARAM *param, SEL_ARG *key1, SEL_ARG *key2, uint clone_flag) ...@@ -7478,6 +7498,7 @@ key_and(RANGE_OPT_PARAM *param, SEL_ARG *key1, SEL_ARG *key2, uint clone_flag)
key2->free_tree(); key2->free_tree();
if (!new_tree) if (!new_tree)
return &null_element; // Impossible range return &null_element; // Impossible range
new_tree->max_part_no= max_part_no;
return new_tree; return new_tree;
} }
...@@ -7557,6 +7578,8 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2) ...@@ -7557,6 +7578,8 @@ key_or(RANGE_OPT_PARAM *param, SEL_ARG *key1,SEL_ARG *key2)
bool key2_shared=key2->use_count != 0; bool key2_shared=key2->use_count != 0;
key1->maybe_flag|=key2->maybe_flag; key1->maybe_flag|=key2->maybe_flag;
uint max_part_no= max(key1->max_part_no, key2->max_part_no);
for (key2=key2->first(); key2; ) for (key2=key2->first(); key2; )
{ {
SEL_ARG *tmp=key1->find_range(key2); // Find key1.min <= key2.min SEL_ARG *tmp=key1->find_range(key2); // Find key1.min <= key2.min
...@@ -7749,6 +7772,7 @@ end: ...@@ -7749,6 +7772,7 @@ end:
key2=next; key2=next;
} }
key1->use_count++; key1->use_count++;
key1->max_part_no= max_part_no;
return key1; return key1;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment