Skip to content

Commit ce6cb86

Browse files
committed
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 ....
1 parent e02f4d7 commit ce6cb86

14 files changed

+669
-91
lines changed

mysql-test/main/opt_hint_rowid_filter.result

Lines changed: 275 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
--enable_prepare_warnings
2+
--disable_view_protocol # Since optimizer hints are not supported inside views
3+
4+
CREATE TABLE t1 (a INT, b VARCHAR(10), c VARCHAR(100), d VARCHAR(200),
5+
KEY key_a(a), KEY key_b(b), KEY key_c(c), KEY key_d(d));
6+
7+
INSERT INTO t1 VALUES
8+
(1,'w','z','F'), (1,'X','o','s'), (1,'q','c','a'), (5,'w','c','d'), (2,'j','m','k'),
9+
(2,'Q','s','q'), (9,'e','J','B'), (2,'p','W','l'), (9,'o','F','Y'), (2,'g','S','M'),
10+
(1,'Y','a','h'), (NULL,'Y','p','J'), (NULL,'s','x','M'), (NULL,'i','S','k'),
11+
(1,'l','q','w'), (7,'r','e','_'), (4,'b','h','I'), (NULL,'E','c','z'),
12+
(NULL,'M','a','j'), (3,'e','X','K'), (NULL,'p','r','R'), (9,'e','i','g'),
13+
(3,'g','x','m'), (2,'h','y','p');
14+
15+
ANALYZE TABLE t1;
16+
17+
set optimizer_switch='rowid_filter=on';
18+
--echo # range|filter `key_d|key_b` is applied by default when there are no hints:
19+
EXPLAIN EXTENDED
20+
SELECT a FROM t1 WHERE c < 'e' AND b > 't' and d < 'e';
21+
22+
--echo # Sample result set to compare against later
23+
SELECT a FROM t1 WHERE c < 'e' AND b > 't' and d < 'e';
24+
25+
--echo # Disable rowid filter for t1
26+
EXPLAIN EXTENDED
27+
SELECT /*+ no_rowid_filter(t1)*/ a FROM t1 WHERE c < 'e' AND b > 't';
28+
29+
--echo # Force using rowid filter made from `key_c` despite being less
30+
--echo # efficient then the one made from `key_b`
31+
EXPLAIN EXTENDED
32+
SELECT /*+ rowid_filter(t1 key_c)*/ a FROM t1 WHERE c < 'e' AND b > 't' and d < 'e';
33+
34+
--echo # Allow the optimizer to choose the best rowid filter from key_a,key_b,key_c,key_d
35+
EXPLAIN EXTENDED
36+
SELECT /*+ rowid_filter(t1 key_a,key_b,key_c,key_d)*/ a FROM t1
37+
WHERE c < 'e' AND b > 't' and d < 'e';
38+
39+
--echo # Forbid `key_b` for rowid filtering, so `key_c` is used
40+
let $q= SELECT /*+ no_rowid_filter(t1 key_b)*/ a FROM t1
41+
WHERE c < 'e' AND b > 't' and d < 'e';
42+
43+
eval EXPLAIN EXTENDED $q;
44+
--echo # Validate the result set
45+
eval $q;
46+
47+
--echo # Forbid both `key_b` and `key_c`, so no rowid filter can be applied
48+
EXPLAIN EXTENDED
49+
SELECT /*+ no_rowid_filter(t1 key_b, key_c)*/ a FROM t1
50+
WHERE c < 'e' AND b > 't' and d < 'e';
51+
52+
--echo # Disable rowid filter for indexes which would not be used anyway
53+
EXPLAIN EXTENDED
54+
SELECT /*+ no_rowid_filter(t1 key_a)*/ a FROM t1
55+
WHERE c < 'e' AND b > 't' and d < 'e';
56+
57+
EXPLAIN EXTENDED
58+
SELECT /*+ no_rowid_filter(t1 key_a,key_b)*/ a FROM t1
59+
WHERE c < 'e' AND b > 't' and d < 'e';
60+
61+
--echo # Test the hint with query blocks
62+
set optimizer_switch='derived_merge=off';
63+
64+
EXPLAIN EXTENDED
65+
SELECT /*+ no_rowid_filter(t1@qb1)*/ * FROM (
66+
SELECT /*+ qb_name(qb1)*/ a FROM t1 WHERE c < 'e' AND b > 't') dt;
67+
68+
--echo # `key_b|key_c` access is forced
69+
EXPLAIN EXTENDED
70+
SELECT /*+ /*+ rowid_filter(t1@qb1 key_c)*/ * FROM (
71+
SELECT /*+ qb_name(qb1)*/ a FROM t1 WHERE c < 'e' AND b > 't') dt;
72+
73+
set optimizer_switch=default;
74+
75+
--echo # Turn rowid filtering off and enable it selectively
76+
set optimizer_switch='rowid_filter=off';
77+
78+
--echo # Make sure rowid filter is not used when there are no hints:
79+
EXPLAIN EXTENDED
80+
SELECT a FROM t1 WHERE c < 'e' AND b > 't' and d < 'e';
81+
82+
--echo # Enable rowid filter for t1
83+
EXPLAIN EXTENDED
84+
SELECT /*+ rowid_filter(t1)*/ a FROM t1 WHERE c < 'e' AND b > 't' and d < 'e';
85+
86+
--echo # Enable rowid filter only for some indexes of t1
87+
EXPLAIN EXTENDED
88+
SELECT /*+ rowid_filter(t1 key_a, key_c)*/ a FROM t1
89+
WHERE c < 'e' AND b > 't' and d < 'e';
90+
91+
EXPLAIN EXTENDED
92+
SELECT /*+ rowid_filter(t1 key_c)*/ a FROM t1
93+
WHERE c < 'e' AND b > 't' and d < 'e';
94+
95+
--echo # Enable rowid filter for the same index that is used to access data (impossible)
96+
EXPLAIN EXTENDED
97+
SELECT /*+ rowid_filter(t1 key_d)*/ a FROM t1 WHERE c < 'e' AND b > 't' and d < 'e';
98+
99+
--echo # Conflicting hints
100+
EXPLAIN EXTENDED
101+
SELECT /*+ no_rowid_filter(t1) ROWID_FILTER(t1 key_a)*/a FROM t1
102+
WHERE c < 'e' AND b > 't';
103+
104+
EXPLAIN EXTENDED
105+
SELECT /*+ no_rowid_filter(t1 key_a, key_b) no_rowid_filter(t1 key_c, key_b)*/a FROM t1
106+
WHERE c < 'e' AND b > 't' and d < 'e';
107+
108+
EXPLAIN EXTENDED
109+
SELECT /*+ no_rowid_filter(t1 key_a,key_b) rowid_filter(t1 key_c)*/a FROM t1
110+
WHERE c < 'e' AND b > 't' and d < 'e';
111+
112+
# Warnings "Unresolved table/index name..." are generated during both prepare
113+
# and execution stages. So disable PS protocol to avoid duplication
114+
--disable_ps_protocol
115+
--echo # Wrong table name
116+
EXPLAIN EXTENDED
117+
SELECT /*+ no_rowid_filter(missing_table)*/a FROM t1
118+
WHERE c < 'e' AND b > 't' and d < 'e';
119+
120+
--echo # Some index names are wrong but key_c is applicable
121+
EXPLAIN EXTENDED
122+
SELECT /*+ rowid_filter(t1 wrong_key1, key_c, wrong_key2)*/ a FROM t1
123+
WHERE c < 'e' AND b > 't' and d < 'e';
124+
--enable_ps_protocol
125+
126+
--echo # Test cases mixing with index hints.
127+
--echo # Keys must be whitelisted in INDEX()/JOIN_INDEX() hint to be
128+
--echo # applicable as a rowid filter. ROWID_FILTER() hint cannot enable keys
129+
--echo # not enabled by INDEX()/JOIN_INDEX() hints or disabled by
130+
--echo # NO_INDEX()/NO_JOIN_INDEX() hints
131+
EXPLAIN EXTENDED
132+
SELECT /*+ index(t1 key_b,key_c) rowid_filter(t1 key_c)*/ a FROM t1
133+
WHERE c < 'e' AND b > 't' and d < 'e';
134+
135+
EXPLAIN EXTENDED
136+
SELECT /*+ join_index(t1 key_b,key_c) rowid_filter(t1 key_c)*/ a FROM t1
137+
WHERE c < 'e' AND b > 't' and d < 'e';
138+
139+
EXPLAIN EXTENDED
140+
SELECT /*+ index(t1 key_d,key_c) rowid_filter(t1 key_c)*/ a FROM t1
141+
WHERE c < 'e' AND b > 't' and d < 'e';
142+
143+
EXPLAIN EXTENDED
144+
SELECT /*+ no_index(t1) rowid_filter(t1 key_c)*/ a FROM t1
145+
WHERE c < 'e' AND b > 't' and d < 'e';
146+
147+
EXPLAIN EXTENDED
148+
SELECT /*+ no_index(t1 key_d) rowid_filter(t1 key_c)*/ a FROM t1
149+
WHERE c < 'e' AND b > 't' and d < 'e';
150+
151+
EXPLAIN EXTENDED
152+
SELECT /*+ no_index(t1 key_c) rowid_filter(t1 key_c)*/ a FROM t1
153+
WHERE c < 'e' AND b > 't' and d < 'e';
154+
155+
EXPLAIN EXTENDED
156+
SELECT /*+ index(t1) no_rowid_filter(t1 key_c)*/ a FROM t1
157+
WHERE c < 'e' AND b > 't' and d < 'e';
158+
159+
--echo # Test cases for old-style hints
160+
EXPLAIN EXTENDED
161+
SELECT a FROM t1 FORCE INDEX(key_d, key_b)
162+
WHERE c < 'e' AND b > 't' and d < 'e';
163+
164+
--echo # `key_c` is not listed in the hint so is not applicable as rowid filter
165+
--echo # (also see comment for new-style index hints above)
166+
EXPLAIN EXTENDED
167+
SELECT a FROM t1 FORCE INDEX(key_d)
168+
WHERE c < 'e' AND b > 't' and d < 'e';
169+
170+
--echo # `key_c` is blacklisted, so `key_b` is used for rowid filter
171+
EXPLAIN EXTENDED
172+
SELECT a FROM t1 IGNORE INDEX(key_c)
173+
WHERE c < 'e' AND b > 't' and d < 'e';
174+
175+
--echo # `key_b` is blacklisted, so `key_c` is used for rowid filter
176+
EXPLAIN EXTENDED
177+
SELECT a FROM t1 IGNORE INDEX(key_b)
178+
WHERE c < 'e' AND b > 't' and d < 'e';
179+
180+
DROP TABLE t1;

sql/multi_range_read.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1150,7 +1150,7 @@ int DsMrr_impl::dsmrr_init(handler *h_arg, RANGE_SEQ_IF *seq_funcs,
11501150
buf_manager.redistribute_buffer_space= do_nothing;
11511151

11521152
if (!hint_key_state(thd, table, h_arg->active_index,
1153-
MRR_HINT_ENUM, OPTIMIZER_SWITCH_MRR) ||
1153+
MRR_HINT_ENUM, optimizer_flag(thd, OPTIMIZER_SWITCH_MRR)) ||
11541154
mode & (HA_MRR_USE_DEFAULT_IMPL | HA_MRR_SORTED))
11551155
goto use_default_impl;
11561156

@@ -1905,9 +1905,9 @@ bool DsMrr_impl::choose_mrr_impl(uint keyno, ha_rows rows, uint *flags,
19051905
TABLE_SHARE *share= primary_file->get_table_share();
19061906

19071907
const bool mrr_on= hint_key_state(thd, table, keyno, MRR_HINT_ENUM,
1908-
OPTIMIZER_SWITCH_MRR);
1908+
optimizer_flag(thd, OPTIMIZER_SWITCH_MRR));
19091909
const bool force_dsmrr_by_hints=
1910-
hint_key_state(thd, table, keyno, MRR_HINT_ENUM, 0) ||
1910+
hint_key_state(thd, table, keyno, MRR_HINT_ENUM, false) ||
19111911
hint_table_state(thd, table, BKA_HINT_ENUM, false);
19121912

19131913
bool doing_cpk_scan= check_cpk_scan(thd, share, keyno, *flags);

0 commit comments

Comments
 (0)