mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 12:32:27 +01:00
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.
This commit is contained in:
parent
da47ee7ef9
commit
27191a30c0
4 changed files with 214 additions and 21 deletions
|
@ -1076,6 +1076,68 @@ ID BETWEEN 3500 AND 3800) AND Country='USA'
|
|||
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 (
|
||||
|
|
|
@ -1077,6 +1077,68 @@ ID BETWEEN 3500 AND 3800) AND Country='USA'
|
|||
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 (
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -261,6 +261,11 @@ public:
|
|||
uint8 min_flag,max_flag,maybe_flag;
|
||||
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_alloc()
|
|||
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 *min_value_arg,
|
|||
: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 part_,
|
|||
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 *param, SEL_ARG *new_parent,
|
|||
increment_use_count(1);
|
||||
tmp->color= color;
|
||||
tmp->elements= this->elements;
|
||||
tmp->max_part_no= max_part_no;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
|
@ -7000,13 +7008,13 @@ bool sel_trees_can_be_ored(RANGE_OPT_PARAM* param,
|
|||
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
|
||||
|
@ -7031,21 +7039,31 @@ bool sel_trees_must_be_ored(RANGE_OPT_PARAM* param,
|
|||
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)
|
||||
int idx1, idx2;
|
||||
key_map::Iterator it1(oredable_keys);
|
||||
while ((idx1= it1++) != key_map::Iterator::BITMAP_END)
|
||||
{
|
||||
SEL_ARG *key1= tree1->keys[key_no];
|
||||
if (!field)
|
||||
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;
|
||||
}
|
||||
else if (!field->eq(key1->field))
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DBUG_RETURN(TRUE);
|
||||
}
|
||||
|
||||
|
@ -7342,6 +7360,7 @@ and_all_keys(RANGE_OPT_PARAM *param, SEL_ARG *key1, SEL_ARG *key2,
|
|||
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, SEL_ARG *key2, uint clone_flag)
|
|||
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 *key1, SEL_ARG *key2, uint clone_flag)
|
|||
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 *key1,SEL_ARG *key2)
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue