diff --git a/mysql-test/main/opt_hints.result b/mysql-test/main/opt_hints.result index 7ee6ae0a001..b1a99176f95 100644 --- a/mysql-test/main/opt_hints.result +++ b/mysql-test/main/opt_hints.result @@ -21,7 +21,7 @@ a 1 2 Warnings: -Warning 4219 Hint NO_MRR(`t1` `idx_A`) is ignored as conflicting/duplicated +Warning 4227 Hint NO_MRR(`t1` `idx_A`) is ignored as conflicting/duplicated (an index hint of the same type has already been specified for this key) Warning 4222 Unresolved index name `t1`@`select#1` `idx_å` for NO_MRR hint DROP TABLE t1; # Testing that query block names are accent sensitive case insensitive @@ -885,7 +885,7 @@ Warning 4219 Hint BKA(`t1`@`q1`) is ignored as conflicting/duplicated SELECT /*+ QB_NAME(q1) NO_ICP(@q1 t1 PRIMARY) NO_ICP(@q1 t1 PRIMARY) */ * FROM t1; i Warnings: -Warning 4219 Hint NO_ICP(`t1`@`q1` `PRIMARY`) is ignored as conflicting/duplicated +Warning 4227 Hint NO_ICP(`t1`@`q1` `PRIMARY`) is ignored as conflicting/duplicated (an index hint of the same type has already been specified for this key) DROP TABLE t1; # # Hints inside views are not supported diff --git a/mysql-test/main/opt_hints_index.result b/mysql-test/main/opt_hints_index.result new file mode 100644 index 00000000000..1865a513eed --- /dev/null +++ b/mysql-test/main/opt_hints_index.result @@ -0,0 +1,274 @@ +CREATE TABLE t1 (a INT, b INT, c INT, d INT, +KEY i_a(a), KEY i_b(b), +KEY i_ab(a,b), KEY i_c(c), KEY i_d(d)); +INSERT INTO t1 VALUES +(1,1,1,1),(2,2,2,1),(3,3,3,1),(4,4,4,1), +(5,5,5,1),(6,6,6,1),(7,7,7,1),(8,8,8,1); +INSERT INTO t1 SELECT a,b, c + 10, d FROM t1; +INSERT INTO t1 SELECT a,b, c + 20, d FROM t1; +INSERT INTO t1 SELECT a,b, c + 40, d FROM t1; +INSERT INTO t1 SELECT a,b, c + 80, d FROM t1; +INSERT INTO t1 SELECT a,b, c + 160, d FROM t1; +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze status Engine-independent statistics collected +test.t1 analyze status OK +# Check behavior of duplicated/intersected hints. +# First specified hint is applied and next conflicting/intersected hints +# are ignored with warning. +# JOIN_INDEX(t1) is ignored as intersected (INDEX/other hint for the same table) +EXPLAIN EXTENDED SELECT /*+ INDEX(t1) JOIN_INDEX(t1) */ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL i_a 5 NULL 256 100.00 Using index +Warnings: +Warning 4228 Hint JOIN_INDEX(`t1` ) is ignored as conflicting/duplicated (an index hint of the same type or opposite kind has already been specified for this table) +Note 1003 select /*+ INDEX(`t1`@`select#1`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +# INDEX(t1 i_d) is ignored as duplicated (same type of hint for the same table) +EXPLAIN EXTENDED SELECT /*+ INDEX(t1 i_a, i_b, i_c) NO_INDEX(t1 i_d) */ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL i_a 5 NULL 256 100.00 Using index +Warnings: +Warning 4226 Hint NO_INDEX(`t1` `i_d`) is ignored as conflicting/duplicated (an index hint of the same type has already been specified for this table) +Note 1003 select /*+ INDEX(`t1`@`select#1` `i_a`,`i_b`,`i_c`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +# JOIN_INDEX(t1 i_a, i_b) is ignored as intersected (INDEX/other hint for the same table) +EXPLAIN EXTENDED SELECT /*+ INDEX(t1 i_a) JOIN_INDEX(t1 i_a, i_b) JOIN_INDEX(t1 i_b) */ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL i_a 5 NULL 256 100.00 Using index +Warnings: +Warning 4229 Hint JOIN_INDEX(`t1` `i_a`,`i_b`) is ignored as conflicting/duplicated (an index hint of the same type or opposite kind has already been specified for the key) +Note 1003 select /*+ INDEX(`t1`@`select#1` `i_a`) JOIN_INDEX(`t1`@`select#1` `i_b`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +# GROUP_INDEX(t1 i_a, i_b) is ignored as intersected (INDEX/other hint for the same table) +EXPLAIN EXTENDED SELECT /*+ INDEX(t1 i_a) GROUP_INDEX(t1 i_a, i_b) GROUP_INDEX(t1 i_b) */ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL i_a 5 NULL 256 100.00 Using index +Warnings: +Warning 4229 Hint GROUP_INDEX(`t1` `i_a`,`i_b`) is ignored as conflicting/duplicated (an index hint of the same type or opposite kind has already been specified for the key) +Note 1003 select /*+ INDEX(`t1`@`select#1` `i_a`) GROUP_INDEX(`t1`@`select#1` `i_b`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +# GROUP_INDEX(t1) is ignored as intersected (INDEX/other hint for the same table) +EXPLAIN EXTENDED SELECT /*+ INDEX(t1 i_a) GROUP_INDEX(t1) */ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL i_a 5 NULL 256 100.00 Using index +Warnings: +Warning 4228 Hint GROUP_INDEX(`t1` ) is ignored as conflicting/duplicated (an index hint of the same type or opposite kind has already been specified for this table) +Note 1003 select /*+ INDEX(`t1`@`select#1` `i_a`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +# ORDER_INDEX(t1 i_a, i_b) is ignored as intersected (INDEX/other hint for the same key) +EXPLAIN EXTENDED SELECT /*+ INDEX(t1 i_a) ORDER_INDEX(t1 i_a, i_b) ORDER_INDEX(t1 i_b) */ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL i_a 5 NULL 256 100.00 Using index +Warnings: +Warning 4229 Hint ORDER_INDEX(`t1` `i_a`,`i_b`) is ignored as conflicting/duplicated (an index hint of the same type or opposite kind has already been specified for the key) +Note 1003 select /*+ INDEX(`t1`@`select#1` `i_a`) ORDER_INDEX(`t1`@`select#1` `i_b`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +# INDEX(t1 i_b,i_a) is ignored as intersected (INDEX/other hint for the same key) +EXPLAIN EXTENDED SELECT /*+ ORDER_INDEX(t1 i_a) INDEX(t1 i_b,i_a)*/ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL i_a 5 NULL 256 100.00 Using index +Warnings: +Warning 4229 Hint INDEX(`t1` `i_b`,`i_a`) is ignored as conflicting/duplicated (an index hint of the same type or opposite kind has already been specified for the key) +Note 1003 select /*+ ORDER_INDEX(`t1`@`select#1` `i_a`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +# ORDER_INDEX(t1) is ignored as intersected (INDEX/other hint for the same table) +EXPLAIN EXTENDED SELECT /*+ INDEX(t1 i_a) ORDER_INDEX(t1) */ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL i_a 5 NULL 256 100.00 Using index +Warnings: +Warning 4228 Hint ORDER_INDEX(`t1` ) is ignored as conflicting/duplicated (an index hint of the same type or opposite kind has already been specified for this table) +Note 1003 select /*+ INDEX(`t1`@`select#1` `i_a`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +# ORDER_INDEX(t1 i_b) is ignored as intersected (same hint/INDEX for the same table) +EXPLAIN EXTENDED SELECT /*+ ORDER_INDEX(t1 i_a, i_b) NO_ORDER_INDEX(t1 i_b) INDEX(t1 i_c)*/ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL i_a 5 NULL 256 100.00 Using index +Warnings: +Warning 4226 Hint NO_ORDER_INDEX(`t1` `i_b`) is ignored as conflicting/duplicated (an index hint of the same type has already been specified for this table) +Note 1003 select /*+ INDEX(`t1`@`select#1` `i_c`) ORDER_INDEX(`t1`@`select#1` `i_a`,`i_b`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +# INDEX(t1) is ignored as intersected (INDEX/other hint for the same table) +EXPLAIN EXTENDED SELECT /*+ ORDER_INDEX(t1 i_a, i_b) GROUP_INDEX(t1 i_b) INDEX(t1)*/ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL i_a 5 NULL 256 100.00 Using index +Warnings: +Warning 4228 Hint INDEX(`t1` ) is ignored as conflicting/duplicated (an index hint of the same type or opposite kind has already been specified for this table) +Note 1003 select /*+ GROUP_INDEX(`t1`@`select#1` `i_b`) ORDER_INDEX(`t1`@`select#1` `i_a`,`i_b`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +# INDEX(t1) is ignored as intersected (INDEX/other hint for the same table) +EXPLAIN EXTENDED SELECT /*+ ORDER_INDEX(t1) GROUP_INDEX(t1) INDEX(t1)*/ a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL i_a 5 NULL 256 100.00 Using index +Warnings: +Warning 4228 Hint INDEX(`t1` ) is ignored as conflicting/duplicated (an index hint of the same type or opposite kind has already been specified for this table) +Note 1003 select /*+ GROUP_INDEX(`t1`@`select#1`) ORDER_INDEX(`t1`@`select#1`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +# Check the use of index hints. +# Force the use of i_a, i_b indexes, intersect(i_a,i_b) is used. +EXPLAIN EXTENDED SELECT /*+ INDEX(t1 i_a, i_b) */ a FROM t1 WHERE a = 1 AND b = 2 AND c = 3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index_merge i_a,i_b i_a,i_b 5,5 NULL 2 0.39 Using intersect(i_a,i_b); Using where +Warnings: +Note 1003 select /*+ INDEX(`t1`@`select#1` `i_a`,`i_b`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 1 and `test`.`t1`.`b` = 2 and `test`.`t1`.`c` = 3 +# Force the use of i_a, i_ab indexes, i_ab index is used. +EXPLAIN EXTENDED SELECT /*+ INDEX(t1 i_a, i_ab) */ a FROM t1 WHERE a = 1 AND b = 2 AND c = 3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ref i_a,i_ab i_ab 10 const,const 1 0.39 Using where +Warnings: +Note 1003 select /*+ INDEX(`t1`@`select#1` `i_a`,`i_ab`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 1 and `test`.`t1`.`b` = 2 and `test`.`t1`.`c` = 3 +# Force the use of i_a, i_b, i_c indexes, i_c index is used. +EXPLAIN EXTENDED SELECT /*+ INDEX(t1 i_a, i_b) JOIN_INDEX(t1 i_c) */ * FROM t1 WHERE a = 1 AND b = 2 AND c = 3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ref i_a,i_b,i_c i_c 5 const 1 1.03 Using where +Warnings: +Note 1003 select /*+ INDEX(`t1`@`select#1` `i_a`,`i_b`) JOIN_INDEX(`t1`@`select#1` `i_c`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`test`.`t1`.`d` AS `d` from `test`.`t1` where `test`.`t1`.`a` = 1 and `test`.`t1`.`b` = 2 and `test`.`t1`.`c` = 3 +# Test range index access +EXPLAIN EXTENDED SELECT a FROM t1 WHERE a > 1 AND a < 3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range i_a,i_ab i_a 5 NULL 26 100.00 Using where; Using index +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` > 1 and `test`.`t1`.`a` < 3 +# Indexes are forbidden, full scan is employed +EXPLAIN EXTENDED SELECT /*+ NO_INDEX(t1)*/a FROM t1 WHERE a > 1 AND a < 3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 256 12.50 Using where +Warnings: +Note 1003 select /*+ NO_INDEX(`t1`@`select#1`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` > 1 and `test`.`t1`.`a` < 3 +EXPLAIN EXTENDED SELECT /*+ NO_JOIN_INDEX(t1)*/a FROM t1 WHERE a > 1 AND a < 3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL i_a 5 NULL 256 12.50 Using where; Using index +Warnings: +Note 1003 select /*+ NO_JOIN_INDEX(`t1`@`select#1`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` > 1 and `test`.`t1`.`a` < 3 +# Indexes are forbidden for grouping and ordering but are usable for data access +EXPLAIN EXTENDED SELECT /*+ NO_GROUP_INDEX(t1) NO_ORDER_INDEX(t1)*/a FROM t1 WHERE a > 1 AND a < 3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range i_a,i_ab i_a 5 NULL 26 100.00 Using where; Using index +Warnings: +Note 1003 select /*+ NO_GROUP_INDEX(`t1`@`select#1`) NO_ORDER_INDEX(`t1`@`select#1`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` > 1 and `test`.`t1`.`a` < 3 +# Index "i_a" is forbidden, "i_ab" is used instead +EXPLAIN EXTENDED SELECT /*+ NO_INDEX(t1 i_a)*/a FROM t1 WHERE a > 1 AND a < 3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range i_ab i_ab 5 NULL 36 100.00 Using where; Using index +Warnings: +Note 1003 select /*+ NO_INDEX(`t1`@`select#1` `i_a`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` > 1 and `test`.`t1`.`a` < 3 +# Usable indexes are forbidden, full scan is employed +EXPLAIN EXTENDED SELECT /*+ NO_INDEX(t1 i_a, i_ab)*/a FROM t1 WHERE a > 1 AND a < 3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 256 12.50 Using where +Warnings: +Note 1003 select /*+ NO_INDEX(`t1`@`select#1` `i_a`,`i_ab`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` > 1 and `test`.`t1`.`a` < 3 +EXPLAIN EXTENDED SELECT /*+ INDEX(t1 i_ab)*/a FROM t1 WHERE a > 1 AND a < 3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range i_ab i_ab 5 NULL 36 100.00 Using where; Using index +Warnings: +Note 1003 select /*+ INDEX(`t1`@`select#1` `i_ab`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` > 1 and `test`.`t1`.`a` < 3 +# Full scan is more efficient for this query, so indexes are not used by default: +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a < 8; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL i_a,i_ab NULL NULL NULL 256 67.19 Using where +Warnings: +Note 1003 select `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`test`.`t1`.`d` AS `d` from `test`.`t1` where `test`.`t1`.`a` < 8 +# Force using of any index: +EXPLAIN EXTENDED SELECT /*+ index(t1)*/* FROM t1 WHERE a < 8; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range i_a,i_ab i_a 5 NULL 172 100.00 Using index condition +Warnings: +Note 1003 select /*+ INDEX(`t1`@`select#1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`test`.`t1`.`d` AS `d` from `test`.`t1` where `test`.`t1`.`a` < 8 +# Force using of a particular index: +EXPLAIN EXTENDED SELECT /*+ index(t1 i_a)*/* FROM t1 WHERE a < 8; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range i_a i_a 5 NULL 172 100.00 Using index condition +Warnings: +Note 1003 select /*+ INDEX(`t1`@`select#1` `i_a`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`test`.`t1`.`d` AS `d` from `test`.`t1` where `test`.`t1`.`a` < 8 +# Ignore i_ab index, i_b index is used. +EXPLAIN EXTENDED SELECT /*+ NO_INDEX(t1 i_ab) */ a, max(b) FROM t1 WHERE b = 2 GROUP BY a; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ref i_b i_b 5 const 26 100.00 Using where; Using temporary; Using filesort +Warnings: +Note 1003 select /*+ NO_INDEX(`t1`@`select#1` `i_ab`) */ `test`.`t1`.`a` AS `a`,max(`test`.`t1`.`b`) AS `max(b)` from `test`.`t1` where `test`.`t1`.`b` = 2 group by `test`.`t1`.`a` +# Ignore i_ab index, i_b index is used. +EXPLAIN EXTENDED SELECT /*+ NO_JOIN_INDEX(t1 i_ab) */ a, max(b) FROM t1 WHERE b = 2 GROUP BY a; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ref i_b i_b 5 const 26 100.00 Using where; Using temporary; Using filesort +Warnings: +Note 1003 select /*+ NO_JOIN_INDEX(`t1`@`select#1` `i_ab`) */ `test`.`t1`.`a` AS `a`,max(`test`.`t1`.`b`) AS `max(b)` from `test`.`t1` where `test`.`t1`.`b` = 2 group by `test`.`t1`.`a` +# Force i_ab index for GROUP BY, i_ab index scan is used. +EXPLAIN EXTENDED SELECT /*+ GROUP_INDEX(t1 i_ab) */ a, max(b) FROM t1 GROUP BY a; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range NULL i_ab 5 NULL 9 100.00 Using index for group-by +Warnings: +Note 1003 select /*+ GROUP_INDEX(`t1`@`select#1` `i_ab`) */ `test`.`t1`.`a` AS `a`,max(`test`.`t1`.`b`) AS `max(b)` from `test`.`t1` group by `test`.`t1`.`a` +# Force i_ab index for JOIN, i_ab loose index scan is used. +EXPLAIN EXTENDED SELECT /*+ JOIN_INDEX(t1 i_ab) */ a, max(b) FROM t1 GROUP BY a; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range NULL i_ab 5 NULL 9 100.00 Using index for group-by +Warnings: +Note 1003 select /*+ JOIN_INDEX(`t1`@`select#1` `i_ab`) */ `test`.`t1`.`a` AS `a`,max(`test`.`t1`.`b`) AS `max(b)` from `test`.`t1` group by `test`.`t1`.`a` +# Ignore i_ab for sorting rows. i_a index is used for sorting. +EXPLAIN EXTENDED SELECT /*+ NO_ORDER_INDEX(t1 i_ab) */ a FROM t1 ORDER BY a; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL i_a 5 NULL 256 100.00 Using index +Warnings: +Note 1003 select /*+ NO_ORDER_INDEX(`t1`@`select#1` `i_ab`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` order by `test`.`t1`.`a` +# Ignore i_a for sorting rows. i_ab is used for sorting. +EXPLAIN EXTENDED SELECT /*+ NO_ORDER_INDEX(t1 i_a) */ a FROM t1 ORDER BY a; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL i_ab 10 NULL 256 100.00 Using index +Warnings: +Note 1003 select /*+ NO_ORDER_INDEX(`t1`@`select#1` `i_a`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` order by `test`.`t1`.`a` +# Force i_ab index for sorting rows. +EXPLAIN EXTENDED SELECT /*+ ORDER_INDEX(t1 i_ab) */ a FROM t1 ORDER BY a; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL i_ab 10 NULL 256 100.00 Using index +Warnings: +Note 1003 select /*+ ORDER_INDEX(`t1`@`select#1` `i_ab`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` order by `test`.`t1`.`a` +# Force i_a index for sorting rows. +EXPLAIN EXTENDED SELECT /*+ ORDER_INDEX(t1 i_a) */ a FROM t1 ORDER BY a; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL i_a 5 NULL 256 100.00 Using index +Warnings: +Note 1003 select /*+ ORDER_INDEX(`t1`@`select#1` `i_a`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` order by `test`.`t1`.`a` +# Ignore all indexes. +EXPLAIN EXTENDED SELECT /*+ NO_INDEX(t1) */ * FROM t1 WHERE a = 1 AND b = 2 AND c = 3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 256 0.01 Using where +Warnings: +Note 1003 select /*+ NO_INDEX(`t1`@`select#1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`test`.`t1`.`d` AS `d` from `test`.`t1` where `test`.`t1`.`a` = 1 and `test`.`t1`.`b` = 2 and `test`.`t1`.`c` = 3 +# Check if old-style hints work if no new hints are specified. +EXPLAIN EXTENDED SELECT /*+ NO_MRR(t1) */ * FROM t1 IGNORE INDEX (i_a) +WHERE a = 1 AND b = 2 AND c = 3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ref|filter i_b,i_ab,i_c i_ab|i_c 10|5 const,const 1 (0%) 0.39 Using where; Using rowid filter +Warnings: +Note 1003 select /*+ NO_MRR(`t1`@`select#1`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`test`.`t1`.`d` AS `d` from `test`.`t1` IGNORE INDEX (`i_a`) where `test`.`t1`.`a` = 1 and `test`.`t1`.`b` = 2 and `test`.`t1`.`c` = 3 +# Check that old-style hint is silently ignored if a new hint is specified. +EXPLAIN EXTENDED SELECT /*+ INDEX(t1 i_a) */ * FROM t1 IGNORE INDEX(i_a) +WHERE a = 1 AND b = 2 AND c = 3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 ref i_a i_a 5 const 26 0.05 Using where +Warnings: +Note 1003 select /*+ INDEX(`t1`@`select#1` `i_a`) */ `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`test`.`t1`.`c` AS `c`,`test`.`t1`.`d` AS `d` from `test`.`t1` IGNORE INDEX (`i_a`) where `test`.`t1`.`a` = 1 and `test`.`t1`.`b` = 2 and `test`.`t1`.`c` = 3 +# No conflicts between different hints for same indexes +EXPLAIN EXTENDED +SELECT /*+ MRR(t1 i_a, i_b, i_ab) NO_ICP(t1 i_a, i_b, i_ab) JOIN_INDEX(t1 i_a, i_b) ORDER_INDEX(t1 i_a, i_ab) */ +a FROM t1; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 index NULL i_a 5 NULL 256 100.00 Using index +Warnings: +Note 1003 select /*+ JOIN_INDEX(`t1`@`select#1` `i_a`,`i_b`) ORDER_INDEX(`t1`@`select#1` `i_a`,`i_ab`) NO_ICP(`t1`@`select#1` `i_a`) MRR(`t1`@`select#1` `i_a`) NO_ICP(`t1`@`select#1` `i_b`) MRR(`t1`@`select#1` `i_b`) NO_ICP(`t1`@`select#1` `i_ab`) MRR(`t1`@`select#1` `i_ab`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` +# Check index hints with UPDATE/DELETE commands. +# By default, "i_ab" index is used +EXPLAIN EXTENDED UPDATE t1 SET d = 1 WHERE a = 1 AND b = 2 AND c = 3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range i_a,i_b,i_ab,i_c i_ab 10 NULL 1 100.00 Using where +Warnings: +Note 1003 update `test`.`t1` set `test`.`t1`.`d` = 1 where `test`.`t1`.`a` = 1 and `test`.`t1`.`b` = 2 and `test`.`t1`.`c` = 3 +# Force i_a index to be used. +EXPLAIN EXTENDED UPDATE /*+ INDEX(t1 i_a) */ t1 SET d = 1 WHERE a = 1 AND b = 2 AND c = 3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range i_a i_a 5 NULL 26 100.00 Using where +Warnings: +Note 1003 update /*+ INDEX(`t1`@`select#1` `i_a`) */ `test`.`t1` set `test`.`t1`.`d` = 1 where `test`.`t1`.`a` = 1 and `test`.`t1`.`b` = 2 and `test`.`t1`.`c` = 3 +# By default, "i_ab" index is used +EXPLAIN EXTENDED DELETE FROM t1 WHERE a = 1 AND b = 2 AND c = 3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range i_a,i_b,i_ab,i_c i_ab 10 NULL 1 100.00 Using where +Warnings: +Note 1003 delete from `test`.`t1` using dual where `test`.`t1`.`a` = 1 and `test`.`t1`.`b` = 2 and `test`.`t1`.`c` = 3 +# Forbid i_ab index for DELETE. +EXPLAIN EXTENDED DELETE /*+ NO_INDEX(t1 i_ab) */ FROM t1 WHERE a = 1 AND b = 2 AND c = 3; +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 range i_a,i_b,i_c i_c 5 NULL 1 100.00 Using where +Warnings: +Note 1003 delete /*+ NO_INDEX(`t1`@`select#1` `i_ab`) */ from `test`.`t1` using dual where `test`.`t1`.`a` = 1 and `test`.`t1`.`b` = 2 and `test`.`t1`.`c` = 3 +DROP TABLE t1; diff --git a/mysql-test/main/opt_hints_index.test b/mysql-test/main/opt_hints_index.test new file mode 100644 index 00000000000..abe704b4693 --- /dev/null +++ b/mysql-test/main/opt_hints_index.test @@ -0,0 +1,114 @@ +--enable_prepare_warnings +--disable_view_protocol # Since optimizer hints are not supported inside views + +CREATE TABLE t1 (a INT, b INT, c INT, d INT, + KEY i_a(a), KEY i_b(b), + KEY i_ab(a,b), KEY i_c(c), KEY i_d(d)); +INSERT INTO t1 VALUES +(1,1,1,1),(2,2,2,1),(3,3,3,1),(4,4,4,1), +(5,5,5,1),(6,6,6,1),(7,7,7,1),(8,8,8,1); +INSERT INTO t1 SELECT a,b, c + 10, d FROM t1; +INSERT INTO t1 SELECT a,b, c + 20, d FROM t1; +INSERT INTO t1 SELECT a,b, c + 40, d FROM t1; +INSERT INTO t1 SELECT a,b, c + 80, d FROM t1; +INSERT INTO t1 SELECT a,b, c + 160, d FROM t1; +ANALYZE TABLE t1; + +--echo # Check behavior of duplicated/intersected hints. +--echo # First specified hint is applied and next conflicting/intersected hints +--echo # are ignored with warning. + +--echo # JOIN_INDEX(t1) is ignored as intersected (INDEX/other hint for the same table) +EXPLAIN EXTENDED SELECT /*+ INDEX(t1) JOIN_INDEX(t1) */ a FROM t1; +--echo # INDEX(t1 i_d) is ignored as duplicated (same type of hint for the same table) +EXPLAIN EXTENDED SELECT /*+ INDEX(t1 i_a, i_b, i_c) NO_INDEX(t1 i_d) */ a FROM t1; +--echo # JOIN_INDEX(t1 i_a, i_b) is ignored as intersected (INDEX/other hint for the same table) +EXPLAIN EXTENDED SELECT /*+ INDEX(t1 i_a) JOIN_INDEX(t1 i_a, i_b) JOIN_INDEX(t1 i_b) */ a FROM t1; +--echo # GROUP_INDEX(t1 i_a, i_b) is ignored as intersected (INDEX/other hint for the same table) +EXPLAIN EXTENDED SELECT /*+ INDEX(t1 i_a) GROUP_INDEX(t1 i_a, i_b) GROUP_INDEX(t1 i_b) */ a FROM t1; +--echo # GROUP_INDEX(t1) is ignored as intersected (INDEX/other hint for the same table) +EXPLAIN EXTENDED SELECT /*+ INDEX(t1 i_a) GROUP_INDEX(t1) */ a FROM t1; +--echo # ORDER_INDEX(t1 i_a, i_b) is ignored as intersected (INDEX/other hint for the same key) +EXPLAIN EXTENDED SELECT /*+ INDEX(t1 i_a) ORDER_INDEX(t1 i_a, i_b) ORDER_INDEX(t1 i_b) */ a FROM t1; +--echo # INDEX(t1 i_b,i_a) is ignored as intersected (INDEX/other hint for the same key) +EXPLAIN EXTENDED SELECT /*+ ORDER_INDEX(t1 i_a) INDEX(t1 i_b,i_a)*/ a FROM t1; +--echo # ORDER_INDEX(t1) is ignored as intersected (INDEX/other hint for the same table) +EXPLAIN EXTENDED SELECT /*+ INDEX(t1 i_a) ORDER_INDEX(t1) */ a FROM t1; +--echo # ORDER_INDEX(t1 i_b) is ignored as intersected (same hint/INDEX for the same table) +EXPLAIN EXTENDED SELECT /*+ ORDER_INDEX(t1 i_a, i_b) NO_ORDER_INDEX(t1 i_b) INDEX(t1 i_c)*/ a FROM t1; +--echo # INDEX(t1) is ignored as intersected (INDEX/other hint for the same table) +EXPLAIN EXTENDED SELECT /*+ ORDER_INDEX(t1 i_a, i_b) GROUP_INDEX(t1 i_b) INDEX(t1)*/ a FROM t1; +--echo # INDEX(t1) is ignored as intersected (INDEX/other hint for the same table) +EXPLAIN EXTENDED SELECT /*+ ORDER_INDEX(t1) GROUP_INDEX(t1) INDEX(t1)*/ a FROM t1; +--echo # Check the use of index hints. +--echo # Force the use of i_a, i_b indexes, intersect(i_a,i_b) is used. +EXPLAIN EXTENDED SELECT /*+ INDEX(t1 i_a, i_b) */ a FROM t1 WHERE a = 1 AND b = 2 AND c = 3; +--echo # Force the use of i_a, i_ab indexes, i_ab index is used. +EXPLAIN EXTENDED SELECT /*+ INDEX(t1 i_a, i_ab) */ a FROM t1 WHERE a = 1 AND b = 2 AND c = 3; +--echo # Force the use of i_a, i_b, i_c indexes, i_c index is used. +EXPLAIN EXTENDED SELECT /*+ INDEX(t1 i_a, i_b) JOIN_INDEX(t1 i_c) */ * FROM t1 WHERE a = 1 AND b = 2 AND c = 3; + +--echo # Test range index access +EXPLAIN EXTENDED SELECT a FROM t1 WHERE a > 1 AND a < 3; +--echo # Indexes are forbidden, full scan is employed +EXPLAIN EXTENDED SELECT /*+ NO_INDEX(t1)*/a FROM t1 WHERE a > 1 AND a < 3; +EXPLAIN EXTENDED SELECT /*+ NO_JOIN_INDEX(t1)*/a FROM t1 WHERE a > 1 AND a < 3; +--echo # Indexes are forbidden for grouping and ordering but are usable for data access +EXPLAIN EXTENDED SELECT /*+ NO_GROUP_INDEX(t1) NO_ORDER_INDEX(t1)*/a FROM t1 WHERE a > 1 AND a < 3; +--echo # Index "i_a" is forbidden, "i_ab" is used instead +EXPLAIN EXTENDED SELECT /*+ NO_INDEX(t1 i_a)*/a FROM t1 WHERE a > 1 AND a < 3; + +--echo # Usable indexes are forbidden, full scan is employed +EXPLAIN EXTENDED SELECT /*+ NO_INDEX(t1 i_a, i_ab)*/a FROM t1 WHERE a > 1 AND a < 3; + +EXPLAIN EXTENDED SELECT /*+ INDEX(t1 i_ab)*/a FROM t1 WHERE a > 1 AND a < 3; + +--echo # Full scan is more efficient for this query, so indexes are not used by default: +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a < 8; +--echo # Force using of any index: +EXPLAIN EXTENDED SELECT /*+ index(t1)*/* FROM t1 WHERE a < 8; +--echo # Force using of a particular index: +EXPLAIN EXTENDED SELECT /*+ index(t1 i_a)*/* FROM t1 WHERE a < 8; + +--echo # Ignore i_ab index, i_b index is used. +EXPLAIN EXTENDED SELECT /*+ NO_INDEX(t1 i_ab) */ a, max(b) FROM t1 WHERE b = 2 GROUP BY a; +--echo # Ignore i_ab index, i_b index is used. +EXPLAIN EXTENDED SELECT /*+ NO_JOIN_INDEX(t1 i_ab) */ a, max(b) FROM t1 WHERE b = 2 GROUP BY a; +--echo # Force i_ab index for GROUP BY, i_ab index scan is used. +EXPLAIN EXTENDED SELECT /*+ GROUP_INDEX(t1 i_ab) */ a, max(b) FROM t1 GROUP BY a; +--echo # Force i_ab index for JOIN, i_ab loose index scan is used. +EXPLAIN EXTENDED SELECT /*+ JOIN_INDEX(t1 i_ab) */ a, max(b) FROM t1 GROUP BY a; +--echo # Ignore i_ab for sorting rows. i_a index is used for sorting. +EXPLAIN EXTENDED SELECT /*+ NO_ORDER_INDEX(t1 i_ab) */ a FROM t1 ORDER BY a; +--echo # Ignore i_a for sorting rows. i_ab is used for sorting. +EXPLAIN EXTENDED SELECT /*+ NO_ORDER_INDEX(t1 i_a) */ a FROM t1 ORDER BY a; +--echo # Force i_ab index for sorting rows. +EXPLAIN EXTENDED SELECT /*+ ORDER_INDEX(t1 i_ab) */ a FROM t1 ORDER BY a; +--echo # Force i_a index for sorting rows. +EXPLAIN EXTENDED SELECT /*+ ORDER_INDEX(t1 i_a) */ a FROM t1 ORDER BY a; +--echo # Ignore all indexes. +EXPLAIN EXTENDED SELECT /*+ NO_INDEX(t1) */ * FROM t1 WHERE a = 1 AND b = 2 AND c = 3; +--echo # Check if old-style hints work if no new hints are specified. +EXPLAIN EXTENDED SELECT /*+ NO_MRR(t1) */ * FROM t1 IGNORE INDEX (i_a) +WHERE a = 1 AND b = 2 AND c = 3; + +--echo # Check that old-style hint is silently ignored if a new hint is specified. +EXPLAIN EXTENDED SELECT /*+ INDEX(t1 i_a) */ * FROM t1 IGNORE INDEX(i_a) +WHERE a = 1 AND b = 2 AND c = 3; + +--echo # No conflicts between different hints for same indexes +EXPLAIN EXTENDED +SELECT /*+ MRR(t1 i_a, i_b, i_ab) NO_ICP(t1 i_a, i_b, i_ab) JOIN_INDEX(t1 i_a, i_b) ORDER_INDEX(t1 i_a, i_ab) */ + a FROM t1; + +--echo # Check index hints with UPDATE/DELETE commands. +--echo # By default, "i_ab" index is used +EXPLAIN EXTENDED UPDATE t1 SET d = 1 WHERE a = 1 AND b = 2 AND c = 3; +--echo # Force i_a index to be used. +EXPLAIN EXTENDED UPDATE /*+ INDEX(t1 i_a) */ t1 SET d = 1 WHERE a = 1 AND b = 2 AND c = 3; +--echo # By default, "i_ab" index is used +EXPLAIN EXTENDED DELETE FROM t1 WHERE a = 1 AND b = 2 AND c = 3; +--echo # Forbid i_ab index for DELETE. +EXPLAIN EXTENDED DELETE /*+ NO_INDEX(t1 i_ab) */ FROM t1 WHERE a = 1 AND b = 2 AND c = 3; + +DROP TABLE t1; diff --git a/sql/mem_root_array.h b/sql/mem_root_array.h index 4284b704202..74d72bf4b98 100644 --- a/sql/mem_root_array.h +++ b/sql/mem_root_array.h @@ -17,6 +17,7 @@ #ifndef MEM_ROOT_ARRAY_INCLUDED #define MEM_ROOT_ARRAY_INCLUDED +#include #include /** @@ -44,7 +45,9 @@ compilers we use. */ -template +template::value> class Mem_root_array { public: @@ -263,5 +266,4 @@ private: } }; - #endif // MEM_ROOT_ARRAY_INCLUDED diff --git a/sql/opt_hints.cc b/sql/opt_hints.cc index c57e94ce50a..d36bfdfb89e 100644 --- a/sql/opt_hints.cc +++ b/sql/opt_hints.cc @@ -45,6 +45,10 @@ struct st_opt_hint_info opt_hint_info[]= {{STRING_WITH_LEN("JOIN_SUFFIX")}, false, true, true}, {{STRING_WITH_LEN("JOIN_ORDER")}, false, true, true}, {{STRING_WITH_LEN("JOIN_FIXED_ORDER")}, false, true, false}, + {{STRING_WITH_LEN("INDEX")}, false, true, false}, + {{STRING_WITH_LEN("JOIN_INDEX")}, false, true, false}, + {{STRING_WITH_LEN("GROUP_INDEX")}, false, true, false}, + {{STRING_WITH_LEN("ORDER_INDEX")}, false, true, false}, {null_clex_str, 0, 0, 0} }; @@ -76,6 +80,22 @@ void push_warning_safe(THD *thd, Sql_condition::enum_warning_level level, DBUG_VOID_RETURN; } +/* + Function prepares and prints a warning message related to hints parsing or + resolving. + + @param thd Pointer to THD object for session. + err_code Enumerated code of the warning + hint_type Enumerated hint type + hint_state true: enabling hint (HINT(...), + false: disabling (NO_HINT(...)) + qb_name_arg optional query block name + table_name_arg optional table name + key_name_arg optional key (index) name + hint optional pointer to the parsed hint object + add_info optional string, if given then will be appended to + the end of the message embraced in brackets +*/ void print_warn(THD *thd, uint err_code, opt_hints_enum hint_type, bool hint_state, @@ -151,8 +171,8 @@ void print_warn(THD *thd, uint err_code, opt_hints_enum hint_type, hint->append_args(thd, &str); } - str.append(')'); + push_warning_safe(thd, Sql_condition::WARN_LEVEL_WARN, err_code, ER_THD(thd, err_code), str.c_ptr_safe()); } @@ -295,19 +315,13 @@ Opt_hints* Opt_hints::find_by_name(const LEX_CSTRING &name_arg) const void Opt_hints::print(THD *thd, String *str) { - /* Do not print the hint if we couldn't attach it to its object */ - if (!is_fixed()) - return; - // Print the hints stored in the bitmap for (uint i= 0; i < MAX_HINT_ENUM; i++) { - if (opt_hint_info[i].irregular_hint) - continue; - opt_hints_enum hint_type= static_cast(i); - if (is_specified(hint_type)) + opt_hints_enum hint= static_cast(i); + if (is_specified(hint) && !ignore_print(hint) && is_fixed(hint)) { - append_hint_type(str, hint_type); + append_hint_type(str, hint); str->append(STRING_WITH_LEN("(")); uint32 len_before_name= str->length(); append_name(thd, str); @@ -315,7 +329,7 @@ void Opt_hints::print(THD *thd, String *str) if (len_after_name > len_before_name) str->append(' '); if (opt_hint_info[i].has_arguments) - append_hint_arguments(thd, hint_type, str); + append_hint_arguments(thd, hint, str); if (str->length() == len_after_name + 1) { // No additional arguments were printed, trim the space added before @@ -332,6 +346,12 @@ void Opt_hints::print(THD *thd, String *str) } +bool Opt_hints::ignore_print(opt_hints_enum type_arg) const +{ + return opt_hint_info[type_arg].irregular_hint; +} + + /* @brief Append hint "type", for example, "NO_RANGE_OPTIMIZATION" or "BKA" @@ -373,7 +393,7 @@ void Opt_hints::print_unfixed_warnings(THD *thd) void Opt_hints::check_unfixed(THD *thd) { - if (!is_fixed()) + if (!are_all_fixed()) print_unfixed_warnings(thd); if (!are_children_fully_fixed()) @@ -408,7 +428,7 @@ Opt_hints_table *Opt_hints_qb::fix_hints_for_table(TABLE *table, if (!tab) // Tables not found return NULL; - if (!tab->fix_hint(table)) + if (!tab->fix_key_hints(table)) incr_fully_fixed_children(); return tab; @@ -479,16 +499,21 @@ void Opt_hints_qb::append_hint_arguments(THD *thd, opt_hints_enum hint, For each index IDX, put its hints into keyinfo_array[IDX] */ -bool Opt_hints_table::fix_hint(TABLE *table) +bool Opt_hints_table::fix_key_hints(TABLE *table) { /* Ok, there's a table we attach to. Mark this hint as fixed and proceed to fixing the child objects. */ set_fixed(); - - if (child_array_ptr()->size() == 0) // No key level hints - return false; // Ok, fully fixed + if (is_specified(INDEX_HINT_ENUM)) + global_index_map.set_fixed(); + if (is_specified(JOIN_INDEX_HINT_ENUM)) + join_index_map.set_fixed(); + if (is_specified(GROUP_INDEX_HINT_ENUM)) + group_index_map.set_fixed(); + if (is_specified(ORDER_INDEX_HINT_ENUM)) + order_index_map.set_fixed(); /* Make sure that adjustment is called only once. */ DBUG_ASSERT(keyinfo_array.size() == 0); @@ -505,6 +530,7 @@ bool Opt_hints_table::fix_hint(TABLE *table) (*hint)->set_fixed(); keyinfo_array[j]= static_cast(*hint); incr_fully_fixed_children(); + set_compound_key_hint_map(*hint, j); break; } } @@ -517,6 +543,216 @@ bool Opt_hints_table::fix_hint(TABLE *table) } +static bool table_or_key_hint_type_specified(Opt_hints_table *table_hint, + Opt_hints_key *key_hint, + opt_hints_enum type) +{ + DBUG_ASSERT(table_hint || key_hint ); + return key_hint ? key_hint->is_specified(type) : + table_hint->is_specified(type); +} + + +bool Opt_hints_table::is_fixed(opt_hints_enum type_arg) +{ + if (is_compound_hint(type_arg)) + return Opt_hints::is_fixed(type_arg) && + get_key_hint_bitmap(type_arg)->is_fixed(); + return Opt_hints::is_fixed(type_arg); +} + + +void Opt_hints_table::set_compound_key_hint_map(Opt_hints *hint, uint keynr) +{ + if (hint->is_specified(INDEX_HINT_ENUM)) + global_index_map.set_key_map(keynr); + if (hint->is_specified(JOIN_INDEX_HINT_ENUM)) + join_index_map.set_key_map(keynr); + if (hint->is_specified(GROUP_INDEX_HINT_ENUM)) + group_index_map.set_key_map(keynr); + if (hint->is_specified(ORDER_INDEX_HINT_ENUM)) + order_index_map.set_key_map(keynr); +} + + +Opt_hints_key_bitmap *Opt_hints_table::get_key_hint_bitmap(opt_hints_enum type) +{ + switch (type) { + case INDEX_HINT_ENUM: + return &global_index_map; + case JOIN_INDEX_HINT_ENUM: + return &join_index_map; + case GROUP_INDEX_HINT_ENUM: + return &group_index_map; + case ORDER_INDEX_HINT_ENUM: + return &order_index_map; + default: + DBUG_ASSERT(0); + return nullptr; + } +} + + +/** + Function updates key_to_use key map depending on index hint state. + + @param keys_to_use key to use + @param available_keys_to_use available keys to use + @param type_arg hint type +*/ + +void Opt_hints_table::update_index_hint_map(Key_map *keys_to_use, + const Key_map *available_keys_to_use, + opt_hints_enum type_arg) +{ + // Check if hint is resolved. + if (is_fixed(type_arg)) + { + Key_map *keys_specified_in_hint= + get_key_hint_bitmap(type_arg)->get_key_map(); + if (get_switch(type_arg)) + { + if (keys_specified_in_hint->is_clear_all()) + { + /* + If the hint is on and no keys are specified in the hint, + then set "keys_to_use" to all the available keys. + */ + keys_to_use->merge(*available_keys_to_use); + } + else + { + /* + If the hint is on and there are keys specified in the hint, then add + the specified keys to "keys_to_use" taking care of the disabled keys + (available_keys_to_use). + */ + keys_to_use->merge(*keys_specified_in_hint); + keys_to_use->intersect(*available_keys_to_use); + } + } + else + { + if (keys_specified_in_hint->is_clear_all()) + { + /* + If the hint is off and there are no keys specified in the hint, then + we clear "keys_to_use". + */ + keys_to_use->clear_all(); + } + else + { + /* + If hint is off and some keys are specified in the hint, then remove + the specified keys from "keys_to_use. + */ + keys_to_use->subtract(*keys_specified_in_hint); + } + } + } +} + + +/** + @brief + Set TABLE::keys_in_use_for_XXXX and other members according to the + specified index hints for this table + + @detail + For each index hint that is not ignored, include the index in + - tbl->keys_in_use_for_query if the hint is INDEX or JOIN_INDEX + - tbl->keys_in_use_for_group_by if the hint is INDEX or + GROUP_INDEX + - tbl->keys_in_use_for_order_by if the hint is INDEX or + ORDER_INDEX + conversely, subtract the index from the corresponding + tbl->keys_in_use_for_... map if the hint is prefixed with NO_. + + See also: TABLE_LIST::process_index_hints(), which handles similar logic + for old-style index hints. + + @param thd pointer to THD object + @param tbl pointer to TABLE object + + @return + false if no index hint is specified + true otherwise. +*/ + +bool Opt_hints_table::update_index_hint_maps(THD *thd, TABLE *tbl) +{ + if (!is_fixed(INDEX_HINT_ENUM) && !is_fixed(JOIN_INDEX_HINT_ENUM) && + !is_fixed(GROUP_INDEX_HINT_ENUM) && !is_fixed(ORDER_INDEX_HINT_ENUM)) + return false; // No index hint is specified + + Key_map usable_index_map(tbl->s->usable_indexes(thd)); + tbl->keys_in_use_for_query= tbl->keys_in_use_for_group_by= + tbl->keys_in_use_for_order_by= usable_index_map; + + bool is_force= is_force_index_hint(INDEX_HINT_ENUM); + tbl->force_index_join= + (is_force || is_force_index_hint(JOIN_INDEX_HINT_ENUM)); + tbl->force_index_group= + (is_force || is_force_index_hint(GROUP_INDEX_HINT_ENUM)); + tbl->force_index_order= + (is_force || is_force_index_hint(ORDER_INDEX_HINT_ENUM)); + + if (tbl->force_index_join) + tbl->keys_in_use_for_query.clear_all(); + if (tbl->force_index_group) + tbl->keys_in_use_for_group_by.clear_all(); + if (tbl->force_index_order) + tbl->keys_in_use_for_order_by.clear_all(); + + // See comment to the identical code at TABLE_LIST::process_index_hints + tbl->force_index= (tbl->force_index_order | tbl->force_index_group | + tbl->force_index_join); + + update_index_hint_map(&tbl->keys_in_use_for_query, &usable_index_map, + INDEX_HINT_ENUM); + update_index_hint_map(&tbl->keys_in_use_for_group_by, &usable_index_map, + INDEX_HINT_ENUM); + update_index_hint_map(&tbl->keys_in_use_for_order_by, &usable_index_map, + INDEX_HINT_ENUM); + update_index_hint_map(&tbl->keys_in_use_for_query, &usable_index_map, + JOIN_INDEX_HINT_ENUM); + update_index_hint_map(&tbl->keys_in_use_for_group_by, &usable_index_map, + GROUP_INDEX_HINT_ENUM); + update_index_hint_map(&tbl->keys_in_use_for_order_by, &usable_index_map, + ORDER_INDEX_HINT_ENUM); + /* Make sure "covering_keys" does not include indexes disabled with a hint */ + Key_map covering_keys(tbl->keys_in_use_for_query); + covering_keys.merge(tbl->keys_in_use_for_group_by); + covering_keys.merge(tbl->keys_in_use_for_order_by); + tbl->covering_keys.intersect(covering_keys); + return true; +} + + +void Opt_hints_table::append_hint_arguments(THD *thd, opt_hints_enum hint, + String *str) +{ + switch (hint) + { + case INDEX_HINT_ENUM: + global_index_map.parsed_hint->append_args(thd, str); + break; + case JOIN_INDEX_HINT_ENUM: + join_index_map.parsed_hint->append_args(thd, str); + break; + case GROUP_INDEX_HINT_ENUM: + group_index_map.parsed_hint->append_args(thd, str); + break; + case ORDER_INDEX_HINT_ENUM: + order_index_map.parsed_hint->append_args(thd, str); + break; + default: + DBUG_ASSERT(0); + } +} + + /** Function returns hint value depending on the specfied hint level. If hint is specified @@ -992,6 +1228,43 @@ bool Opt_hints_global::fix_hint(THD *thd) } +/** + Function checks if an INDEX (resp. JOIN_INDEX, GROUP_INDEX or + ORDER_INDEX) hint conflicts with any JOIN_INDEX, GROUP_INDEX or + ORDER_INDEX (resp. INDEX) hints, by checking if any of the latter is + already specified at table level or index level. + + @param table_hint pointer to table hint + @param key_hint pointer to key hint + @param hint_type enumerated hint type + + @return false if no conflict, true otherwise. +*/ + +bool is_index_hint_conflicting(Opt_hints_table *table_hint, + Opt_hints_key *key_hint, + opt_hints_enum hint_type) +{ + if (hint_type != INDEX_HINT_ENUM) + return table_or_key_hint_type_specified(table_hint, key_hint, + INDEX_HINT_ENUM); + return (table_or_key_hint_type_specified(table_hint, key_hint, + JOIN_INDEX_HINT_ENUM) || + table_or_key_hint_type_specified(table_hint, key_hint, + ORDER_INDEX_HINT_ENUM) || + table_or_key_hint_type_specified(table_hint, key_hint, + GROUP_INDEX_HINT_ENUM)); +} + + +bool is_compound_hint(opt_hints_enum type_arg) +{ + return ( + type_arg == INDEX_HINT_ENUM || type_arg == JOIN_INDEX_HINT_ENUM || + type_arg == GROUP_INDEX_HINT_ENUM || type_arg == ORDER_INDEX_HINT_ENUM); +} + + #ifndef DBUG_OFF static char dbug_print_hint_buf[64]; diff --git a/sql/opt_hints.h b/sql/opt_hints.h index 817a5a27ae2..73e540473ed 100644 --- a/sql/opt_hints.h +++ b/sql/opt_hints.h @@ -59,6 +59,7 @@ Opt_hints_qb Opt_hints_table Opt_hints_key + Opt_hints_key_bitmap Some hints can be specified at a specific level (e.g. per-index) or at a more general level (e.g. per-table). When checking the hint, we need @@ -99,6 +100,8 @@ struct LEX; struct TABLE; +using Key_map = Bitmap; + struct st_opt_hint_info { LEX_CSTRING hint_type; // Hint "type", like "BKA" or "MRR". @@ -178,6 +181,7 @@ protected: for key level. */ Lex_ident_sys name; + private: /* Parent object. There is no parent for global level, @@ -191,7 +195,7 @@ private: Opt_hints_map hints_map; /* Array of child objects. i.e. array of the lower level objects */ - Mem_root_array child_array; + Mem_root_array child_array; /* true if hint is connected to the real object */ bool fixed; @@ -203,7 +207,6 @@ private: uint n_fully_fixed_children; public: - Opt_hints(const Lex_ident_sys &name_arg, Opt_hints *parent_arg, MEM_ROOT *mem_root_arg) @@ -260,10 +263,35 @@ public: } void set_name(const Lex_ident_sys &name_arg) { name= name_arg; } Opt_hints *get_parent() const { return parent; } - void set_fixed() { fixed= true; } - bool is_fixed() const { return fixed; } + + /* + The caller is responsible for making sure all hints in this + hint collection are fixed. + */ + void set_fixed() { fixed= true; } + + /* + @brief + Check if all elements in this collection are fixed. + + Whether this means children hint collections are fixed depends on + the collection. + */ + bool are_all_fixed() const + { + return fixed; + } + + /* + Check if the individual hint type_arg in this collection is fixed. + */ + virtual bool is_fixed(opt_hints_enum type_arg) + { + return fixed; + } + void incr_fully_fixed_children() { n_fully_fixed_children++; } - Mem_root_array *child_array_ptr() { return &child_array; } + Mem_root_array *child_array_ptr() { return &child_array; } bool are_children_fully_fixed() const { @@ -346,6 +374,18 @@ protected: @param str pointer to String object */ virtual void print_irregular_hints(THD *thd, String *str) {} + + /** + If ignore_print() returns true, hint is not printed + by Opt_hints::print() function. At the moment used for + INDEX, JOIN_INDEX, GROUP_INDEX and ORDER_INDEX hints. + + @param type_arg hint type + + @return true if the hint should not be printed + in Opt_hints::print() function, false otherwise. + */ + virtual bool ignore_print(opt_hints_enum type_arg) const; }; @@ -411,7 +451,7 @@ class Opt_hints_qb : public Opt_hints char buff[32]; // Buffer to hold sys name // Array of join order hints - Mem_root_array join_order_hints; + Mem_root_array join_order_hints; // Bitmap marking ignored hints ulonglong join_order_hints_ignored; // Max capacity to avoid overflowing of join_order_hints_ignored bitmap @@ -563,14 +603,67 @@ private: }; +/** + Auxiliary class for "compound" index hints: INDEX, JOIN_INDEX, + GROUP_INDEX and ORDER_INDEX. + It represents a bitmap of keys specified in the hint along with methods for + handling it. + + Data structures for compound index hints are stored in two places: + 1) in a Opt_hints_key object(s) for each of the affected indexes; + 2) in a Opt_hints_key_bitmap object which stores a bitmap of all indexes + specified in the hint. + These `Opt_hints_key_bitmap` objects themselves are stored + in the `Opt_hints_table` object. + + `Opt_hints_table->get_switch(hint_type)` tells whether the hint was + "positive" or "negative". +*/ + +class Opt_hints_key_bitmap +{ + Key_map key_map; // Keys specified in the hint. + bool fixed= false; // true if all keys of the hint are resolved + +public: + Opt_hints_key_bitmap() + { + key_map.clear_all(); + } + + const Parser::Index_level_hint *parsed_hint= nullptr; + + void set_fixed() { fixed= true; } + bool is_fixed() const { return fixed; } + + void set_key_map(uint i) { key_map.set_bit(i); } + bool is_set_key_map(uint i) { return key_map.is_set(i); } + bool is_key_map_clear_all() { return key_map.is_clear_all(); } + Key_map *get_key_map() { return &key_map; } +}; + + /** Table level hints. + Collection of hints attached to a certain table (and its indexes) */ class Opt_hints_table : public Opt_hints { public: - Mem_root_array keyinfo_array; + /* + keyinfo_array[IDX] has all hint prescriptions for index number IDX. + */ + Mem_root_array keyinfo_array; + + /* + Bitmaps for "compound" index hints. + Check get_switch(opt_hint_enum) to see if it's INDEX or NO_INDEX, etc. + */ + Opt_hints_key_bitmap global_index_map; // INDEX(), NO_INDEX() + Opt_hints_key_bitmap join_index_map; // JOIN_INDEX(), NO_JOIN_INDEX() + Opt_hints_key_bitmap group_index_map; // GROUP_INDEX(), NO_GROUP_INDEX() + Opt_hints_key_bitmap order_index_map; // ORDER_INDEX(), NO_ORDER_INDEX() Opt_hints_table(const Lex_ident_sys &table_name_arg, Opt_hints_qb *qb_hints_arg, @@ -602,17 +695,58 @@ public: @param table Pointer to TABLE object */ - bool fix_hint(TABLE *table); + bool fix_key_hints(TABLE *table); + + /** + Returns `true` if a particular hint has been attached to an object + (table or key) and `false` otherwise + + @param type_arg hint type + */ + bool is_fixed(opt_hints_enum type_arg) override; virtual uint get_unfixed_warning_code() const override { return ER_UNRESOLVED_TABLE_HINT_NAME; } + + /** + Return bitmap object corresponding to the hint type passed + + @param type hint_type + */ + Opt_hints_key_bitmap *get_key_hint_bitmap(opt_hints_enum type); + + void append_hint_arguments(THD *thd, opt_hints_enum hint, + String *str) override; + + + bool update_index_hint_maps(THD *thd, TABLE *tbl); + +private: + bool is_force_index_hint(opt_hints_enum type_arg) + { + return (get_key_hint_bitmap(type_arg)->is_fixed() && + get_switch(type_arg)); + } + + void update_index_hint_map(Key_map *keys_to_use, + const Key_map *available_keys_to_use, + opt_hints_enum type_arg); + void set_compound_key_hint_map(Opt_hints *hint, uint keynr); + }; +bool is_index_hint_conflicting(Opt_hints_table *table_hint, + Opt_hints_key *key_hint, + opt_hints_enum hint_type); + +bool is_compound_hint(opt_hints_enum type_arg); + /** Key level hints. + A set of hints attached to a particular index. */ class Opt_hints_key : public Opt_hints @@ -642,6 +776,19 @@ public: { return ER_UNRESOLVED_INDEX_HINT_NAME; } + + /** + Compound index hints are present at both index-level (`Opt_hints_key`) and + table-level (`Opt_hints_table`) but must be printed only once (at the + table level). That is why they must be skipped when printing + `Opt_hints_key` hints + */ + bool ignore_print(opt_hints_enum type_arg) const override + { + if (is_compound_hint(type_arg)) + return true; + return Opt_hints::ignore_print(type_arg); + } }; diff --git a/sql/opt_hints_parser.cc b/sql/opt_hints_parser.cc index 3e0f38639f5..7e931183171 100644 --- a/sql/opt_hints_parser.cc +++ b/sql/opt_hints_parser.cc @@ -71,6 +71,10 @@ Optimizer_hint_tokenizer::find_keyword(const LEX_CSTRING &str) if ("MRR"_Lex_ident_column.streq(str)) return TokenID::keyword_MRR; break; + case 5: + if ("INDEX"_Lex_ident_column.streq(str)) return TokenID::keyword_INDEX; + break; + case 6: if ("NO_BKA"_Lex_ident_column.streq(str)) return TokenID::keyword_NO_BKA; if ("NO_BNL"_Lex_ident_column.streq(str)) return TokenID::keyword_NO_BNL; @@ -86,8 +90,10 @@ Optimizer_hint_tokenizer::find_keyword(const LEX_CSTRING &str) case 8: if ("SEMIJOIN"_Lex_ident_column.streq(str)) return TokenID::keyword_SEMIJOIN; - else if ("SUBQUERY"_Lex_ident_column.streq(str)) + if ("SUBQUERY"_Lex_ident_column.streq(str)) return TokenID::keyword_SUBQUERY; + if ("NO_INDEX"_Lex_ident_column.streq(str)) + return TokenID::keyword_NO_INDEX; break; case 9: @@ -98,21 +104,39 @@ Optimizer_hint_tokenizer::find_keyword(const LEX_CSTRING &str) case 10: if ("FIRSTMATCH"_Lex_ident_column.streq(str)) return TokenID::keyword_FIRSTMATCH; - else if ("INTOEXISTS"_Lex_ident_column.streq(str)) + if ("INTOEXISTS"_Lex_ident_column.streq(str)) return TokenID::keyword_INTOEXISTS; - else if ("JOIN_ORDER"_Lex_ident_column.streq(str)) + if ("JOIN_ORDER"_Lex_ident_column.streq(str)) return TokenID::keyword_JOIN_ORDER; + if ("JOIN_INDEX"_Lex_ident_column.streq(str)) + return TokenID::keyword_JOIN_INDEX; break; case 11: if ("NO_SEMIJOIN"_Lex_ident_column.streq(str)) return TokenID::keyword_NO_SEMIJOIN; - else if ("DUPSWEEDOUT"_Lex_ident_column.streq(str)) + if ("DUPSWEEDOUT"_Lex_ident_column.streq(str)) return TokenID::keyword_DUPSWEEDOUT; - else if ("JOIN_PREFIX"_Lex_ident_column.streq(str)) + if ("JOIN_PREFIX"_Lex_ident_column.streq(str)) return TokenID::keyword_JOIN_PREFIX; - else if ("JOIN_SUFFIX"_Lex_ident_column.streq(str)) + if ("JOIN_SUFFIX"_Lex_ident_column.streq(str)) return TokenID::keyword_JOIN_SUFFIX; + if ("ORDER_INDEX"_Lex_ident_column.streq(str)) + return TokenID::keyword_ORDER_INDEX; + if ("GROUP_INDEX"_Lex_ident_column.streq(str)) + return TokenID::keyword_GROUP_INDEX; + break; + + case 13: + if ("NO_JOIN_INDEX"_Lex_ident_column.streq(str)) + return TokenID::keyword_NO_JOIN_INDEX; + break; + + case 14: + if ("NO_ORDER_INDEX"_Lex_ident_column.streq(str)) + return TokenID::keyword_NO_ORDER_INDEX; + if ("NO_GROUP_INDEX"_Lex_ident_column.streq(str)) + return TokenID::keyword_NO_GROUP_INDEX; break; case 15: @@ -411,10 +435,40 @@ bool Parser::Table_level_hint::resolve(Parse_context *pc) const Resolve a parsed index level hint, i.e. set up proper Opt_hint_* structures which will be used later during query preparation and optimization. -Return value: -- false: no critical errors, warnings on duplicated hints, - unresolved query block names, etc. are allowed -- true: critical errors detected, break further hints processing + Return value: + - false: no critical errors, warnings on duplicated hints, + unresolved query block names, etc. are allowed + - true: critical errors detected, break further hints processing + + Taxonomy of index hints + - 2 levels of hints: + - table level hints: only table name specified but no index names + - index level hints: both table name and index names specified + - 2 kinds of hints: + - global: [NO_]INDEX + - non-global: [NO_]JOIN_INDEX, [NO_]GROUP_INDEX, [NO_]ORDER_INDEX + - 4 types of hints: + - [NO_]JOIN_INDEX + - [NO_]GROUP_INDEX + - [NO_]ORDER_INDEX + - [NO_]INDEX + + Conflict checking + - A conflict happens if and only if + - for a table level hint + - a hint of the same type or opposite kind has already been specified + for the same table + - for a index level hint + - the same type of hint has already been specified for the same + table or for the same index, OR + - the opposite kind of hint has already been specified for the + same index + - For a multi index hint like JOIN_INDEX(t1 i1, i2, i3), it conflicts + with a previous hint if any of the JOIN_INDEX(t1 i1), JOIN_INDEX(t1 + i2), JOIN_INDEX(t1 i3) conflicts with a previous hint + + When a hint type is specified for an index, it is also marked as + specified with the same switch state for the table */ bool Parser::Index_level_hint::resolve(Parse_context *pc) const { @@ -440,6 +494,38 @@ bool Parser::Index_level_hint::resolve(Parse_context *pc) const hint_type= NO_RANGE_HINT_ENUM; hint_state= true; break; + case TokenID::keyword_INDEX: + hint_type= INDEX_HINT_ENUM; + hint_state= true; + break; + case TokenID::keyword_NO_INDEX: + hint_type= INDEX_HINT_ENUM; + hint_state= false; + break; + case TokenID::keyword_JOIN_INDEX: + hint_type= JOIN_INDEX_HINT_ENUM; + hint_state= true; + break; + case TokenID::keyword_NO_JOIN_INDEX: + hint_type= JOIN_INDEX_HINT_ENUM; + hint_state= false; + break; + case TokenID::keyword_ORDER_INDEX: + hint_type= ORDER_INDEX_HINT_ENUM; + hint_state= true; + break; + case TokenID::keyword_NO_ORDER_INDEX: + hint_type= ORDER_INDEX_HINT_ENUM; + hint_state= false; + break; + case TokenID::keyword_GROUP_INDEX: + hint_type= GROUP_INDEX_HINT_ENUM; + hint_state= true; + break; + case TokenID::keyword_NO_GROUP_INDEX: + hint_type= GROUP_INDEX_HINT_ENUM; + hint_state= false; + break; default: DBUG_ASSERT(0); return true; @@ -458,37 +544,127 @@ bool Parser::Index_level_hint::resolve(Parse_context *pc) const if (!tab) return false; - if (is_empty()) // Table level hint + const Lex_ident_sys key_conflict( + STRING_WITH_LEN("another hint was already specified for this index")); + + if (is_empty()) // Empty list of index names, i.e. it is a table level hint { - if (tab->set_switch(hint_state, hint_type, false)) + uint warn_code= 0; + if (is_compound_hint(hint_type) && + is_index_hint_conflicting(tab, nullptr, hint_type)) { - print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state, - &qb_name_sys, &table_name_sys, nullptr, nullptr); + warn_code= ER_WARN_CONFLICTING_COMPOUND_INDEX_HINT_FOR_TABLE; + } + else if (tab->set_switch(hint_state, hint_type, false)) + { + warn_code= ER_WARN_CONFLICTING_INDEX_HINT_FOR_TABLE; + } + + if (warn_code != 0) + { + print_warn(pc->thd, warn_code, hint_type, hint_state, &qb_name_sys, + &table_name_sys, nullptr, this); + } + else if (is_compound_hint(hint_type)) + { + tab->get_key_hint_bitmap(hint_type)->parsed_hint= this; } return false; } + // Key names for a compound hint are first collected into the array: + Mem_root_array> + key_hints(pc->thd->mem_root); + bool is_conflicting= false; for (const Hint_param_index &index_name : *this) { const Lex_ident_sys index_name_sys= index_name.to_ident_sys(pc->thd); - Opt_hints_key *idx= (Opt_hints_key *)tab->find_by_name(index_name_sys); - if (!idx) + bool new_opt_key_hint_created= false; + Opt_hints_key *key= (Opt_hints_key *)tab->find_by_name(index_name_sys); + if (!key) { - idx= new (pc->thd->mem_root) + key= new (pc->thd->mem_root) Opt_hints_key(index_name_sys, tab, pc->thd->mem_root); - tab->register_child(idx); + new_opt_key_hint_created= true; } - if (idx->set_switch(hint_state, hint_type, true)) + if (!is_compound_hint(hint_type)) { - print_warn(pc->thd, ER_WARN_CONFLICTING_HINT, hint_type, hint_state, - &qb_name_sys, &table_name_sys, &index_name_sys, nullptr); + if (key->set_switch(hint_state, hint_type, true)) + { + print_warn(pc->thd, ER_WARN_CONFLICTING_INDEX_HINT_FOR_KEY, + hint_type, hint_state, &qb_name_sys, &table_name_sys, + &index_name_sys, nullptr); + continue; + } + if (new_opt_key_hint_created) + tab->register_child(key); + } + else + { + bool is_specified= tab->is_specified(hint_type) || + key->is_specified(hint_type); + if (is_specified || is_index_hint_conflicting(tab, key, hint_type)) + { + is_conflicting= true; + uint warn_code; + if (is_specified) + { + warn_code= tab->is_specified(hint_type) ? + ER_WARN_CONFLICTING_INDEX_HINT_FOR_TABLE : + ER_WARN_CONFLICTING_INDEX_HINT_FOR_KEY; + } + else + { + warn_code= ER_WARN_CONFLICTING_COMPOUND_INDEX_HINT_FOR_KEY; + } + print_warn(pc->thd, warn_code, hint_type, hint_state, + &qb_name_sys, &table_name_sys, nullptr, this); + break; + } + key_hints.push_back({ key, new_opt_key_hint_created }); } } + + if (is_compound_hint(hint_type) && !is_conflicting) + { + /* + Process key names collected for a compound hint. They have already been + checked for conflicts/duplication above, so there is no need to examine + the `set_switch()` return value + */ + for (size_t i= 0; i < key_hints.size(); i++) + { + std::pair key= key_hints.at(i); + key.first->set_switch(hint_state, hint_type, true); + if (key.second) + tab->register_child(key.first); + } + + tab->get_key_hint_bitmap(hint_type)->parsed_hint= this; + tab->set_switch(hint_state, hint_type, false); + } return false; } +void Parser::Index_level_hint::append_args(THD *thd, String *str) const +{ + if (is_empty()) // Empty list of index names, no additional info + return; + + bool first_index_name= true; + for (const Hint_param_index &index_name : *this) + { + if (!first_index_name) + str->append(STRING_WITH_LEN(",")); + append_identifier(thd, str, &index_name); + first_index_name= false; + } +} + + /* Resolve a parsed query block name hint, i.e. set up proper Opt_hint_* structures which will be used later during query preparation and optimization. diff --git a/sql/opt_hints_parser.h b/sql/opt_hints_parser.h index 0a8e13f53cb..8a81305851d 100644 --- a/sql/opt_hints_parser.h +++ b/sql/opt_hints_parser.h @@ -48,9 +48,14 @@ enum opt_hints_enum JOIN_SUFFIX_HINT_ENUM, JOIN_ORDER_HINT_ENUM, JOIN_FIXED_ORDER_HINT_ENUM, + INDEX_HINT_ENUM, + JOIN_INDEX_HINT_ENUM, + GROUP_INDEX_HINT_ENUM, + ORDER_INDEX_HINT_ENUM, MAX_HINT_ENUM // This one must be the last in the list }; + /** Environment data for the name resolution phase */ @@ -112,7 +117,15 @@ public: keyword_JOIN_PREFIX, keyword_JOIN_SUFFIX, keyword_JOIN_ORDER, - keyword_JOIN_FIXED_ORDER + keyword_JOIN_FIXED_ORDER, + keyword_INDEX, + keyword_NO_INDEX, + keyword_JOIN_INDEX, + keyword_NO_JOIN_INDEX, + keyword_GROUP_INDEX, + keyword_NO_GROUP_INDEX, + keyword_ORDER_INDEX, + keyword_NO_ORDER_INDEX }; class Token: public Lex_cstring @@ -374,7 +387,11 @@ private: }; - // index_level_hint_type ::= MRR | NO_RANGE_OPTIMIZATION | NO_ICP | NO_MRR + /* + index_level_hint_type ::= MRR | NO_RANGE_OPTIMIZATION | NO_ICP | NO_MRR | + INDEX | NO_INDEX | JOIN_INDEX | NO_JOIN_INDEX | ORDER_INDEX | + NO_ORDER_INDEX | GROUP_INDEX | NO_GROUP_INDEX + */ class Index_level_hint_type_cond { public: @@ -383,7 +400,15 @@ private: return id == TokenID::keyword_MRR || id == TokenID::keyword_NO_RANGE_OPTIMIZATION || id == TokenID::keyword_NO_ICP || - id == TokenID::keyword_NO_MRR; + id == TokenID::keyword_NO_MRR || + id == TokenID::keyword_INDEX || + id == TokenID::keyword_NO_INDEX || + id == TokenID::keyword_JOIN_INDEX || + id == TokenID::keyword_NO_JOIN_INDEX || + id == TokenID::keyword_ORDER_INDEX || + id == TokenID::keyword_NO_ORDER_INDEX || + id == TokenID::keyword_GROUP_INDEX || + id == TokenID::keyword_NO_GROUP_INDEX; } }; class Index_level_hint_type: public TokenChoice + RParen>, + public Printable_parser_rule { public: using AND4::AND4; - + bool resolve(Parse_context *pc) const; + void append_args(THD *thd, String *str) const override; }; @@ -650,6 +677,7 @@ public: ulonglong get_milliseconds() const; }; +private: // semijoin_hint_type ::= SEMIJOIN | NO_SEMIJOIN class Semijoin_hint_type_cond { diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index ee13a1c501a..da582e6b21f 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -12328,3 +12328,11 @@ ER_WARN_MALFORMED_HINT eng "Hint %s is ignored as malformed" ER_WARN_HINTS_ON_INSERT_PART_OF_INSERT_SELECT eng "Optimizer hints at the INSERT part of a INSERT..SELECT statement are not supported" +ER_WARN_CONFLICTING_INDEX_HINT_FOR_TABLE + eng "Hint %s is ignored as conflicting/duplicated (an index hint of the same type has already been specified for this table)" +ER_WARN_CONFLICTING_INDEX_HINT_FOR_KEY + eng "Hint %s is ignored as conflicting/duplicated (an index hint of the same type has already been specified for this key)" +ER_WARN_CONFLICTING_COMPOUND_INDEX_HINT_FOR_TABLE + eng "Hint %s is ignored as conflicting/duplicated (an index hint of the same type or opposite kind has already been specified for this table)" +ER_WARN_CONFLICTING_COMPOUND_INDEX_HINT_FOR_KEY + eng "Hint %s is ignored as conflicting/duplicated (an index hint of the same type or opposite kind has already been specified for the key)" diff --git a/sql/sql_base.cc b/sql/sql_base.cc index f40138e3243..b6bcf51e8a0 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -8361,8 +8361,31 @@ bool setup_tables(THD *thd, Name_resolution_context *context, { setup_table_map(table, table_list, tablenr); - if (table_list->process_index_hints(table)) - DBUG_RETURN(1); + /* + Conditions to meet for optimizer hints resolution: + (1) QB hints initialized + (2) Table hints are not adjusted yet + (3) Table is not in the INSERT part of INSERT..SELECT + */ + if (qb_hints && // (1) + !table_list->opt_hints_table && // (2) + !(select_insert && is_insert_part)) // (3) + { + table_list->opt_hints_table= + qb_hints->fix_hints_for_table(table_list->table, + table_list->alias); + } + + if (!table_list->opt_hints_table || + !table_list->opt_hints_table->update_index_hint_maps(thd, table)) + { + /* + Old-style index hints are processed only if + new-style hints are not specified + */ + if (table_list->process_index_hints(table)) + DBUG_RETURN(1); + } } tablenr++; /* @@ -8375,20 +8398,6 @@ bool setup_tables(THD *thd, Name_resolution_context *context, DBUG_RETURN(1); } - /* - Conditions to meet for optimizer hints resolution: - (1) QB hints initialized - (2) Table hints are not adjusted yet - (3) Table is not in the INSERT part of INSERT..SELECT - */ - if (qb_hints && // (1) - !table_list->opt_hints_table && // (2) - !(select_insert && is_insert_part)) // (3) - { - table_list->opt_hints_table= - qb_hints->fix_hints_for_table(table_list->table, - table_list->alias); - } } if (select_insert && is_insert_part) { @@ -8413,12 +8422,22 @@ bool setup_tables(THD *thd, Name_resolution_context *context, } else { - table_list->table->tablenr= table_list->tablenr_exec; - table_list->table->map= table_list->map_exec; - table_list->table->maybe_null= table_list->maybe_null_exec; - table_list->table->pos_in_table_list= table_list; - if (table_list->process_index_hints(table_list->table)) - DBUG_RETURN(1); + TABLE *table= table_list->table; + table->tablenr= table_list->tablenr_exec; + table->map= table_list->map_exec; + table->maybe_null= table_list->maybe_null_exec; + table->pos_in_table_list= table_list; + + if (!table_list->opt_hints_table || + !table_list->opt_hints_table->update_index_hint_maps(thd, table)) + { + /* + Old-style index hints are processed only if + new-style hints are not specified + */ + if (table_list->process_index_hints(table)) + DBUG_RETURN(1); + } } select_lex->leaf_tables.push_back(table_list); } diff --git a/sql/table.h b/sql/table.h index 43267ac2fa3..7ac81d10495 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1554,13 +1554,15 @@ public: /** Flag set when the statement contains FORCE INDEX FOR ORDER BY - See TABLE_LIST::process_index_hints(). + See TABLE_LIST::process_index_hints(), + Opt_hints_table::update_index_hint_maps() */ bool force_index_order; /** Flag set when the statement contains FORCE INDEX FOR GROUP BY - See TABLE_LIST::process_index_hints(). + See TABLE_LIST::process_index_hints(), + Opt_hints_table::update_index_hint_maps() */ bool force_index_group; /*