mirror of
https://github.com/MariaDB/server.git
synced 2025-01-29 10:14:19 +01:00
Bug#11751794 MYSQL GIVES THE WRONG RESULT WITH SOME SPECIAL USAGE
Consider the following query: SELECT f_1,..,f_m, AGGREGATE_FN(C) FROM t1 WHERE ... GROUP BY ... Loose index scan ("Using index for group-by") can be used for this query if there is an index 'i' covering all fields in the select list, and the GROUP BY clause makes up a prefix f1,...,fn of 'i'. Furthermore, according to rule NGA2 of get_best_group_min_max(), the WHERE clause must contain a conjunction of equality predicates for all fields fn+1,...,fm. The problem in this bug was that a query with WHERE clause that broke NGA2(NGA: Non Group Attribuite) was not detected and therefore used loose index scan. This lead to wrong result. The query had an index covering (c1,c2) and had: "WHERE (c1 = 1 AND c2 = 'a') OR (c1 = 2 AND c2 = 'b') GROUP BY c1" or "WHERE (c1 = 1 ) OR (c1 = 2 AND c2 = 'b') GROUP BY c1" This WHERE clause cannot be transformed to a conjunction of equality predicates. The solution is to introduce another rule, NGA3, that complements NGA2. NGA3 says that if a gap field (field between those listed in GROUP BY and C in the index) has a predicate, then there can only be one range in the query. This requirement is more strict than it has to be in theory. BUG 15947433 will deal with that. sql/opt_range.cc: check for the repetition of non group field.
This commit is contained in:
commit
064c6db0f5
1 changed files with 81 additions and 17 deletions
|
@ -9396,13 +9396,15 @@ cost_group_min_max(TABLE* table, KEY *index_info, uint used_key_parts,
|
|||
NGA1.If in the index I there is a gap between the last GROUP attribute G_k,
|
||||
and the MIN/MAX attribute C, then NGA must consist of exactly the
|
||||
index attributes that constitute the gap. As a result there is a
|
||||
permutation of NGA that coincides with the gap in the index
|
||||
<B_1, ..., B_m>.
|
||||
permutation of NGA, BA=<B_1,...,B_m>, that coincides with the gap
|
||||
in the index.
|
||||
NGA2.If BA <> {}, then the WHERE clause must contain a conjunction EQ of
|
||||
equality conditions for all NG_i of the form (NG_i = const) or
|
||||
(const = NG_i), such that each NG_i is referenced in exactly one
|
||||
conjunct. Informally, the predicates provide constants to fill the
|
||||
gap in the index.
|
||||
NGA3.If BA <> {}, there can only be one range. TODO: This is a code
|
||||
limitation and is not strictly needed. See BUG#15947433
|
||||
WA1. There are no other attributes in the WHERE clause except the ones
|
||||
referenced in predicates RNG, PA, PC, EQ defined above. Therefore
|
||||
WA is subset of (GA union NGA union C) for GA,NGA,C that pass the
|
||||
|
@ -10056,6 +10058,72 @@ check_group_min_max_predicates(COND *cond, Item_field *min_max_arg_item,
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
Get SEL_ARG tree, if any, for the keypart covering non grouping
|
||||
attribute (NGA) field 'nga_field'.
|
||||
|
||||
This function enforces the NGA3 test: If 'keypart_tree' contains a
|
||||
condition for 'nga_field', there can only be one range. In the
|
||||
opposite case, this function returns with error and 'cur_range'
|
||||
should not be used.
|
||||
|
||||
Note that the NGA1 and NGA2 requirements, like whether or not the
|
||||
range predicate for 'nga_field' is equality, is not tested by this
|
||||
function.
|
||||
|
||||
@param[in] nga_field The NGA field we want the SEL_ARG tree for
|
||||
@param[in] keypart_tree Root node of the SEL_ARG* tree for the index
|
||||
@param[out] cur_range The SEL_ARG tree, if any, for the keypart
|
||||
covering field 'keypart_field'
|
||||
@retval true 'keypart_tree' contained a predicate for 'nga_field' but
|
||||
multiple ranges exists. 'cur_range' should not be used.
|
||||
@retval false otherwise
|
||||
*/
|
||||
|
||||
static bool
|
||||
get_sel_arg_for_keypart(Field *nga_field,
|
||||
SEL_ARG *keypart_tree,
|
||||
SEL_ARG **cur_range)
|
||||
{
|
||||
if(keypart_tree->field->eq(nga_field))
|
||||
{
|
||||
/*
|
||||
Enforce NGA3: If a condition for nga_field has been found, only
|
||||
a single range is allowed.
|
||||
*/
|
||||
if (keypart_tree->prev || keypart_tree->next)
|
||||
return true; // There are multiple ranges
|
||||
|
||||
*cur_range= keypart_tree;
|
||||
return false;
|
||||
}
|
||||
|
||||
SEL_ARG *found_tree= NULL;
|
||||
SEL_ARG *first_kp= keypart_tree->first();
|
||||
|
||||
for (SEL_ARG *cur_kp= first_kp; cur_kp && !found_tree;
|
||||
cur_kp= cur_kp->next)
|
||||
{
|
||||
if (cur_kp->next_key_part)
|
||||
{
|
||||
if (get_sel_arg_for_keypart(nga_field,
|
||||
cur_kp->next_key_part,
|
||||
&found_tree))
|
||||
return true;
|
||||
|
||||
}
|
||||
/*
|
||||
Enforce NGA3: If a condition for nga_field has been found,only
|
||||
a single range is allowed.
|
||||
*/
|
||||
if (found_tree && first_kp->next)
|
||||
return true; // There are multiple ranges
|
||||
}
|
||||
*cur_range= found_tree;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Extract a sequence of constants from a conjunction of equality predicates.
|
||||
|
||||
|
@ -10070,12 +10138,13 @@ check_group_min_max_predicates(COND *cond, Item_field *min_max_arg_item,
|
|||
key_infix [out] Infix of constants to be used for index lookup
|
||||
key_infix_len [out] Lenghth of the infix
|
||||
first_non_infix_part [out] The first keypart after the infix (if any)
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
Test conditions (NGA1, NGA2) from get_best_group_min_max(). Namely,
|
||||
for each keypart field NGF_i not in GROUP-BY, check that there is a
|
||||
constant equality predicate among conds with the form (NGF_i = const_ci) or
|
||||
(const_ci = NGF_i).
|
||||
Test conditions (NGA1, NGA2, NGA3) from get_best_group_min_max(). Namely,
|
||||
for each keypart field NG_i not in GROUP-BY, check that there is exactly one
|
||||
constant equality predicate among conds with the form (NG_i = const_ci) or
|
||||
(const_ci = NG_i).. In addition, there can only be one range when there is
|
||||
such a gap.
|
||||
Thus all the NGF_i attributes must fill the 'gap' between the last group-by
|
||||
attribute and the MIN/MAX attribute in the index (if present). If these
|
||||
conditions hold, copy each constant from its corresponding predicate into
|
||||
|
@ -10104,16 +10173,14 @@ get_constant_key_infix(KEY *index_info, SEL_ARG *index_range_tree,
|
|||
uchar *key_ptr= key_infix;
|
||||
for (cur_part= first_non_group_part; cur_part != end_part; cur_part++)
|
||||
{
|
||||
cur_range= NULL;
|
||||
/*
|
||||
Find the range tree for the current keypart. We assume that
|
||||
index_range_tree points to the leftmost keypart in the index.
|
||||
index_range_tree points to the first keypart in the index.
|
||||
*/
|
||||
for (cur_range= index_range_tree; cur_range;
|
||||
cur_range= cur_range->next_key_part)
|
||||
{
|
||||
if (cur_range->field->eq(cur_part->field))
|
||||
break;
|
||||
}
|
||||
if(get_sel_arg_for_keypart(cur_part->field, index_range_tree, &cur_range))
|
||||
return false;
|
||||
|
||||
if (!cur_range)
|
||||
{
|
||||
if (min_max_arg_part)
|
||||
|
@ -10125,9 +10192,6 @@ get_constant_key_infix(KEY *index_info, SEL_ARG *index_range_tree,
|
|||
}
|
||||
}
|
||||
|
||||
/* Check that the current range tree is a single point interval. */
|
||||
if (cur_range->prev || cur_range->next)
|
||||
return FALSE; /* This is not the only range predicate for the field. */
|
||||
if ((cur_range->min_flag & NO_MIN_RANGE) ||
|
||||
(cur_range->max_flag & NO_MAX_RANGE) ||
|
||||
(cur_range->min_flag & NEAR_MIN) || (cur_range->max_flag & NEAR_MAX))
|
||||
|
|
Loading…
Add table
Reference in a new issue