Commit 3930dbf7 authored by Neeraj Bisht's avatar Neeraj Bisht

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 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.
parent 65af83f6
......@@ -9219,13 +9219,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
......@@ -9841,6 +9843,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.
......@@ -9855,12 +9923,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
......@@ -9889,16 +9958,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)
......@@ -9910,9 +9977,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))
......
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