MDEV-35856: implement index hints

Part of an umbrella task MDEV-33281 for implementing optimizer hints.

This commit introduces hints affecting the use of indexes:
  - JOIN_INDEX, NO_JOIN_INDEX
  - GROUP_INDEX, NO_GROUP_INDEX
  - ORDER_INDEX, NO_ORDER_INDEX
  - INDEX, NO_INDEX

Syntax of index hints:
  hint_name([@query_block_name] tbl_name [index_name [, index_name] ...])
  hint_name(tbl_name@query_block_name [index_name [, index_name] ...])

JOIN_INDEX, NO_JOIN_INDEX: Forces the server to use or ignore the specified
index or indexes for any access method, such as ref, range, index_merge,
and so on. Equivalent to FORCE INDEX FOR JOIN, IGNORE INDEX FOR JOIN.

GROUP_INDEX, NO_GROUP_INDEX: Enable or disable the specified index or indexes
for index scans for GROUP BY operations. Equivalent to the index hints
FORCE INDEX FOR GROUP BY, IGNORE INDEX FOR GROUP BY.

ORDER_INDEX, NO_ORDER_INDEX: Causes the server to use or to ignore
the specified index or indexes for sorting rows. Equivalent to
FORCE INDEX FOR ORDER BY, IGNORE INDEX FOR ORDER BY.

INDEX, NO_INDEX: Acts as the combination of JOIN_INDEX, GROUP_INDEX
and ORDER_INDEX, forcing the server to use the specified index or indexes
for any and all scopes, or as the combination of NO_JOIN_INDEX, NO_GROUP_INDEX
and NO_ORDER_INDEX, which causes the server to ignore the specified index
or indexes for any and all scopes. Equivalent to FORCE INDEX, IGNORE INDEX.

Two kinds of index hints were introduced during implementation:
the global kind for [NO_]INDEX hint, and the non-global kind for all others.

Possible conflicts which will generate warnings:
- for a table level hint
  - a hint of the same type or the 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
This commit is contained in:
Oleg Smirnov 2025-05-01 13:38:10 +07:00
parent 6a2afb42ba
commit 69dee99c5f
11 changed files with 1123 additions and 80 deletions

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -17,6 +17,7 @@
#ifndef MEM_ROOT_ARRAY_INCLUDED
#define MEM_ROOT_ARRAY_INCLUDED
#include <type_traits>
#include <my_alloc.h>
/**
@ -44,7 +45,9 @@
compilers we use.
*/
template<typename Element_type, bool has_trivial_destructor>
template<typename Element_type,
bool has_trivial_destructor=
std::is_trivially_destructible<Element_type>::value>
class Mem_root_array
{
public:
@ -263,5 +266,4 @@ private:
}
};
#endif // MEM_ROOT_ARRAY_INCLUDED

View File

@ -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<opt_hints_enum>(i);
if (is_specified(hint_type))
opt_hints_enum hint= static_cast<opt_hints_enum>(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<Opt_hints_key *>(*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];

View File

@ -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<MAX_INDEXES>;
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<Opt_hints*, true> child_array;
Mem_root_array<Opt_hints *> 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<Opt_hints*, true> *child_array_ptr() { return &child_array; }
Mem_root_array<Opt_hints *> *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<Parser::Join_order_hint *, false> join_order_hints;
Mem_root_array<Parser::Join_order_hint *> 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<Opt_hints_key*, true> keyinfo_array;
/*
keyinfo_array[IDX] has all hint prescriptions for index number IDX.
*/
Mem_root_array<Opt_hints_key *> 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);
}
};

View File

@ -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<std::pair<Opt_hints_key *,
bool /* whether a new one was created */>>
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<Opt_hints_key *, bool> 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.

View File

@ -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<Parser,
@ -604,18 +629,20 @@ private:
using AND2::AND2;
};
public:
// index_level_hint ::= index_level_hint_type ( index_level_hint_body )
class Index_level_hint: public AND4<Parser,
Index_level_hint_type,
LParen,
Index_level_hint_body,
RParen>
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
{

View File

@ -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)"

View File

@ -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);
}

View File

@ -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;
/*