mirror of
https://github.com/MariaDB/server.git
synced 2026-10-23 18:49:29 +03:00
MDEV-36089 New-style hint: [NO_]ROWID_FILTER
This commit introduces two optimizer hints: ROWID_FILTER and NO_ROWID_FILTER. They allow explicit control over whether certain indexes are used to build rowid filters. Syntax: [NO_]ROWID_FILTER(tbl_name@query_block_name [index_name [, index_name] ...]) Hints can be applied at two levels: Table-level (only table name specified): enables or disables rowid filters for the given table. Index-level (table and index names specified): forces the server to use or ignore the listed indexes when building rowid filters. Examples: SELECT /*+ rowid_filter(t1)*/ a FROM t1 WHERE a < 'e' AND b > 't'; SELECT /*+ no_rowid_filter(t1)*/ a FROM t1 WHERE a < 'e' AND b > 't'; SELECT /*+ rowid_filter(t1 key_a, key_b)*/ a FROM t1 WHERE a < 'e' AND b > 't'; SELECT /*+ no_rowid_filter(t1 key_c)*/ a FROM t1 WHERE a < 'e' AND b > 't'; ===== Interaction with other index hints ===== If [NO_]INDEX, [NO_]JOIN_INDEX hints are specified for the same table, then only indexes whitelisted by INDEX() or JOIN_INDEX() can be considered for rowid filters. ROWID_FILTER() hint cannot force the use of indexes that are disabled by NO_INDEX() / NO_JOIN_INDEX() or omitted from INDEX() / JOIN_INDEX(). For example, if one index should be used for data access and another for a rowid filter, the filter index must be mentioned in both hints: SELECT /*+ index(t1 key_a, key_b) rowid_filter(t1 key_b) a FROM t1 ....
This commit is contained in:
committed by
Oleg Smirnov
parent
3b5901a1f3
commit
8e9213da94
289
mysql-test/main/opt_hint_rowid_filter.result
Normal file
289
mysql-test/main/opt_hint_rowid_filter.result
Normal file
@@ -0,0 +1,289 @@
|
||||
CREATE TABLE t1 (a INT, b VARCHAR(10), c VARCHAR(100), d VARCHAR(200),
|
||||
KEY key_a(a), KEY key_b(b), KEY key_c(c), KEY key_d(d));
|
||||
INSERT INTO t1 VALUES
|
||||
(1,'w','z','F'), (1,'X','o','s'), (1,'q','c','a'), (5,'w','c','d'), (2,'j','m','k'),
|
||||
(2,'Q','s','q'), (9,'e','J','B'), (2,'p','W','l'), (9,'o','F','Y'), (2,'g','S','M'),
|
||||
(1,'Y','a','h'), (NULL,'Y','p','J'), (NULL,'s','x','M'), (NULL,'i','S','k'),
|
||||
(1,'l','q','w'), (7,'r','e','_'), (4,'b','h','I'), (NULL,'E','c','z'),
|
||||
(NULL,'M','a','j'), (3,'e','X','K'), (NULL,'p','r','R'), (9,'e','i','g'),
|
||||
(3,'g','x','m'), (2,'h','y','p');
|
||||
ANALYZE TABLE t1;
|
||||
Table Op Msg_type Msg_text
|
||||
test.t1 analyze status Engine-independent statistics collected
|
||||
test.t1 analyze status OK
|
||||
set optimizer_switch='rowid_filter=on';
|
||||
# range|filter `key_d|key_b` is applied by default when there are no hints:
|
||||
EXPLAIN EXTENDED
|
||||
SELECT a FROM t1 WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range|filter key_b,key_c,key_d key_d|key_b 803|43 NULL 4 (21%) 4.34 Using index condition; Using where; Using rowid filter
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't' and `test`.`t1`.`d` < 'e'
|
||||
# Sample result set to compare against later
|
||||
SELECT a FROM t1 WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
a
|
||||
5
|
||||
# Disable rowid filter for t1
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ no_rowid_filter(t1)*/ a FROM t1 WHERE c < 'e' AND b > 't';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range key_b,key_c key_b 43 NULL 5 20.83 Using index condition; Using where
|
||||
Warnings:
|
||||
Note 1003 select /*+ NO_ROWID_FILTER(`t1`@`select#1`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't'
|
||||
# Force using rowid filter made from `key_c` despite being less
|
||||
# efficient then the one made from `key_b`
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ rowid_filter(t1 key_c)*/ a FROM t1 WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range|filter key_b,key_c,key_d key_d|key_c 803|403 NULL 4 (21%) 4.34 Using index condition; Using where; Using rowid filter
|
||||
Warnings:
|
||||
Note 1003 select /*+ ROWID_FILTER(`t1`@`select#1` `key_c`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't' and `test`.`t1`.`d` < 'e'
|
||||
# Allow the optimizer to choose the best rowid filter from key_a,key_b,key_c,key_d
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ rowid_filter(t1 key_a,key_b,key_c,key_d)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range|filter key_b,key_c,key_d key_d|key_b 803|43 NULL 4 (21%) 4.34 Using index condition; Using where; Using rowid filter
|
||||
Warnings:
|
||||
Note 1003 select /*+ ROWID_FILTER(`t1`@`select#1` `key_a`,`key_b`,`key_c`,`key_d`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't' and `test`.`t1`.`d` < 'e'
|
||||
# Forbid `key_b` for rowid filtering, so `key_c` is used
|
||||
EXPLAIN EXTENDED SELECT /*+ no_rowid_filter(t1 key_b)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range|filter key_b,key_c,key_d key_d|key_c 803|403 NULL 4 (21%) 4.34 Using index condition; Using where; Using rowid filter
|
||||
Warnings:
|
||||
Note 1003 select /*+ NO_ROWID_FILTER(`t1`@`select#1` `key_b`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't' and `test`.`t1`.`d` < 'e'
|
||||
# Validate the result set
|
||||
SELECT /*+ no_rowid_filter(t1 key_b)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
a
|
||||
5
|
||||
# Forbid both `key_b` and `key_c`, so no rowid filter can be applied
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ no_rowid_filter(t1 key_b, key_c)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range key_b,key_c,key_d key_d 803 NULL 4 4.34 Using index condition; Using where
|
||||
Warnings:
|
||||
Note 1003 select /*+ NO_ROWID_FILTER(`t1`@`select#1` `key_b`,`key_c`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't' and `test`.`t1`.`d` < 'e'
|
||||
# Disable rowid filter for indexes which would not be used anyway
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ no_rowid_filter(t1 key_a)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range|filter key_b,key_c,key_d key_d|key_b 803|43 NULL 4 (21%) 4.34 Using index condition; Using where; Using rowid filter
|
||||
Warnings:
|
||||
Note 1003 select /*+ NO_ROWID_FILTER(`t1`@`select#1` `key_a`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't' and `test`.`t1`.`d` < 'e'
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ no_rowid_filter(t1 key_a,key_b)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range|filter key_b,key_c,key_d key_d|key_c 803|403 NULL 4 (21%) 4.34 Using index condition; Using where; Using rowid filter
|
||||
Warnings:
|
||||
Note 1003 select /*+ NO_ROWID_FILTER(`t1`@`select#1` `key_a`,`key_b`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't' and `test`.`t1`.`d` < 'e'
|
||||
# Filter use is not beneficial for this query, so there is just 'range' access
|
||||
EXPLAIN EXTENDED
|
||||
SELECT a FROM t1 WHERE c < 'z' AND b > 't';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range key_b,key_c key_b 43 NULL 5 95.83 Using index condition; Using where
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'z' and `test`.`t1`.`b` > 't'
|
||||
# Force the use of the rowid filter anyway:
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ rowid_filter(t1 key_c)*/ a FROM t1 WHERE c < 'z' AND b > 't';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range|filter key_b,key_c key_b|key_c 43|403 NULL 5 (96%) 95.83 Using index condition; Using where; Using rowid filter
|
||||
Warnings:
|
||||
Note 1003 select /*+ ROWID_FILTER(`t1`@`select#1` `key_c`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'z' and `test`.`t1`.`b` > 't'
|
||||
# Test the hint with query blocks
|
||||
set optimizer_switch='derived_merge=off';
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ no_rowid_filter(t1@qb1)*/ * FROM (
|
||||
SELECT /*+ qb_name(qb1)*/ a FROM t1 WHERE c < 'e' AND b > 't') dt;
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 5 100.00
|
||||
2 DERIVED t1 range key_b,key_c key_b 43 NULL 5 20.83 Using index condition; Using where
|
||||
Warnings:
|
||||
Note 1003 /* select#1 */ select /*+ NO_ROWID_FILTER(`t1`@`qb1`) */ `dt`.`a` AS `a` from (/* select#2 */ select /*+ QB_NAME(`qb1`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't') `dt`
|
||||
# `key_b|key_c` access is forced
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ /*+ rowid_filter(t1@qb1 key_c)*/ * FROM (
|
||||
SELECT /*+ qb_name(qb1)*/ a FROM t1 WHERE c < 'e' AND b > 't') dt;
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 2 100.00
|
||||
2 DERIVED t1 range|filter key_b,key_c key_b|key_c 43|403 NULL 5 (21%) 20.83 Using index condition; Using where; Using rowid filter
|
||||
Warnings:
|
||||
Warning 1064 Optimizer hint syntax error near '/*+ rowid_filter(t1@qb1 key_c)*/ * FROM (
|
||||
SELECT /*+ qb_name(qb1)*/ a FROM t1...' at line 2
|
||||
Note 1003 /* select#1 */ select `dt`.`a` AS `a` from (/* select#2 */ select /*+ QB_NAME(`qb1`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't') `dt`
|
||||
set optimizer_switch=default;
|
||||
# Turn rowid filtering off and enable it selectively
|
||||
set optimizer_switch='rowid_filter=off';
|
||||
# Make sure rowid filter is not used when there are no hints:
|
||||
EXPLAIN EXTENDED
|
||||
SELECT a FROM t1 WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range key_b,key_c,key_d key_d 803 NULL 4 4.34 Using index condition; Using where
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't' and `test`.`t1`.`d` < 'e'
|
||||
# Enable rowid filter for t1
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ rowid_filter(t1)*/ a FROM t1 WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range|filter key_b,key_c,key_d key_d|key_b 803|43 NULL 4 (21%) 4.34 Using index condition; Using where; Using rowid filter
|
||||
Warnings:
|
||||
Note 1003 select /*+ ROWID_FILTER(`t1`@`select#1`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't' and `test`.`t1`.`d` < 'e'
|
||||
# Enable rowid filter only for some indexes of t1
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ rowid_filter(t1 key_a, key_c)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range|filter key_b,key_c,key_d key_d|key_c 803|403 NULL 4 (21%) 4.34 Using index condition; Using where; Using rowid filter
|
||||
Warnings:
|
||||
Note 1003 select /*+ ROWID_FILTER(`t1`@`select#1` `key_a`,`key_c`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't' and `test`.`t1`.`d` < 'e'
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ rowid_filter(t1 key_c)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range|filter key_b,key_c,key_d key_d|key_c 803|403 NULL 4 (21%) 4.34 Using index condition; Using where; Using rowid filter
|
||||
Warnings:
|
||||
Note 1003 select /*+ ROWID_FILTER(`t1`@`select#1` `key_c`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't' and `test`.`t1`.`d` < 'e'
|
||||
# Enable rowid filter for the same index that is used to access data (impossible)
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ rowid_filter(t1 key_d)*/ a FROM t1 WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range key_b,key_c,key_d key_d 803 NULL 4 4.34 Using index condition; Using where
|
||||
Warnings:
|
||||
Note 1003 select /*+ ROWID_FILTER(`t1`@`select#1` `key_d`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't' and `test`.`t1`.`d` < 'e'
|
||||
# Conflicting hints
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ no_rowid_filter(t1) ROWID_FILTER(t1 key_a)*/a FROM t1
|
||||
WHERE c < 'e' AND b > 't';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range key_b,key_c key_b 43 NULL 5 20.83 Using index condition; Using where
|
||||
Warnings:
|
||||
Warning 4238 Hint ROWID_FILTER(`t1` `key_a`) is ignored as conflicting/duplicated (an index hint of the same type has already been specified for this table)
|
||||
Note 1003 select /*+ NO_ROWID_FILTER(`t1`@`select#1`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't'
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ no_rowid_filter(t1 key_a, key_b) no_rowid_filter(t1 key_c, key_b)*/a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range|filter key_b,key_c,key_d key_d|key_c 803|403 NULL 4 (21%) 4.34 Using index condition; Using where; Using rowid filter
|
||||
Warnings:
|
||||
Warning 4238 Hint NO_ROWID_FILTER(`t1` `key_c`,`key_b`) is ignored as conflicting/duplicated (an index hint of the same type has already been specified for this table)
|
||||
Note 1003 select /*+ NO_ROWID_FILTER(`t1`@`select#1` `key_a`,`key_b`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't' and `test`.`t1`.`d` < 'e'
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ no_rowid_filter(t1 key_a,key_b) rowid_filter(t1 key_c)*/a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range|filter key_b,key_c,key_d key_d|key_c 803|403 NULL 4 (21%) 4.34 Using index condition; Using where; Using rowid filter
|
||||
Warnings:
|
||||
Warning 4238 Hint ROWID_FILTER(`t1` `key_c`) is ignored as conflicting/duplicated (an index hint of the same type has already been specified for this table)
|
||||
Note 1003 select /*+ NO_ROWID_FILTER(`t1`@`select#1` `key_a`,`key_b`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't' and `test`.`t1`.`d` < 'e'
|
||||
# Wrong table name
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ no_rowid_filter(missing_table)*/a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range key_b,key_c,key_d key_d 803 NULL 4 4.34 Using index condition; Using where
|
||||
Warnings:
|
||||
Warning 4221 Unresolved table name `missing_table`@`select#1` for NO_ROWID_FILTER hint
|
||||
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't' and `test`.`t1`.`d` < 'e'
|
||||
# Some index names are wrong but key_c is applicable
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ rowid_filter(t1 wrong_key1, key_c, wrong_key2)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range|filter key_b,key_c,key_d key_d|key_c 803|403 NULL 4 (21%) 4.34 Using index condition; Using where; Using rowid filter
|
||||
Warnings:
|
||||
Warning 4222 Unresolved index name `t1`@`select#1` `wrong_key1` for ROWID_FILTER hint
|
||||
Warning 4222 Unresolved index name `t1`@`select#1` `wrong_key2` for ROWID_FILTER hint
|
||||
Note 1003 select /*+ ROWID_FILTER(`t1`@`select#1` `wrong_key1`,`key_c`,`wrong_key2`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't' and `test`.`t1`.`d` < 'e'
|
||||
# Test cases mixing with index hints.
|
||||
# Keys must be whitelisted in INDEX()/JOIN_INDEX() hint to be
|
||||
# applicable as a rowid filter. ROWID_FILTER() hint cannot enable keys
|
||||
# not enabled by INDEX()/JOIN_INDEX() hints or disabled by
|
||||
# NO_INDEX()/NO_JOIN_INDEX() hints
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ index(t1 key_b,key_c) rowid_filter(t1 key_c)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range|filter key_b,key_c key_b|key_c 43|403 NULL 5 (21%) 3.47 Using index condition; Using where; Using rowid filter
|
||||
Warnings:
|
||||
Note 1003 select /*+ INDEX(`t1`@`select#1` `key_b`,`key_c`) ROWID_FILTER(`t1`@`select#1` `key_c`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't' and `test`.`t1`.`d` < 'e'
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ join_index(t1 key_b,key_c) rowid_filter(t1 key_c)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range|filter key_b,key_c key_b|key_c 43|403 NULL 5 (21%) 3.47 Using index condition; Using where; Using rowid filter
|
||||
Warnings:
|
||||
Note 1003 select /*+ JOIN_INDEX(`t1`@`select#1` `key_b`,`key_c`) ROWID_FILTER(`t1`@`select#1` `key_c`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't' and `test`.`t1`.`d` < 'e'
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ index(t1 key_d,key_c) rowid_filter(t1 key_c)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range|filter key_c,key_d key_d|key_c 803|403 NULL 4 (21%) 4.34 Using index condition; Using where; Using rowid filter
|
||||
Warnings:
|
||||
Note 1003 select /*+ INDEX(`t1`@`select#1` `key_d`,`key_c`) ROWID_FILTER(`t1`@`select#1` `key_c`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't' and `test`.`t1`.`d` < 'e'
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ no_index(t1) rowid_filter(t1 key_c)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 ALL NULL NULL NULL NULL 24 0.72 Using where
|
||||
Warnings:
|
||||
Note 1003 select /*+ NO_INDEX(`t1`@`select#1`) ROWID_FILTER(`t1`@`select#1` `key_c`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't' and `test`.`t1`.`d` < 'e'
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ no_index(t1 key_d) rowid_filter(t1 key_c)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range|filter key_b,key_c key_b|key_c 43|403 NULL 5 (21%) 3.47 Using index condition; Using where; Using rowid filter
|
||||
Warnings:
|
||||
Note 1003 select /*+ NO_INDEX(`t1`@`select#1` `key_d`) ROWID_FILTER(`t1`@`select#1` `key_c`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't' and `test`.`t1`.`d` < 'e'
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ no_index(t1 key_c) rowid_filter(t1 key_c)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range key_b,key_d key_d 803 NULL 4 4.34 Using index condition; Using where
|
||||
Warnings:
|
||||
Note 1003 select /*+ NO_INDEX(`t1`@`select#1` `key_c`) ROWID_FILTER(`t1`@`select#1` `key_c`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't' and `test`.`t1`.`d` < 'e'
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ index(t1) no_rowid_filter(t1 key_c)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range|filter key_b,key_c,key_d key_d|key_b 803|43 NULL 4 (21%) 4.34 Using index condition; Using where; Using rowid filter
|
||||
Warnings:
|
||||
Note 1003 select /*+ INDEX(`t1`@`select#1`) NO_ROWID_FILTER(`t1`@`select#1` `key_c`) */ `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't' and `test`.`t1`.`d` < 'e'
|
||||
# Test cases for old-style hints
|
||||
EXPLAIN EXTENDED
|
||||
SELECT a FROM t1 FORCE INDEX(key_d, key_b)
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range key_b,key_d key_d 803 NULL 4 4.34 Using index condition; Using where
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` FORCE INDEX (`key_b`) FORCE INDEX (`key_d`) where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't' and `test`.`t1`.`d` < 'e'
|
||||
# `key_c` is not listed in the hint so is not applicable as rowid filter
|
||||
# (also see comment for new-style index hints above)
|
||||
EXPLAIN EXTENDED
|
||||
SELECT a FROM t1 FORCE INDEX(key_d)
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range key_d key_d 803 NULL 4 4.34 Using index condition; Using where
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` FORCE INDEX (`key_d`) where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't' and `test`.`t1`.`d` < 'e'
|
||||
# `key_c` is blacklisted, so `key_b` is used for rowid filter
|
||||
EXPLAIN EXTENDED
|
||||
SELECT a FROM t1 IGNORE INDEX(key_c)
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range key_b,key_d key_d 803 NULL 4 4.34 Using index condition; Using where
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` IGNORE INDEX (`key_c`) where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't' and `test`.`t1`.`d` < 'e'
|
||||
# `key_b` is blacklisted, so `key_c` is used for rowid filter
|
||||
EXPLAIN EXTENDED
|
||||
SELECT a FROM t1 IGNORE INDEX(key_b)
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
id select_type table type possible_keys key key_len ref rows filtered Extra
|
||||
1 SIMPLE t1 range key_c,key_d key_d 803 NULL 4 4.34 Using index condition; Using where
|
||||
Warnings:
|
||||
Note 1003 select `test`.`t1`.`a` AS `a` from `test`.`t1` IGNORE INDEX (`key_b`) where `test`.`t1`.`c` < 'e' and `test`.`t1`.`b` > 't' and `test`.`t1`.`d` < 'e'
|
||||
DROP TABLE t1;
|
||||
189
mysql-test/main/opt_hint_rowid_filter.test
Normal file
189
mysql-test/main/opt_hint_rowid_filter.test
Normal file
@@ -0,0 +1,189 @@
|
||||
--enable_prepare_warnings
|
||||
--disable_view_protocol # Since optimizer hints are not supported inside views
|
||||
|
||||
CREATE TABLE t1 (a INT, b VARCHAR(10), c VARCHAR(100), d VARCHAR(200),
|
||||
KEY key_a(a), KEY key_b(b), KEY key_c(c), KEY key_d(d));
|
||||
|
||||
INSERT INTO t1 VALUES
|
||||
(1,'w','z','F'), (1,'X','o','s'), (1,'q','c','a'), (5,'w','c','d'), (2,'j','m','k'),
|
||||
(2,'Q','s','q'), (9,'e','J','B'), (2,'p','W','l'), (9,'o','F','Y'), (2,'g','S','M'),
|
||||
(1,'Y','a','h'), (NULL,'Y','p','J'), (NULL,'s','x','M'), (NULL,'i','S','k'),
|
||||
(1,'l','q','w'), (7,'r','e','_'), (4,'b','h','I'), (NULL,'E','c','z'),
|
||||
(NULL,'M','a','j'), (3,'e','X','K'), (NULL,'p','r','R'), (9,'e','i','g'),
|
||||
(3,'g','x','m'), (2,'h','y','p');
|
||||
|
||||
ANALYZE TABLE t1;
|
||||
|
||||
set optimizer_switch='rowid_filter=on';
|
||||
--echo # range|filter `key_d|key_b` is applied by default when there are no hints:
|
||||
EXPLAIN EXTENDED
|
||||
SELECT a FROM t1 WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
|
||||
--echo # Sample result set to compare against later
|
||||
SELECT a FROM t1 WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
|
||||
--echo # Disable rowid filter for t1
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ no_rowid_filter(t1)*/ a FROM t1 WHERE c < 'e' AND b > 't';
|
||||
|
||||
--echo # Force using rowid filter made from `key_c` despite being less
|
||||
--echo # efficient then the one made from `key_b`
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ rowid_filter(t1 key_c)*/ a FROM t1 WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
|
||||
--echo # Allow the optimizer to choose the best rowid filter from key_a,key_b,key_c,key_d
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ rowid_filter(t1 key_a,key_b,key_c,key_d)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
|
||||
--echo # Forbid `key_b` for rowid filtering, so `key_c` is used
|
||||
let $q= SELECT /*+ no_rowid_filter(t1 key_b)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
|
||||
eval EXPLAIN EXTENDED $q;
|
||||
--echo # Validate the result set
|
||||
eval $q;
|
||||
|
||||
--echo # Forbid both `key_b` and `key_c`, so no rowid filter can be applied
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ no_rowid_filter(t1 key_b, key_c)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
|
||||
--echo # Disable rowid filter for indexes which would not be used anyway
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ no_rowid_filter(t1 key_a)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ no_rowid_filter(t1 key_a,key_b)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
|
||||
--echo # Filter use is not beneficial for this query, so there is just 'range' access
|
||||
EXPLAIN EXTENDED
|
||||
SELECT a FROM t1 WHERE c < 'z' AND b > 't';
|
||||
|
||||
--echo # Force the use of the rowid filter anyway:
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ rowid_filter(t1 key_c)*/ a FROM t1 WHERE c < 'z' AND b > 't';
|
||||
|
||||
|
||||
--echo # Test the hint with query blocks
|
||||
set optimizer_switch='derived_merge=off';
|
||||
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ no_rowid_filter(t1@qb1)*/ * FROM (
|
||||
SELECT /*+ qb_name(qb1)*/ a FROM t1 WHERE c < 'e' AND b > 't') dt;
|
||||
|
||||
--echo # `key_b|key_c` access is forced
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ /*+ rowid_filter(t1@qb1 key_c)*/ * FROM (
|
||||
SELECT /*+ qb_name(qb1)*/ a FROM t1 WHERE c < 'e' AND b > 't') dt;
|
||||
|
||||
set optimizer_switch=default;
|
||||
|
||||
--echo # Turn rowid filtering off and enable it selectively
|
||||
set optimizer_switch='rowid_filter=off';
|
||||
|
||||
--echo # Make sure rowid filter is not used when there are no hints:
|
||||
EXPLAIN EXTENDED
|
||||
SELECT a FROM t1 WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
|
||||
--echo # Enable rowid filter for t1
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ rowid_filter(t1)*/ a FROM t1 WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
|
||||
--echo # Enable rowid filter only for some indexes of t1
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ rowid_filter(t1 key_a, key_c)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ rowid_filter(t1 key_c)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
|
||||
--echo # Enable rowid filter for the same index that is used to access data (impossible)
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ rowid_filter(t1 key_d)*/ a FROM t1 WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
|
||||
--echo # Conflicting hints
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ no_rowid_filter(t1) ROWID_FILTER(t1 key_a)*/a FROM t1
|
||||
WHERE c < 'e' AND b > 't';
|
||||
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ no_rowid_filter(t1 key_a, key_b) no_rowid_filter(t1 key_c, key_b)*/a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ no_rowid_filter(t1 key_a,key_b) rowid_filter(t1 key_c)*/a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
|
||||
# Warnings "Unresolved table/index name..." are generated during both prepare
|
||||
# and execution stages. So disable PS protocol to avoid duplication
|
||||
--disable_ps_protocol
|
||||
--echo # Wrong table name
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ no_rowid_filter(missing_table)*/a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
|
||||
--echo # Some index names are wrong but key_c is applicable
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ rowid_filter(t1 wrong_key1, key_c, wrong_key2)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
--enable_ps_protocol
|
||||
|
||||
--echo # Test cases mixing with index hints.
|
||||
--echo # Keys must be whitelisted in INDEX()/JOIN_INDEX() hint to be
|
||||
--echo # applicable as a rowid filter. ROWID_FILTER() hint cannot enable keys
|
||||
--echo # not enabled by INDEX()/JOIN_INDEX() hints or disabled by
|
||||
--echo # NO_INDEX()/NO_JOIN_INDEX() hints
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ index(t1 key_b,key_c) rowid_filter(t1 key_c)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ join_index(t1 key_b,key_c) rowid_filter(t1 key_c)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ index(t1 key_d,key_c) rowid_filter(t1 key_c)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ no_index(t1) rowid_filter(t1 key_c)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ no_index(t1 key_d) rowid_filter(t1 key_c)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ no_index(t1 key_c) rowid_filter(t1 key_c)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
|
||||
EXPLAIN EXTENDED
|
||||
SELECT /*+ index(t1) no_rowid_filter(t1 key_c)*/ a FROM t1
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
|
||||
--echo # Test cases for old-style hints
|
||||
EXPLAIN EXTENDED
|
||||
SELECT a FROM t1 FORCE INDEX(key_d, key_b)
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
|
||||
--echo # `key_c` is not listed in the hint so is not applicable as rowid filter
|
||||
--echo # (also see comment for new-style index hints above)
|
||||
EXPLAIN EXTENDED
|
||||
SELECT a FROM t1 FORCE INDEX(key_d)
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
|
||||
--echo # `key_c` is blacklisted, so `key_b` is used for rowid filter
|
||||
EXPLAIN EXTENDED
|
||||
SELECT a FROM t1 IGNORE INDEX(key_c)
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
|
||||
--echo # `key_b` is blacklisted, so `key_c` is used for rowid filter
|
||||
EXPLAIN EXTENDED
|
||||
SELECT a FROM t1 IGNORE INDEX(key_b)
|
||||
WHERE c < 'e' AND b > 't' and d < 'e';
|
||||
|
||||
DROP TABLE t1;
|
||||
@@ -1150,7 +1150,7 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
|
||||
buf_manager.redistribute_buffer_space= do_nothing;
|
||||
|
||||
if (!hint_key_state(thd, table, h_arg->active_index,
|
||||
MRR_HINT_ENUM, OPTIMIZER_SWITCH_MRR) ||
|
||||
MRR_HINT_ENUM, optimizer_flag(thd, OPTIMIZER_SWITCH_MRR)) ||
|
||||
mode & (HA_MRR_USE_DEFAULT_IMPL | HA_MRR_SORTED))
|
||||
goto use_default_impl;
|
||||
|
||||
@@ -1905,9 +1905,9 @@ bool DsMrr_impl::choose_mrr_impl(uint keyno, ha_rows rows, uint *flags,
|
||||
TABLE_SHARE *share= primary_file->get_table_share();
|
||||
|
||||
const bool mrr_on= hint_key_state(thd, table, keyno, MRR_HINT_ENUM,
|
||||
OPTIMIZER_SWITCH_MRR);
|
||||
optimizer_flag(thd, OPTIMIZER_SWITCH_MRR));
|
||||
const bool force_dsmrr_by_hints=
|
||||
hint_key_state(thd, table, keyno, MRR_HINT_ENUM, 0) ||
|
||||
hint_key_state(thd, table, keyno, MRR_HINT_ENUM, false) ||
|
||||
hint_table_state(thd, table, BKA_HINT_ENUM, false);
|
||||
|
||||
bool doing_cpk_scan= check_cpk_scan(thd, share, keyno, *flags);
|
||||
|
||||
146
sql/opt_hints.cc
146
sql/opt_hints.cc
@@ -32,26 +32,28 @@
|
||||
|
||||
struct st_opt_hint_info opt_hint_info[]=
|
||||
{
|
||||
{{STRING_WITH_LEN("BKA")}, true, false, false},
|
||||
{{STRING_WITH_LEN("BNL")}, true, false, false},
|
||||
{{STRING_WITH_LEN("ICP")}, true, false, false},
|
||||
{{STRING_WITH_LEN("MRR")}, true, false, false},
|
||||
{{STRING_WITH_LEN("NO_RANGE_OPTIMIZATION")}, true, false, false},
|
||||
{{STRING_WITH_LEN("QB_NAME")}, false, false, false},
|
||||
{{STRING_WITH_LEN("MAX_EXECUTION_TIME")}, false, true, false},
|
||||
{{STRING_WITH_LEN("SEMIJOIN")}, false, true, false},
|
||||
{{STRING_WITH_LEN("SUBQUERY")}, false, true, false},
|
||||
{{STRING_WITH_LEN("JOIN_PREFIX")}, false, true, true},
|
||||
{{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},
|
||||
// hint_type check_upper has_args irregular
|
||||
{{STRING_WITH_LEN("BKA")}, true, false, false},
|
||||
{{STRING_WITH_LEN("BNL")}, true, false, false},
|
||||
{{STRING_WITH_LEN("ICP")}, true, false, false},
|
||||
{{STRING_WITH_LEN("MRR")}, true, false, false},
|
||||
{{STRING_WITH_LEN("NO_RANGE_OPTIMIZATION")}, true, false, false},
|
||||
{{STRING_WITH_LEN("QB_NAME")}, false, false, false},
|
||||
{{STRING_WITH_LEN("MAX_EXECUTION_TIME")}, false, true, false},
|
||||
{{STRING_WITH_LEN("SEMIJOIN")}, false, true, false},
|
||||
{{STRING_WITH_LEN("SUBQUERY")}, false, true, false},
|
||||
{{STRING_WITH_LEN("JOIN_PREFIX")}, false, true, true},
|
||||
{{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("DERIVED_CONDITION_PUSHDOWN")}, false, false, false},
|
||||
{{STRING_WITH_LEN("MERGE")}, false, false, false},
|
||||
{{STRING_WITH_LEN("SPLIT_MATERIALIZED")}, false, false, 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},
|
||||
{{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},
|
||||
{{STRING_WITH_LEN("ROWID_FILTER")}, false, true, false},
|
||||
{null_clex_str, 0, 0, 0}
|
||||
};
|
||||
|
||||
@@ -584,7 +586,8 @@ bool Opt_hints_table::fix_key_hints(TABLE *table)
|
||||
*/
|
||||
for (opt_hints_enum
|
||||
hint_type : { INDEX_HINT_ENUM, JOIN_INDEX_HINT_ENUM,
|
||||
GROUP_INDEX_HINT_ENUM, ORDER_INDEX_HINT_ENUM })
|
||||
GROUP_INDEX_HINT_ENUM, ORDER_INDEX_HINT_ENUM,
|
||||
ROWID_FILTER_HINT_ENUM })
|
||||
{
|
||||
if (is_specified(hint_type))
|
||||
{
|
||||
@@ -630,6 +633,8 @@ void Opt_hints_table::set_compound_key_hint_map(Opt_hints *hint, uint keynr)
|
||||
group_index_map.set_key_map(keynr);
|
||||
if (hint->is_specified(ORDER_INDEX_HINT_ENUM))
|
||||
order_index_map.set_key_map(keynr);
|
||||
if (hint->is_specified(ROWID_FILTER_HINT_ENUM))
|
||||
rowid_filter_map.set_key_map(keynr);
|
||||
}
|
||||
|
||||
|
||||
@@ -644,6 +649,8 @@ Opt_hints_key_bitmap *Opt_hints_table::get_key_hint_bitmap(opt_hints_enum type)
|
||||
return &group_index_map;
|
||||
case ORDER_INDEX_HINT_ENUM:
|
||||
return &order_index_map;
|
||||
case ROWID_FILTER_HINT_ENUM:
|
||||
return &rowid_filter_map;
|
||||
default:
|
||||
DBUG_ASSERT(0);
|
||||
return nullptr;
|
||||
@@ -666,6 +673,7 @@ void Opt_hints_table::update_index_hint_map(Key_map *keys_to_use,
|
||||
// Check if hint is resolved.
|
||||
if (is_fixed(type_arg))
|
||||
{
|
||||
// Whitelisting hints: INDEX(), ORDER_INDEX(), etc
|
||||
Key_map *keys_specified_in_hint=
|
||||
get_key_hint_bitmap(type_arg)->get_key_map();
|
||||
if (get_switch(type_arg))
|
||||
@@ -691,6 +699,7 @@ void Opt_hints_table::update_index_hint_map(Key_map *keys_to_use,
|
||||
}
|
||||
else
|
||||
{
|
||||
// Blacklisting hints: NO_INDEX(), NO_JOIN_INDEX(), etc
|
||||
if (keys_specified_in_hint->is_clear_all())
|
||||
{
|
||||
/*
|
||||
@@ -703,7 +712,7 @@ void Opt_hints_table::update_index_hint_map(Key_map *keys_to_use,
|
||||
{
|
||||
/*
|
||||
If hint is off and some keys are specified in the hint, then remove
|
||||
the specified keys from "keys_to_use.
|
||||
the specified keys from "keys_to_use"
|
||||
*/
|
||||
keys_to_use->subtract(*keys_specified_in_hint);
|
||||
}
|
||||
@@ -724,9 +733,9 @@ void Opt_hints_table::update_index_hint_map(Key_map *keys_to_use,
|
||||
GROUP_INDEX
|
||||
- tbl->keys_in_use_for_order_by if the hint is INDEX or
|
||||
ORDER_INDEX
|
||||
- tbl->keys_in_use_for_rowid_filter if the hint is ROWID_FILTER
|
||||
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.
|
||||
|
||||
@@ -741,20 +750,22 @@ void Opt_hints_table::update_index_hint_map(Key_map *keys_to_use,
|
||||
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))
|
||||
!is_fixed(GROUP_INDEX_HINT_ENUM) && !is_fixed(ORDER_INDEX_HINT_ENUM) &&
|
||||
!is_fixed(ROWID_FILTER_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;
|
||||
tbl->keys_in_use_for_order_by= tbl->keys_in_use_for_rowid_filter=
|
||||
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));
|
||||
bool is_global_whitelisting= is_whitelisting_index_hint(INDEX_HINT_ENUM);
|
||||
tbl->force_index_join= (is_global_whitelisting ||
|
||||
is_whitelisting_index_hint(JOIN_INDEX_HINT_ENUM));
|
||||
tbl->force_index_group= (is_global_whitelisting ||
|
||||
is_whitelisting_index_hint(GROUP_INDEX_HINT_ENUM));
|
||||
tbl->force_index_order= (is_global_whitelisting ||
|
||||
is_whitelisting_index_hint(ORDER_INDEX_HINT_ENUM));
|
||||
|
||||
if (tbl->force_index_join)
|
||||
tbl->keys_in_use_for_query.clear_all();
|
||||
@@ -762,6 +773,8 @@ bool Opt_hints_table::update_index_hint_maps(THD *thd, TABLE *tbl)
|
||||
tbl->keys_in_use_for_group_by.clear_all();
|
||||
if (tbl->force_index_order)
|
||||
tbl->keys_in_use_for_order_by.clear_all();
|
||||
if (is_whitelisting_index_hint(ROWID_FILTER_HINT_ENUM))
|
||||
tbl->keys_in_use_for_rowid_filter.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 |
|
||||
@@ -779,6 +792,19 @@ bool Opt_hints_table::update_index_hint_maps(THD *thd, TABLE *tbl)
|
||||
GROUP_INDEX_HINT_ENUM);
|
||||
update_index_hint_map(&tbl->keys_in_use_for_order_by, &usable_index_map,
|
||||
ORDER_INDEX_HINT_ENUM);
|
||||
if (is_fixed(ROWID_FILTER_HINT_ENUM))
|
||||
{
|
||||
update_index_hint_map(&tbl->keys_in_use_for_rowid_filter, &usable_index_map,
|
||||
ROWID_FILTER_HINT_ENUM);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
If ROWID_FILTER/NO_ROWID_FILTER hint is not specified, then keys
|
||||
for building ROWID filters are the same as for retrieving data
|
||||
*/
|
||||
tbl->keys_in_use_for_rowid_filter= tbl->keys_in_use_for_query;
|
||||
}
|
||||
/* 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);
|
||||
@@ -805,6 +831,9 @@ void Opt_hints_table::append_hint_arguments(THD *thd, opt_hints_enum hint,
|
||||
case ORDER_INDEX_HINT_ENUM:
|
||||
order_index_map.parsed_hint->append_args(thd, str);
|
||||
break;
|
||||
case ROWID_FILTER_HINT_ENUM:
|
||||
rowid_filter_map.parsed_hint->append_args(thd, str);
|
||||
break;
|
||||
default:
|
||||
DBUG_ASSERT(0);
|
||||
}
|
||||
@@ -905,31 +934,46 @@ hint_state hint_table_state(const THD *thd,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
@brief
|
||||
Check whether a given optimization is enabled for table.keyno.
|
||||
|
||||
@detail
|
||||
First check if a hint is present, then check optimizer_switch
|
||||
*/
|
||||
|
||||
bool hint_key_state(const THD *thd, const TABLE *table,
|
||||
uint keyno, opt_hints_enum type_arg,
|
||||
uint optimizer_switch)
|
||||
bool hint_key_state(const THD *thd, const TABLE *table, uint keyno,
|
||||
opt_hints_enum type_arg, bool fallback_value)
|
||||
{
|
||||
Opt_hints_table *table_hints= table->pos_in_table_list->opt_hints_table;
|
||||
|
||||
/* Parent should always be initialized */
|
||||
if (table_hints && keyno != MAX_KEY)
|
||||
{
|
||||
Opt_hints_key *key_hints= table_hints->keyinfo_array.size() > 0 ?
|
||||
table_hints->keyinfo_array[keyno] : NULL;
|
||||
bool ret_val= false;
|
||||
if (get_hint_state(key_hints, table_hints, type_arg, &ret_val))
|
||||
return ret_val;
|
||||
if (!is_compound_hint(type_arg))
|
||||
{
|
||||
// Simple index hint
|
||||
Opt_hints_key *key_hints= table_hints->keyinfo_array.size() > 0 ?
|
||||
table_hints->keyinfo_array[keyno] : NULL;
|
||||
bool ret_val= false;
|
||||
if (get_hint_state(key_hints, table_hints, type_arg, &ret_val))
|
||||
return ret_val;
|
||||
}
|
||||
else if (table_hints->is_fixed(type_arg))
|
||||
{
|
||||
// Compound index hint
|
||||
Key_map *keys_specified_in_hint=
|
||||
table_hints->get_key_hint_bitmap(type_arg)->get_key_map();
|
||||
if (keys_specified_in_hint->is_clear_all())
|
||||
{
|
||||
/*
|
||||
No keys are specified (i.e., it is a table-level hint).
|
||||
This means either all or no keys can be used, depending on whether
|
||||
the hint is whitelisting (INDEX, GROUP_INDEX) or blacklisting
|
||||
(NO_INDEX, NO_ORDER_INDEX)
|
||||
*/
|
||||
return table_hints->get_switch(type_arg);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool is_specified= keys_specified_in_hint->is_set(keyno);
|
||||
bool is_on= table_hints->get_switch(type_arg);
|
||||
return (is_on && is_specified) || (!is_on && !is_specified);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return optimizer_flag(thd, optimizer_switch);
|
||||
return fallback_value;
|
||||
}
|
||||
|
||||
|
||||
@@ -1358,6 +1402,9 @@ bool is_index_hint_conflicting(Opt_hints_table *table_hint,
|
||||
Opt_hints_key *key_hint,
|
||||
opt_hints_enum hint_type)
|
||||
{
|
||||
if (hint_type == ROWID_FILTER_HINT_ENUM)
|
||||
return table_or_key_hint_type_specified(table_hint, key_hint,
|
||||
ROWID_FILTER_HINT_ENUM);
|
||||
if (hint_type != INDEX_HINT_ENUM)
|
||||
return table_or_key_hint_type_specified(table_hint, key_hint,
|
||||
INDEX_HINT_ENUM);
|
||||
@@ -1374,7 +1421,8 @@ 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);
|
||||
type_arg == GROUP_INDEX_HINT_ENUM || type_arg == ORDER_INDEX_HINT_ENUM ||
|
||||
type_arg == ROWID_FILTER_HINT_ENUM);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -657,6 +657,7 @@ public:
|
||||
Key_map *get_key_map() { return &key_map; }
|
||||
};
|
||||
|
||||
bool is_compound_hint(opt_hints_enum type_arg);
|
||||
|
||||
/**
|
||||
Table level hints.
|
||||
@@ -679,6 +680,7 @@ public:
|
||||
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_key_bitmap rowid_filter_map; // ROWID_FILTER(), NO_ROWID_FILTER()
|
||||
|
||||
Opt_hints_table(const Lex_ident_sys &table_name_arg,
|
||||
Opt_hints_qb *qb_hints_arg,
|
||||
@@ -739,8 +741,17 @@ public:
|
||||
bool update_index_hint_maps(THD *thd, TABLE *tbl);
|
||||
|
||||
private:
|
||||
bool is_force_index_hint(opt_hints_enum type_arg)
|
||||
/**
|
||||
Returns `true` for index hints that whitelist certain keys or tables
|
||||
like INDEX(), ORDER_INDEX(), etc.
|
||||
Returns `false` for hints that blacklist keys or tables:
|
||||
NO_INDEX(), NO_ROWID_FILTER(), etc.
|
||||
|
||||
@param type_arg hint type
|
||||
*/
|
||||
bool is_whitelisting_index_hint(opt_hints_enum type_arg)
|
||||
{
|
||||
DBUG_ASSERT(is_compound_hint(type_arg));
|
||||
return (get_key_hint_bitmap(type_arg)->is_fixed() &&
|
||||
get_switch(type_arg));
|
||||
}
|
||||
@@ -756,9 +767,6 @@ 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.
|
||||
@@ -816,21 +824,20 @@ enum class hint_state
|
||||
|
||||
|
||||
/**
|
||||
Returns key hint value if hint is specified, returns
|
||||
optimizer switch value if hint is not specified.
|
||||
Returns key hint value if the hint is specified and resolved (fixed),
|
||||
returns `fallback_value` otherwise
|
||||
|
||||
@param thd Pointer to THD object
|
||||
@param tab Pointer to TABLE object
|
||||
@param keyno Key number
|
||||
@param type_arg Hint type
|
||||
@param optimizer_switch Optimizer switch flag
|
||||
@param fallback_value Value to be returned if the hint is not fixed
|
||||
|
||||
@return key hint value if hint is specified,
|
||||
otherwise optimizer switch value.
|
||||
*/
|
||||
bool hint_key_state(const THD *thd, const TABLE *table,
|
||||
uint keyno, opt_hints_enum type_arg,
|
||||
uint optimizer_switch);
|
||||
bool hint_key_state(const THD *thd, const TABLE *table, uint keyno,
|
||||
opt_hints_enum type_arg, bool fallback_value);
|
||||
|
||||
/**
|
||||
Returns table hint value if hint is specified, returns
|
||||
@@ -848,13 +855,13 @@ bool hint_table_state(const THD *thd, const TABLE_LIST *table_list,
|
||||
opt_hints_enum type_arg, bool fallback_value);
|
||||
|
||||
/**
|
||||
Returns table hint value if hint is specified, returns
|
||||
fallback value if hint is not specified.
|
||||
Returns table hint value if the hint is specified,
|
||||
returns `fallback_value` if the hint is not specified.
|
||||
|
||||
@param thd Pointer to THD object
|
||||
@param tab Pointer to TABLE object
|
||||
@param type_arg Hint type
|
||||
@param fallback_value Value to be returned if the hint is not set
|
||||
@param fallback_value Value to be returned if the hint is not specified
|
||||
|
||||
@return table hint value if hint is specified,
|
||||
otherwise fallback value.
|
||||
|
||||
@@ -130,6 +130,11 @@ Optimizer_hint_tokenizer::find_keyword(const LEX_CSTRING &str)
|
||||
return TokenID::keyword_GROUP_INDEX;
|
||||
break;
|
||||
|
||||
case 12:
|
||||
if ("ROWID_FILTER"_Lex_ident_column.streq(str))
|
||||
return TokenID::keyword_ROWID_FILTER;
|
||||
break;
|
||||
|
||||
case 13:
|
||||
if ("NO_JOIN_INDEX"_Lex_ident_column.streq(str))
|
||||
return TokenID::keyword_NO_JOIN_INDEX;
|
||||
@@ -145,6 +150,8 @@ Optimizer_hint_tokenizer::find_keyword(const LEX_CSTRING &str)
|
||||
case 15:
|
||||
if ("MATERIALIZATION"_Lex_ident_column.streq(str))
|
||||
return TokenID::keyword_MATERIALIZATION;
|
||||
if ("NO_ROWID_FILTER"_Lex_ident_column.streq(str))
|
||||
return TokenID::keyword_NO_ROWID_FILTER;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
@@ -486,13 +493,14 @@ bool Parser::Table_level_hint::resolve(Parse_context *pc) const
|
||||
- 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
|
||||
- global: [NO_]INDEX, [NO_]ROWID_FILTER
|
||||
- non-global: [NO_]JOIN_INDEX, [NO_]GROUP_INDEX, [NO_]ORDER_INDEX
|
||||
- 4 types of hints:
|
||||
- 5 types of hints:
|
||||
- [NO_]JOIN_INDEX
|
||||
- [NO_]GROUP_INDEX
|
||||
- [NO_]ORDER_INDEX
|
||||
- [NO_]INDEX
|
||||
- [NO_]ROWID_FILTER
|
||||
|
||||
Conflict checking
|
||||
- A conflict happens if and only if
|
||||
@@ -567,6 +575,14 @@ bool Parser::Index_level_hint::resolve(Parse_context *pc) const
|
||||
hint_type= GROUP_INDEX_HINT_ENUM;
|
||||
hint_state= false;
|
||||
break;
|
||||
case TokenID::keyword_ROWID_FILTER:
|
||||
hint_type= ROWID_FILTER_HINT_ENUM;
|
||||
hint_state= true;
|
||||
break;
|
||||
case TokenID::keyword_NO_ROWID_FILTER:
|
||||
hint_type= ROWID_FILTER_HINT_ENUM;
|
||||
hint_state= false;
|
||||
break;
|
||||
default:
|
||||
DBUG_ASSERT(0);
|
||||
return true;
|
||||
@@ -585,9 +601,6 @@ bool Parser::Index_level_hint::resolve(Parse_context *pc) const
|
||||
if (!tab)
|
||||
return false;
|
||||
|
||||
const Lex_ident_sys key_conflict(
|
||||
STRING_WITH_LEN("another hint was already specified for this index"));
|
||||
|
||||
/*
|
||||
If no index names are given, this is a table level hint, for example:
|
||||
GROUP_INDEX(t1), NO_MRR(t2).
|
||||
|
||||
@@ -55,6 +55,7 @@ enum opt_hints_enum
|
||||
JOIN_INDEX_HINT_ENUM,
|
||||
GROUP_INDEX_HINT_ENUM,
|
||||
ORDER_INDEX_HINT_ENUM,
|
||||
ROWID_FILTER_HINT_ENUM,
|
||||
MAX_HINT_ENUM // This one must be the last in the list
|
||||
};
|
||||
|
||||
@@ -134,7 +135,9 @@ public:
|
||||
keyword_GROUP_INDEX,
|
||||
keyword_NO_GROUP_INDEX,
|
||||
keyword_ORDER_INDEX,
|
||||
keyword_NO_ORDER_INDEX
|
||||
keyword_NO_ORDER_INDEX,
|
||||
keyword_ROWID_FILTER,
|
||||
keyword_NO_ROWID_FILTER
|
||||
};
|
||||
|
||||
class Token: public Lex_cstring
|
||||
@@ -423,7 +426,9 @@ private:
|
||||
id == TokenID::keyword_ORDER_INDEX ||
|
||||
id == TokenID::keyword_NO_ORDER_INDEX ||
|
||||
id == TokenID::keyword_GROUP_INDEX ||
|
||||
id == TokenID::keyword_NO_GROUP_INDEX;
|
||||
id == TokenID::keyword_NO_GROUP_INDEX ||
|
||||
id == TokenID::keyword_ROWID_FILTER ||
|
||||
id == TokenID::keyword_NO_ROWID_FILTER;
|
||||
}
|
||||
};
|
||||
class Index_level_hint_type: public TokenChoice<Parser,
|
||||
|
||||
@@ -418,7 +418,8 @@ void push_index_cond(JOIN_TAB *tab, uint keyno)
|
||||
*/
|
||||
if ((tab->table->key_info[keyno].index_flags & HA_DO_INDEX_COND_PUSHDOWN) &&
|
||||
hint_key_state(tab->join->thd, tab->table, keyno, ICP_HINT_ENUM,
|
||||
OPTIMIZER_SWITCH_INDEX_COND_PUSHDOWN) &&
|
||||
optimizer_flag(tab->join->thd,
|
||||
OPTIMIZER_SWITCH_INDEX_COND_PUSHDOWN)) &&
|
||||
tab->join->thd->lex->sql_command != SQLCOM_UPDATE_MULTI &&
|
||||
tab->join->thd->lex->sql_command != SQLCOM_DELETE_MULTI &&
|
||||
tab->type != JT_CONST && tab->type != JT_SYSTEM &&
|
||||
|
||||
@@ -2848,7 +2848,7 @@ SQL_SELECT::test_quick_select(THD *thd,
|
||||
add("cause", "not applicable");
|
||||
continue;
|
||||
}
|
||||
if (hint_key_state(thd, head, idx, NO_RANGE_HINT_ENUM, 0))
|
||||
if (hint_key_state(thd, head, idx, NO_RANGE_HINT_ENUM, false))
|
||||
{
|
||||
trace_idx_details.
|
||||
add("usable", false).
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "optimizer_defaults.h"
|
||||
#include "sql_select.h"
|
||||
#include "opt_trace.h"
|
||||
#include "opt_hints.h"
|
||||
|
||||
/*
|
||||
key_next_find_cost below is the cost of finding the next possible key
|
||||
@@ -375,17 +376,16 @@ void TABLE::init_cost_info_for_usable_range_rowid_filters(THD *thd)
|
||||
|
||||
/*
|
||||
From all indexes that can be used for range accesses select only such that
|
||||
- range filter pushdown is supported by the engine for them (1)
|
||||
- they are not clustered primary (2)
|
||||
- the range filter containers for them are not too large (3)
|
||||
- can be used as rowid filters (1)
|
||||
- the range filter containers for them are not too large (2)
|
||||
*/
|
||||
while ((key_no= it++) != key_map::Iterator::BITMAP_END)
|
||||
{
|
||||
if (!can_use_rowid_filter(key_no)) // 1 & 2
|
||||
if (!key_can_be_used_as_rowid_filter(thd, key_no)) // !1
|
||||
continue;
|
||||
if (opt_range[key_no].rows >
|
||||
get_max_range_rowid_filter_elems_for_table(thd, this,
|
||||
SORTED_ARRAY_CONTAINER)) // !3
|
||||
SORTED_ARRAY_CONTAINER)) // !2
|
||||
continue;
|
||||
usable_range_filter_keys.set_bit(key_no);
|
||||
}
|
||||
@@ -421,6 +421,8 @@ void TABLE::init_cost_info_for_usable_range_rowid_filters(THD *thd)
|
||||
{
|
||||
*curr_ptr= curr_filter_cost_info;
|
||||
curr_filter_cost_info->init(SORTED_ARRAY_CONTAINER, this, key_no);
|
||||
curr_filter_cost_info->is_forced_by_hint=
|
||||
hint_key_state(thd, this, key_no, ROWID_FILTER_HINT_ENUM, false);
|
||||
curr_ptr++;
|
||||
curr_filter_cost_info++;
|
||||
}
|
||||
@@ -432,6 +434,39 @@ void TABLE::init_cost_info_for_usable_range_rowid_filters(THD *thd)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Return true if this `index` can be used as a rowid filter:
|
||||
- filter pushdown is supported by the engine for the index. If this is set
|
||||
then file->ha_table_flags() should not contain HA_NON_COMPARABLE_ROWID
|
||||
- The index is not a clustered index
|
||||
- optimizer hints ROWID_FILTER/NO_ROWID_FILTER do not forbid the use
|
||||
*/
|
||||
|
||||
bool TABLE::key_can_be_used_as_rowid_filter(THD *thd, uint index) const
|
||||
{
|
||||
return (key_info[index].index_flags &
|
||||
(HA_DO_RANGE_FILTER_PUSHDOWN | HA_CLUSTERED_INDEX)) ==
|
||||
HA_DO_RANGE_FILTER_PUSHDOWN &&
|
||||
hint_key_state(thd, this, index, ROWID_FILTER_HINT_ENUM,
|
||||
optimizer_flag(thd, OPTIMIZER_SWITCH_USE_ROWID_FILTER));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Return true if a rowid filter can be applied to this `index`:
|
||||
- filter pushdown is supported by the engine for the index. If this is set
|
||||
then file->ha_table_flags() should not contain HA_NON_COMPARABLE_ROWID
|
||||
- The index is not a clustered index
|
||||
*/
|
||||
|
||||
bool TABLE::rowid_filter_can_be_applied_to_key(uint index) const
|
||||
{
|
||||
return (key_info[index].index_flags &
|
||||
(HA_DO_RANGE_FILTER_PUSHDOWN | HA_CLUSTERED_INDEX)) ==
|
||||
HA_DO_RANGE_FILTER_PUSHDOWN;
|
||||
}
|
||||
|
||||
|
||||
void TABLE::trace_range_rowid_filters(THD *thd) const
|
||||
{
|
||||
DBUG_ASSERT(thd->trace_started());
|
||||
@@ -512,6 +547,7 @@ TABLE::best_range_rowid_filter(uint access_key_no, double records,
|
||||
|
||||
Range_rowid_filter_cost_info *best_filter= 0;
|
||||
double best_filter_gain= DBL_MAX;
|
||||
bool is_forced_filter_applied= false;
|
||||
|
||||
key_map no_filter_usage= key_info[access_key_no].overlapped;
|
||||
no_filter_usage.merge(key_info[access_key_no].constraint_correlated);
|
||||
@@ -539,10 +575,23 @@ TABLE::best_range_rowid_filter(uint access_key_no, double records,
|
||||
in_use->variables.optimizer_where_cost) *
|
||||
prev_records + filter->get_setup_cost());
|
||||
|
||||
if (best_filter_gain > new_total_cost)
|
||||
if (is_forced_filter_applied)
|
||||
{
|
||||
/*
|
||||
Only other forced filters can overwrite best_filter previously set
|
||||
by a forced filter
|
||||
*/
|
||||
if (filter->is_forced_by_hint && new_total_cost < best_filter_gain)
|
||||
{
|
||||
best_filter_gain= new_total_cost;
|
||||
best_filter= filter;
|
||||
}
|
||||
}
|
||||
else if (new_total_cost < best_filter_gain || filter->is_forced_by_hint)
|
||||
{
|
||||
best_filter_gain= new_total_cost;
|
||||
best_filter= filter;
|
||||
is_forced_filter_applied= filter->is_forced_by_hint;
|
||||
}
|
||||
}
|
||||
return best_filter;
|
||||
|
||||
@@ -424,6 +424,8 @@ public:
|
||||
double selectivity;
|
||||
/* The type of the container of the range filter */
|
||||
Rowid_filter_container_type container_type;
|
||||
/* This flag is set if the filter is forced by ROWID_FILTER() hint */
|
||||
bool is_forced_by_hint;
|
||||
|
||||
Range_rowid_filter_cost_info() : table(0), key_no(0) {}
|
||||
|
||||
|
||||
@@ -6217,8 +6217,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list,
|
||||
select->quick=0;
|
||||
s->needed_reg=select->needed_reg;
|
||||
impossible_range= records == 0 && s->table->reginfo.impossible_range;
|
||||
if (optimizer_flag(join->thd, OPTIMIZER_SWITCH_USE_ROWID_FILTER))
|
||||
s->table->init_cost_info_for_usable_range_rowid_filters(join->thd);
|
||||
s->table->init_cost_info_for_usable_range_rowid_filters(join->thd);
|
||||
}
|
||||
if (!impossible_range)
|
||||
{
|
||||
@@ -8366,7 +8365,8 @@ static ALL_READ_COST cost_for_index_read(const THD *thd, const TABLE *table,
|
||||
|
||||
|
||||
/**
|
||||
Apply filter if the filter is better than the current cost
|
||||
Apply filter if the filter is better than the current cost or
|
||||
if it forced by ROWID_FILTER hint
|
||||
|
||||
@param thd Thread handler
|
||||
@param table Table
|
||||
@@ -8440,7 +8440,7 @@ apply_filter(THD *thd, TABLE *table, ALL_READ_COST *cost,
|
||||
new_records * tmp + filter_startup_cost);
|
||||
|
||||
DBUG_ASSERT(new_cost >= 0 && new_records >= 0);
|
||||
use_filter= new_cost < org_cost;
|
||||
use_filter= new_cost < org_cost || is_forced_by_hint;
|
||||
|
||||
if (unlikely(thd->trace_started()))
|
||||
{
|
||||
@@ -9347,7 +9347,7 @@ best_access_path(JOIN *join,
|
||||
Records can be 0 in case of empty tables.
|
||||
*/
|
||||
if ((found_part & 1) && records &&
|
||||
table->can_use_rowid_filter(start_key->key))
|
||||
table->rowid_filter_can_be_applied_to_key(start_key->key))
|
||||
{
|
||||
/*
|
||||
If we use filter F with selectivity s the cost of fetching data
|
||||
@@ -9740,7 +9740,7 @@ best_access_path(JOIN *join,
|
||||
range->cost.setup_cost,
|
||||
s->quick->read_time));
|
||||
|
||||
if (table->can_use_rowid_filter(key_no))
|
||||
if (table->rowid_filter_can_be_applied_to_key(key_no))
|
||||
{
|
||||
filter= table->best_range_rowid_filter(key_no,
|
||||
rows2double(range->rows),
|
||||
|
||||
@@ -9002,7 +9002,8 @@ bool TABLE_LIST::process_index_hints(TABLE *tbl)
|
||||
{
|
||||
/* initialize the result variables */
|
||||
tbl->keys_in_use_for_query= tbl->keys_in_use_for_group_by=
|
||||
tbl->keys_in_use_for_order_by= tbl->s->usable_indexes(tbl->in_use);
|
||||
tbl->keys_in_use_for_order_by= tbl->keys_in_use_for_rowid_filter=
|
||||
tbl->s->usable_indexes(tbl->in_use);
|
||||
|
||||
/* index hint list processing */
|
||||
if (index_hints)
|
||||
@@ -9127,6 +9128,9 @@ bool TABLE_LIST::process_index_hints(TABLE *tbl)
|
||||
tbl->keys_in_use_for_group_by.subtract (index_group[INDEX_HINT_IGNORE]);
|
||||
}
|
||||
|
||||
// Keys for building ROWID filters are the same as for retrieving data
|
||||
tbl->keys_in_use_for_rowid_filter= tbl->keys_in_use_for_query;
|
||||
|
||||
/* make sure covering_keys don't include indexes disabled with a hint */
|
||||
tbl->covering_keys.intersect(tbl->keys_in_use_for_query);
|
||||
return 0;
|
||||
|
||||
17
sql/table.h
17
sql/table.h
@@ -1377,6 +1377,8 @@ public:
|
||||
key_map keys_in_use_for_group_by;
|
||||
/* Map of keys that can be used to calculate ORDER BY without sorting */
|
||||
key_map keys_in_use_for_order_by;
|
||||
/* Map of keys that can be used to build ROWID filters */
|
||||
key_map keys_in_use_for_rowid_filter;
|
||||
/* Map of keys dependent on some constraint */
|
||||
key_map constraint_dependent_keys;
|
||||
KEY *key_info; /* data of keys in database */
|
||||
@@ -1979,20 +1981,9 @@ public:
|
||||
return key_info[index].index_flags & HA_CLUSTERED_INDEX;
|
||||
}
|
||||
|
||||
/*
|
||||
Return true if we can use rowid filter with this index
|
||||
rowid filter can be used if
|
||||
- filter pushdown is supported by the engine for the index. If this is set then
|
||||
file->ha_table_flags() should not contain HA_NON_COMPARABLE_ROWID!
|
||||
- The index is not a clustered primary index
|
||||
*/
|
||||
bool rowid_filter_can_be_applied_to_key(uint index) const;
|
||||
|
||||
inline bool can_use_rowid_filter(uint index) const
|
||||
{
|
||||
return ((key_info[index].index_flags &
|
||||
(HA_DO_RANGE_FILTER_PUSHDOWN | HA_CLUSTERED_INDEX)) ==
|
||||
HA_DO_RANGE_FILTER_PUSHDOWN);
|
||||
}
|
||||
bool key_can_be_used_as_rowid_filter(THD *thd, uint index) const;
|
||||
|
||||
ulonglong vers_start_id(const uchar *ptr) const;
|
||||
ulonglong vers_end_id(const uchar *ptr) const;
|
||||
|
||||
Reference in New Issue
Block a user