Commit 37e049db authored by igor@rurik.mysql.com's avatar igor@rurik.mysql.com

Fixed bug #18206.

The bug report revealed two problems related to min/max optimization:
1. If the length of a constant key used in a SARGable condition for
for the MIN/MAX fields is greater than the length of the field an 
unwanted warning on key truncation is issued;
2. If MIN/MAX optimization is applied to a partial index, like INDEX(b(4))
than can lead to returning a wrong result set.
parent de8a1b4f
...@@ -916,3 +916,27 @@ select count(*), min(7), max(7) from t2m, t1i; ...@@ -916,3 +916,27 @@ select count(*), min(7), max(7) from t2m, t1i;
count(*) min(7) max(7) count(*) min(7) max(7)
0 NULL NULL 0 NULL NULL
drop table t1m, t1i, t2m, t2i; drop table t1m, t1i, t2m, t2i;
CREATE TABLE t1 (id int PRIMARY KEY, b char(3), INDEX(b));
INSERT INTO t1 VALUES (1,'xx'), (2,'aa');
SELECT * FROM t1;
id b
1 xx
2 aa
SELECT MAX(b) FROM t1 WHERE b < 'ppppp';
MAX(b)
aa
SHOW WARNINGS;
Level Code Message
SELECT MAX(b) FROM t1 WHERE b < 'pp';
MAX(b)
aa
DROP TABLE t1;
CREATE TABLE t1 (id int PRIMARY KEY, b char(16), INDEX(b(4)));
INSERT INTO t1 VALUES (1, 'xxxxbbbb'), (2, 'xxxxaaaa');
SELECT MAX(b) FROM t1;
MAX(b)
xxxxbbbb
EXPLAIN SELECT MAX(b) FROM t1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 2
DROP TABLE t1;
...@@ -598,4 +598,23 @@ select count(*), min(7), max(7) from t2m, t1i; ...@@ -598,4 +598,23 @@ select count(*), min(7), max(7) from t2m, t1i;
drop table t1m, t1i, t2m, t2i; drop table t1m, t1i, t2m, t2i;
#
# Bug #18206: min/max optimization cannot be applied to partial index
#
CREATE TABLE t1 (id int PRIMARY KEY, b char(3), INDEX(b));
INSERT INTO t1 VALUES (1,'xx'), (2,'aa');
SELECT * FROM t1;
SELECT MAX(b) FROM t1 WHERE b < 'ppppp';
SHOW WARNINGS;
SELECT MAX(b) FROM t1 WHERE b < 'pp';
DROP TABLE t1;
CREATE TABLE t1 (id int PRIMARY KEY, b char(16), INDEX(b(4)));
INSERT INTO t1 VALUES (1, 'xxxxbbbb'), (2, 'xxxxaaaa');
SELECT MAX(b) FROM t1;
EXPLAIN SELECT MAX(b) FROM t1;
DROP TABLE t1;
# End of 4.1 tests # End of 4.1 tests
...@@ -543,6 +543,10 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, ...@@ -543,6 +543,10 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo,
break; // Found a part od the key for the field break; // Found a part od the key for the field
} }
#if 0
if (part->length != (((Item_field*) args[0])->field)->field_length)
return 0;
#endif
bool is_field_part= part == field_part; bool is_field_part= part == field_part;
if (!(is_field_part || eq_type)) if (!(is_field_part || eq_type))
return 0; return 0;
...@@ -582,7 +586,8 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, ...@@ -582,7 +586,8 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo,
} }
else else
{ {
store_val_in_field(part->field, args[between && max_fl ? 2 : 1]); store_val_in_field(part->field, args[between && max_fl ? 2 : 1],
CHECK_FIELD_IGNORE);
if (part->null_bit) if (part->null_bit)
*key_ptr++= (byte) test(part->field->is_null()); *key_ptr++= (byte) test(part->field->is_null());
part->field->get_key_image((char*) key_ptr, part->length, part->field->get_key_image((char*) key_ptr, part->length,
...@@ -638,6 +643,8 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, ...@@ -638,6 +643,8 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo,
field BETWEEN const1 AND const2 field BETWEEN const1 AND const2
3. all references to the columns from the same table as column field 3. all references to the columns from the same table as column field
occur only in conjucts mentioned above. occur only in conjucts mentioned above.
4. each of k first components the index is not partial, i.e. is not
defined on a fixed length proper prefix of the field.
If such an index exists the function through the ref parameter If such an index exists the function through the ref parameter
returns the key value to find max/min for the field using the index, returns the key value to find max/min for the field using the index,
...@@ -647,8 +654,8 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo, ...@@ -647,8 +654,8 @@ static bool matching_cond(bool max_fl, TABLE_REF *ref, KEY *keyinfo,
of the whole search key) of the whole search key)
NOTE NOTE
This function may set table->key_read to 1, which must be reset after This function may set table->key_read to 1, which must be reset after
index is used! (This can only happen when function returns 1) index is used! (This can only happen when function returns 1)
RETURN RETURN
0 Index can not be used to optimize MIN(field)/MAX(field) 0 Index can not be used to optimize MIN(field)/MAX(field)
...@@ -682,6 +689,10 @@ static bool find_key_for_maxmin(bool max_fl, TABLE_REF *ref, ...@@ -682,6 +689,10 @@ static bool find_key_for_maxmin(bool max_fl, TABLE_REF *ref,
if (!(table->file->index_flags(idx, jdx, 0) & HA_READ_ORDER)) if (!(table->file->index_flags(idx, jdx, 0) & HA_READ_ORDER))
return 0; return 0;
/* Check whether the index component is partial */
if (part->length < table->field[part->fieldnr-1]->pack_length())
break;
if (field->eq(part->field)) if (field->eq(part->field))
{ {
ref->key= idx; ref->key= idx;
......
...@@ -3434,7 +3434,7 @@ get_store_key(THD *thd, KEYUSE *keyuse, table_map used_tables, ...@@ -3434,7 +3434,7 @@ get_store_key(THD *thd, KEYUSE *keyuse, table_map used_tables,
*/ */
bool bool
store_val_in_field(Field *field,Item *item) store_val_in_field(Field *field, Item *item, enum_check_fields check_flag)
{ {
bool error; bool error;
THD *thd=current_thd; THD *thd=current_thd;
...@@ -3445,7 +3445,7 @@ store_val_in_field(Field *field,Item *item) ...@@ -3445,7 +3445,7 @@ store_val_in_field(Field *field,Item *item)
with select_insert, which make count_cuted_fields= 1 with select_insert, which make count_cuted_fields= 1
*/ */
enum_check_fields old_count_cuted_fields= thd->count_cuted_fields; enum_check_fields old_count_cuted_fields= thd->count_cuted_fields;
thd->count_cuted_fields= CHECK_FIELD_WARN; thd->count_cuted_fields= check_flag;
error= item->save_in_field(field, 1); error= item->save_in_field(field, 1);
thd->count_cuted_fields= old_count_cuted_fields; thd->count_cuted_fields= old_count_cuted_fields;
return error || cuted_fields != thd->cuted_fields; return error || cuted_fields != thd->cuted_fields;
...@@ -7097,7 +7097,7 @@ static bool test_if_ref(Item_field *left_item,Item *right_item) ...@@ -7097,7 +7097,7 @@ static bool test_if_ref(Item_field *left_item,Item *right_item)
field->real_type() != FIELD_TYPE_VAR_STRING && field->real_type() != FIELD_TYPE_VAR_STRING &&
(field->type() != FIELD_TYPE_FLOAT || field->decimals() == 0)) (field->type() != FIELD_TYPE_FLOAT || field->decimals() == 0))
{ {
return !store_val_in_field(field,right_item); return !store_val_in_field(field, right_item, CHECK_FIELD_WARN);
} }
} }
} }
......
...@@ -335,7 +335,7 @@ extern const char *join_type_str[]; ...@@ -335,7 +335,7 @@ extern const char *join_type_str[];
void TEST_join(JOIN *join); void TEST_join(JOIN *join);
/* Extern functions in sql_select.cc */ /* Extern functions in sql_select.cc */
bool store_val_in_field(Field *field,Item *val); bool store_val_in_field(Field *field, Item *val, enum_check_fields check_flag);
TABLE *create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, TABLE *create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
ORDER *group, bool distinct, bool save_sum_fields, ORDER *group, bool distinct, bool save_sum_fields,
ulong select_options, ha_rows rows_limit, ulong select_options, ha_rows rows_limit,
......
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