mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
Fix for BUG#16710.
The bug was due to a missed case in the detection of whether an index can be used for loose scan. More precisely, the range optimizer chose to use loose index scan for queries for which the condition(s) over an index key part could not be pushed to the index together with the loose scan. As a result, loose index scan was selecting the first row in the index with a given GROUP BY prefix, and was applying the WHERE clause after that, while it should have inspected all rows with the given prefix, and apply the WHERE clause to all of them. The fix detects and skips such cases. mysql-test/r/group_min_max.result: Added test for BUG#16710. mysql-test/t/group_min_max.test: Added test for BUG#16710. sql/item.cc: Added new method [Item | Item_field]::find_item_in_field_list_processor. sql/item.h: Added new method [Item | Item_field]::find_item_in_field_list_processor. sql/opt_range.cc: Handle the case when there is no MIN/MAX aggregate function, and a keypart of the index being considered, that is after the GROUP BY prefix, is used in the WHERE clause and the condition where it is used cannot be pushed to the index. If this is the case, we rule out this index.
This commit is contained in:
@ -1954,6 +1954,15 @@ id select_type table type possible_keys key key_len ref rows Extra
|
||||
explain select sum(ord(a1)) from t1 where (a1 > 'a') group by a1,a2,b;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 index idx_t1_0,idx_t1_1,idx_t1_2 idx_t1_1 163 NULL 128 Using where; Using index
|
||||
explain select distinct(a1) from t1 where ord(a2) = 98;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 index NULL idx_t1_1 163 NULL 128 Using where; Using index
|
||||
select distinct(a1) from t1 where ord(a2) = 98;
|
||||
a1
|
||||
a
|
||||
b
|
||||
c
|
||||
d
|
||||
explain select a1 from t1 where a2 = 'b' group by a1;
|
||||
id select_type table type possible_keys key key_len ref rows Extra
|
||||
1 SIMPLE t1 range NULL idx_t1_1 130 NULL 5 Using where; Using index for group-by
|
||||
|
@ -641,6 +641,14 @@ explain select a1,a2,count(a2) from t1 group by a1,a2,b;
|
||||
explain select a1,a2,count(a2) from t1 where (a1 > 'a') group by a1,a2,b;
|
||||
explain select sum(ord(a1)) from t1 where (a1 > 'a') group by a1,a2,b;
|
||||
|
||||
|
||||
#
|
||||
# Bug #16710: select distinct doesn't return all it should
|
||||
#
|
||||
|
||||
explain select distinct(a1) from t1 where ord(a2) = 98;
|
||||
select distinct(a1) from t1 where ord(a2) = 98;
|
||||
|
||||
#
|
||||
# BUG#11044: DISTINCT or GROUP BY queries with equality predicates instead of MIN/MAX.
|
||||
#
|
||||
|
34
sql/item.cc
34
sql/item.cc
@ -499,7 +499,7 @@ bool Item_ident::remove_dependence_processor(byte * arg)
|
||||
arguments in a condition the method must return false.
|
||||
|
||||
RETURN
|
||||
false to force the evaluation of collect_item_field_processor
|
||||
FALSE to force the evaluation of collect_item_field_processor
|
||||
for the subsequent items.
|
||||
*/
|
||||
|
||||
@ -520,6 +520,38 @@ bool Item_field::collect_item_field_processor(byte *arg)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Check if an Item_field references some field from a list of fields.
|
||||
|
||||
SYNOPSIS
|
||||
Item_field::find_item_in_field_list_processor
|
||||
arg Field being compared, arg must be of type Field
|
||||
|
||||
DESCRIPTION
|
||||
Check whether the Item_field represented by 'this' references any
|
||||
of the fields in the keyparts passed via 'arg'. Used with the
|
||||
method Item::walk() to test whether any keypart in a sequence of
|
||||
keyparts is referenced in an expression.
|
||||
|
||||
RETURN
|
||||
TRUE if 'this' references the field 'arg'
|
||||
FALE otherwise
|
||||
*/
|
||||
bool Item_field::find_item_in_field_list_processor(byte *arg)
|
||||
{
|
||||
KEY_PART_INFO *first_non_group_part= *((KEY_PART_INFO **) arg);
|
||||
KEY_PART_INFO *last_part= *(((KEY_PART_INFO **) arg) + 1);
|
||||
KEY_PART_INFO *cur_part;
|
||||
|
||||
for (cur_part= first_non_group_part; cur_part != last_part; cur_part++)
|
||||
{
|
||||
if (field->eq(cur_part->field))
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
bool Item::check_cols(uint c)
|
||||
{
|
||||
if (c != 1)
|
||||
|
@ -699,6 +699,7 @@ public:
|
||||
virtual bool remove_fixed(byte * arg) { fixed= 0; return 0; }
|
||||
virtual bool cleanup_processor(byte *arg);
|
||||
virtual bool collect_item_field_processor(byte * arg) { return 0; }
|
||||
virtual bool find_item_in_field_list_processor(byte *arg) { return 0; }
|
||||
virtual bool change_context_processor(byte *context) { return 0; }
|
||||
virtual bool reset_query_id_processor(byte *query_id) { return 0; }
|
||||
|
||||
@ -1146,6 +1147,7 @@ public:
|
||||
bool is_null() { return field->is_null(); }
|
||||
Item *get_tmp_table_item(THD *thd);
|
||||
bool collect_item_field_processor(byte * arg);
|
||||
bool find_item_in_field_list_processor(byte *arg);
|
||||
bool reset_query_id_processor(byte *arg)
|
||||
{
|
||||
field->query_id= *((query_id_t *) arg);
|
||||
|
@ -6876,6 +6876,7 @@ cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts,
|
||||
bool have_min, bool have_max,
|
||||
double *read_cost, ha_rows *records);
|
||||
|
||||
|
||||
/*
|
||||
Test if this access method is applicable to a GROUP query with MIN/MAX
|
||||
functions, and if so, construct a new TRP object.
|
||||
@ -7282,12 +7283,37 @@ get_best_group_min_max(PARAM *param, SEL_TREE *tree)
|
||||
}
|
||||
else if (min_max_arg_part &&
|
||||
(min_max_arg_part - first_non_group_part > 0))
|
||||
{
|
||||
/*
|
||||
There is a gap but no range tree, thus no predicates at all for the
|
||||
non-group keyparts.
|
||||
*/
|
||||
goto next_index;
|
||||
}
|
||||
else if (first_non_group_part && join->conds)
|
||||
{
|
||||
/*
|
||||
If there is no MIN/MAX function in the query, but some index
|
||||
key part is referenced in the WHERE clause, then this index
|
||||
cannot be used because the WHERE condition over the keypart's
|
||||
field cannot be 'pushed' to the index (because there is no
|
||||
range 'tree'), and the WHERE clause must be evaluated before
|
||||
GROUP BY/DISTINCT.
|
||||
*/
|
||||
/*
|
||||
Store the first and last keyparts that need to be analyzed
|
||||
into one array that can be passed as parameter.
|
||||
*/
|
||||
KEY_PART_INFO *key_part_range[2];
|
||||
key_part_range[0]= first_non_group_part;
|
||||
key_part_range[1]= last_part;
|
||||
|
||||
/* Check if cur_part is referenced in the WHERE clause. */
|
||||
if (join->conds->walk(&Item::find_item_in_field_list_processor,
|
||||
(byte*) key_part_range))
|
||||
goto next_index;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Test (WA1) partially - that no other keypart after the last infix part is
|
||||
|
Reference in New Issue
Block a user