From: Jorgen Loland Date: September 9 2010 10:59am Subject: bzr push into mysql-next-mr-bugfixing branch (jorgen.loland:3275 to 3276) Bug#53562 List-Archive: http://lists.mysql.com/commits/117837 X-Bug: 53562 Message-Id: <20100909105911.BE2DD208A@atum21.norway.sun.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============8709965601655372959==" --===============8709965601655372959== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline 3276 Jorgen Loland 2010-09-09 BUG#53562: EXPLAIN statement should hint when index is not used due to type conversion An index may not be used for ref or range access if a) a type conversion is needed to compare an indexed field to a value, or b) there is a collation mismatch between an indexed field and the value compared to Before, the index would not be used, but no hint was given that this was the case. With this patch, EXPLAIN EXTENDED will issue a warning for the cases above. Note that even if an index cannot be used for ref or range access, the index may still be scanned. @ mysql-test/r/explain.result Added test for BUG#53562 @ mysql-test/t/explain.test Added test for BUG#53562 @ sql/opt_range.cc Make EXPLAIN EXTENDED issue a warning if an index cannot be used for range access due to type conversion of collation mismatch. @ sql/share/errmsg-utf8.txt Added ER_WARN_INDEX_NOT_APPLICABLE for use when an index cannot be used for ref or range access due to type conversion or collation mismatch. @ sql/sql_select.cc Make EXPLAIN EXTENDED issue a warning if an index cannot be used for ref access due to type conversion of collation mismatch. modified: mysql-test/r/explain.result mysql-test/t/explain.test sql/opt_range.cc sql/share/errmsg-utf8.txt sql/sql_select.cc 3275 Alexey Botchkov 2010-09-08 [merge] merging. modified: client/mysqltest.cc libmysqld/lib_sql.cc sql/derror.cc sql/mysqld.cc sql/set_var.cc sql/set_var.h sql/sys_vars.h === modified file 'mysql-test/r/explain.result' --- a/mysql-test/r/explain.result 2010-08-26 12:02:59 +0000 +++ b/mysql-test/r/explain.result 2010-09-09 10:58:40 +0000 @@ -324,4 +324,77 @@ id select_type table type possible_keys 25 UNION NULL NULL NULL NULL NULL NULL NULL No tables used NULL UNION RESULT ALL NULL NULL NULL NULL NULL # End BUG#30597 +# +# BUG#53562: EXPLAIN statement should hint when +# index is not used due to type conversion +# +CREATE TABLE t1 (url char(1) PRIMARY KEY); +INSERT INTO t1 VALUES ('1'),('2'),('3'),('4'),('5'); + +# Normally, lookup access on primary key is done +EXPLAIN EXTENDED SELECT * FROM t1 WHERE url='1'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 const PRIMARY PRIMARY 1 const 1 100.00 Using index +Warnings: +Note 1003 select '1' AS `url` from `test`.`t1` where 1 + +# Test that index can't be used for lookup due to type conversion +# (comparing char and int) +SELECT * FROM t1 WHERE url=1; +url +1 +EXPLAIN EXTENDED SELECT * FROM t1 WHERE url=1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index PRIMARY PRIMARY 1 NULL 5 100.00 Using where; Using index +Warnings: +Warning 1708 Cannot use ref access on index 'PRIMARY' due to type or collation conversion on field 'url' +Warning 1708 Cannot use range access on index 'PRIMARY' due to type or collation conversion on field 'url' +Note 1003 select `test`.`t1`.`url` AS `url` from `test`.`t1` where (`test`.`t1`.`url` = 1) + +# Test that index can't be used for lookup due to collation mismatch +SELECT * FROM t1 WHERE url='1' collate latin1_german2_ci; +url +1 +EXPLAIN EXTENDED SELECT * FROM t1 WHERE url='1' collate latin1_german2_ci; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index PRIMARY PRIMARY 1 NULL 5 100.00 Using where; Using index +Warnings: +Warning 1708 Cannot use ref access on index 'PRIMARY' due to type or collation conversion on field 'url' +Warning 1708 Cannot use range access on index 'PRIMARY' due to type or collation conversion on field 'url' +Note 1003 select `test`.`t1`.`url` AS `url` from `test`.`t1` where (`test`.`t1`.`url` = (('1' collate latin1_german2_ci))) + +# Normally, range access on primary key is done +EXPLAIN EXTENDED SELECT * FROM t1 WHERE url>'3'; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range PRIMARY PRIMARY 1 NULL 3 100.00 Using where; Using index +Warnings: +Note 1003 select `test`.`t1`.`url` AS `url` from `test`.`t1` where (`test`.`t1`.`url` > '3') + +# Test that range access on index can't be done due to type conversion +# (comparing char and int) +SELECT * FROM t1 WHERE url>3; +url +4 +5 +EXPLAIN EXTENDED SELECT * FROM t1 WHERE url>3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index PRIMARY PRIMARY 1 NULL 5 100.00 Using where; Using index +Warnings: +Warning 1708 Cannot use range access on index 'PRIMARY' due to type or collation conversion on field 'url' +Note 1003 select `test`.`t1`.`url` AS `url` from `test`.`t1` where (`test`.`t1`.`url` > 3) + +# Test that range access on index can't be done due to collation mismatch +SELECT * FROM t1 WHERE url>'3' collate latin1_german2_ci; +url +4 +5 +EXPLAIN EXTENDED SELECT * FROM t1 WHERE url>'3' collate latin1_german2_ci; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index PRIMARY PRIMARY 1 NULL 5 100.00 Using where; Using index +Warnings: +Warning 1708 Cannot use range access on index 'PRIMARY' due to type or collation conversion on field 'url' +Note 1003 select `test`.`t1`.`url` AS `url` from `test`.`t1` where (`test`.`t1`.`url` > (('3' collate latin1_german2_ci))) + +DROP TABLE t1; +# End BUG#53562 End of 6.0 tests. === modified file 'mysql-test/t/explain.test' --- a/mysql-test/t/explain.test 2010-08-26 12:02:59 +0000 +++ b/mysql-test/t/explain.test 2010-09-09 10:58:40 +0000 @@ -279,4 +279,43 @@ EXPLAIN --echo # End BUG#30597 +--echo # +--echo # BUG#53562: EXPLAIN statement should hint when +--echo # index is not used due to type conversion +--echo # + +CREATE TABLE t1 (url char(1) PRIMARY KEY); +INSERT INTO t1 VALUES ('1'),('2'),('3'),('4'),('5'); + +--echo +--echo # Normally, lookup access on primary key is done +EXPLAIN EXTENDED SELECT * FROM t1 WHERE url='1'; +--echo +--echo # Test that index can't be used for lookup due to type conversion +--echo # (comparing char and int) +SELECT * FROM t1 WHERE url=1; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE url=1; +--echo +--echo # Test that index can't be used for lookup due to collation mismatch +SELECT * FROM t1 WHERE url='1' collate latin1_german2_ci; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE url='1' collate latin1_german2_ci; + +--echo +--echo # Normally, range access on primary key is done +EXPLAIN EXTENDED SELECT * FROM t1 WHERE url>'3'; +--echo +--echo # Test that range access on index can't be done due to type conversion +--echo # (comparing char and int) +SELECT * FROM t1 WHERE url>3; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE url>3; +--echo +--echo # Test that range access on index can't be done due to collation mismatch +SELECT * FROM t1 WHERE url>'3' collate latin1_german2_ci; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE url>'3' collate latin1_german2_ci; + +--echo +DROP TABLE t1; + +--echo # End BUG#53562 + --echo End of 6.0 tests. === modified file 'sql/opt_range.cc' --- a/sql/opt_range.cc 2010-08-26 14:32:33 +0000 +++ b/sql/opt_range.cc 2010-09-09 10:58:40 +0000 @@ -5772,7 +5772,18 @@ get_mm_leaf(RANGE_OPT_PARAM *param, Item ((Field_str*)field)->charset() != conf_func->compare_collation() && !(conf_func->compare_collation()->state & MY_CS_BINSORT && (type == Item_func::EQUAL_FUNC || type == Item_func::EQ_FUNC))) + { + if (param->thd->lex->describe & DESCRIBE_EXTENDED) + push_warning_printf( + param->thd, + MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_INDEX_NOT_APPLICABLE, + ER(ER_WARN_INDEX_NOT_APPLICABLE), + "range", + field->table->key_info[param->real_keynr[key_part->key]].name, + field->field_name); goto end; + } if (key_part->image_type == Field::itMBR) { @@ -5893,7 +5904,18 @@ get_mm_leaf(RANGE_OPT_PARAM *param, Item if (field->result_type() == STRING_RESULT && value->result_type() != STRING_RESULT && field->cmp_type() != value->result_type()) + { + if (param->thd->lex->describe & DESCRIBE_EXTENDED) + push_warning_printf( + param->thd, + MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_INDEX_NOT_APPLICABLE, + ER(ER_WARN_INDEX_NOT_APPLICABLE), + "range", + field->table->key_info[param->real_keynr[key_part->key]].name, + field->field_name); goto end; + } /* For comparison purposes allow invalid dates like 2000-01-32 */ orig_sql_mode= field->table->in_use->variables.sql_mode; if (value->real_item()->type() == Item::STRING_ITEM && === modified file 'sql/share/errmsg-utf8.txt' --- a/sql/share/errmsg-utf8.txt 2010-07-29 14:15:38 +0000 +++ b/sql/share/errmsg-utf8.txt 2010-09-09 10:58:40 +0000 @@ -6387,3 +6387,5 @@ ER_TABLES_DIFFERENT_METADATA ER_ROW_DOES_NOT_MATCH_PARTITION eng "Found row that does not match the partition" swe "Hittade rad som inte passar i partitionen" +ER_WARN_INDEX_NOT_APPLICABLE + eng "Cannot use %-.64s access on index '%-.64s' due to type or collation conversion on field '%-.64s'" === modified file 'sql/sql_select.cc' --- a/sql/sql_select.cc 2010-08-30 08:37:21 +0000 +++ b/sql/sql_select.cc 2010-09-09 10:58:40 +0000 @@ -5159,6 +5159,46 @@ static uint get_semi_join_select_list_in } /** + @brief + If EXPLAIN EXTENDED, add warning that an index cannot be used for + ref access + + @details + If EXPLAIN EXTENDED, add a warning for each index that cannot be + used for ref access due to either type conversion or different + collations on the field used for comparison + + Example type conversion (char compared to int): + + CREATE TABLE t1 (url char(1) PRIMARY KEY); + SELECT * FROM t1 WHERE url=1; + + Example different collations (danish vs german2): + + CREATE TABLE t1 (url char(1) PRIMARY KEY) collate latin1_danish_ci; + SELECT * FROM t1 WHERE url='1' collate latin1_german2_ci; + + @param thd Thread for the connection that submitted the query + @param field Field used in comparision + @param cant_use_indexes Indexes that cannot be used for lookup + */ +static void +warn_index_not_applicable(THD *thd, const Field *field, + const key_map cant_use_index) +{ + if (thd->lex->describe & DESCRIBE_EXTENDED) + for (uint j=0 ; j < field->table->s->keys ; j++) + if (cant_use_index.is_set(j)) + push_warning_printf(thd, + MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARN_INDEX_NOT_APPLICABLE, + ER(ER_WARN_INDEX_NOT_APPLICABLE), + "ref", + field->table->key_info[j].name, + field->field_name); +} + +/** Add a possible key to array of possible keys if it's usable as a key @param key_fields Pointer to add key, if usable @@ -5183,6 +5223,7 @@ add_key_field(KEY_FIELD **key_fields,uin Field *field, bool eq_func, Item **value, uint num_values, table_map usable_tables, SARGABLE_PARAM **sargables) { + DBUG_PRINT("info",("add_key_field for field %s",field->field_name)); uint exists_optimize= 0; if (!(field->flags & PART_KEY_FLAG)) { @@ -5284,7 +5325,10 @@ add_key_field(KEY_FIELD **key_fields,uin if ((*value)->result_type() != STRING_RESULT) { if (field->cmp_type() != (*value)->result_type()) + { + warn_index_not_applicable(stat->join->thd, field, possible_keys); return; + } } else { @@ -5294,7 +5338,10 @@ add_key_field(KEY_FIELD **key_fields,uin */ if (field->cmp_type() == STRING_RESULT && ((Field_str*)field)->charset() != cond->compare_collation()) + { + warn_index_not_applicable(stat->join->thd, field, possible_keys); return; + } } } } --===============8709965601655372959== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/jorgen.loland@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: jorgen.loland@stripped\ # i80sa0637fsimlrc # target_branch: file:///export/home/jl208045/mysql/mysql-next-mr-\ # bugfixing-53562/ # testament_sha1: 541117522ae423fee450901dfc76cb6459a50d37 # timestamp: 2010-09-09 12:59:11 +0200 # base_revision_id: holyfoot@stripped # # Begin bundle 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 qBAof4u5IpwoSE73rCuA --===============8709965601655372959==--