1
0
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:
Oleg Smirnov
2025-05-23 22:08:31 +07:00
committed by Oleg Smirnov
parent 3b5901a1f3
commit 8e9213da94
14 changed files with 698 additions and 100 deletions

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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