Skip to content

Commit 8e9213d

Browse files
Olernovmariadb-OlegSmirnov
authored andcommitted
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 3b5901a commit 8e9213d

14 files changed

+698
-100
lines changed

mysql-test/main/opt_hint_rowid_filter.result

Lines changed: 289 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
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 # Filter use is not beneficial for this query, so there is just 'range' access
62+
EXPLAIN EXTENDED
63+
SELECT a FROM t1 WHERE c < 'z' AND b > 't';
64+
65+
--echo # Force the use of the rowid filter anyway:
66+
EXPLAIN EXTENDED
67+
SELECT /*+ rowid_filter(t1 key_c)*/ a FROM t1 WHERE c < 'z' AND b > 't';
68+
69+
70+
--echo # Test the hint with query blocks
71+
set optimizer_switch='derived_merge=off';
72+
73+
EXPLAIN EXTENDED
74+
SELECT /*+ no_rowid_filter(t1@qb1)*/ * FROM (
75+
SELECT /*+ qb_name(qb1)*/ a FROM t1 WHERE c < 'e' AND b > 't') dt;
76+
77+
--echo # `key_b|key_c` access is forced
78+
EXPLAIN EXTENDED
79+
SELECT /*+ /*+ rowid_filter(t1@qb1 key_c)*/ * FROM (
80+
SELECT /*+ qb_name(qb1)*/ a FROM t1 WHERE c < 'e' AND b > 't') dt;
81+
82+
set optimizer_switch=default;
83+
84+
--echo # Turn rowid filtering off and enable it selectively
85+
set optimizer_switch='rowid_filter=off';
86+
87+
--echo # Make sure rowid filter is not used when there are no hints:
88+
EXPLAIN EXTENDED
89+
SELECT a FROM t1 WHERE c < 'e' AND b > 't' and d < 'e';
90+
91+
--echo # Enable rowid filter for t1
92+
EXPLAIN EXTENDED
93+
SELECT /*+ rowid_filter(t1)*/ a FROM t1 WHERE c < 'e' AND b > 't' and d < 'e';
94+
95+
--echo # Enable rowid filter only for some indexes of t1
96+
EXPLAIN EXTENDED
97+
SELECT /*+ rowid_filter(t1 key_a, key_c)*/ a FROM t1
98+
WHERE c < 'e' AND b > 't' and d < 'e';
99+
100+
EXPLAIN EXTENDED
101+
SELECT /*+ rowid_filter(t1 key_c)*/ a FROM t1
102+
WHERE c < 'e' AND b > 't' and d < 'e';
103+
104+
--echo # Enable rowid filter for the same index that is used to access data (impossible)
105+
EXPLAIN EXTENDED
106+
SELECT /*+ rowid_filter(t1 key_d)*/ a FROM t1 WHERE c < 'e' AND b > 't' and d < 'e';
107+
108+
--echo # Conflicting hints
109+
EXPLAIN EXTENDED
110+
SELECT /*+ no_rowid_filter(t1) ROWID_FILTER(t1 key_a)*/a FROM t1
111+
WHERE c < 'e' AND b > 't';
112+
113+
EXPLAIN EXTENDED
114+
SELECT /*+ no_rowid_filter(t1 key_a, key_b) no_rowid_filter(t1 key_c, key_b)*/a FROM t1
115+
WHERE c < 'e' AND b > 't' and d < 'e';
116+
117+
EXPLAIN EXTENDED
118+
SELECT /*+ no_rowid_filter(t1 key_a,key_b) rowid_filter(t1 key_c)*/a FROM t1
119+
WHERE c < 'e' AND b > 't' and d < 'e';
120+
121+
# Warnings "Unresolved table/index name..." are generated during both prepare
122+
# and execution stages. So disable PS protocol to avoid duplication
123+
--disable_ps_protocol
124+
--echo # Wrong table name
125+
EXPLAIN EXTENDED
126+
SELECT /*+ no_rowid_filter(missing_table)*/a FROM t1
127+
WHERE c < 'e' AND b > 't' and d < 'e';
128+
129+
--echo # Some index names are wrong but key_c is applicable
130+
EXPLAIN EXTENDED
131+
SELECT /*+ rowid_filter(t1 wrong_key1, key_c, wrong_key2)*/ a FROM t1
132+
WHERE c < 'e' AND b > 't' and d < 'e';
133+
--enable_ps_protocol
134+
135+
--echo # Test cases mixing with index hints.
136+
--echo # Keys must be whitelisted in INDEX()/JOIN_INDEX() hint to be
137+
--echo # applicable as a rowid filter. ROWID_FILTER() hint cannot enable keys
138+
--echo # not enabled by INDEX()/JOIN_INDEX() hints or disabled by
139+
--echo # NO_INDEX()/NO_JOIN_INDEX() hints
140+
EXPLAIN EXTENDED
141+
SELECT /*+ index(t1 key_b,key_c) rowid_filter(t1 key_c)*/ a FROM t1
142+
WHERE c < 'e' AND b > 't' and d < 'e';
143+
144+
EXPLAIN EXTENDED
145+
SELECT /*+ join_index(t1 key_b,key_c) rowid_filter(t1 key_c)*/ a FROM t1
146+
WHERE c < 'e' AND b > 't' and d < 'e';
147+
148+
EXPLAIN EXTENDED
149+
SELECT /*+ index(t1 key_d,key_c) rowid_filter(t1 key_c)*/ a FROM t1
150+
WHERE c < 'e' AND b > 't' and d < 'e';
151+
152+
EXPLAIN EXTENDED
153+
SELECT /*+ no_index(t1) rowid_filter(t1 key_c)*/ a FROM t1
154+
WHERE c < 'e' AND b > 't' and d < 'e';
155+
156+
EXPLAIN EXTENDED
157+
SELECT /*+ no_index(t1 key_d) rowid_filter(t1 key_c)*/ a FROM t1
158+
WHERE c < 'e' AND b > 't' and d < 'e';
159+
160+
EXPLAIN EXTENDED
161+
SELECT /*+ no_index(t1 key_c) rowid_filter(t1 key_c)*/ a FROM t1
162+
WHERE c < 'e' AND b > 't' and d < 'e';
163+
164+
EXPLAIN EXTENDED
165+
SELECT /*+ index(t1) no_rowid_filter(t1 key_c)*/ a FROM t1
166+
WHERE c < 'e' AND b > 't' and d < 'e';
167+
168+
--echo # Test cases for old-style hints
169+
EXPLAIN EXTENDED
170+
SELECT a FROM t1 FORCE INDEX(key_d, key_b)
171+
WHERE c < 'e' AND b > 't' and d < 'e';
172+
173+
--echo # `key_c` is not listed in the hint so is not applicable as rowid filter
174+
--echo # (also see comment for new-style index hints above)
175+
EXPLAIN EXTENDED
176+
SELECT a FROM t1 FORCE INDEX(key_d)
177+
WHERE c < 'e' AND b > 't' and d < 'e';
178+
179+
--echo # `key_c` is blacklisted, so `key_b` is used for rowid filter
180+
EXPLAIN EXTENDED
181+
SELECT a FROM t1 IGNORE INDEX(key_c)
182+
WHERE c < 'e' AND b > 't' and d < 'e';
183+
184+
--echo # `key_b` is blacklisted, so `key_c` is used for rowid filter
185+
EXPLAIN EXTENDED
186+
SELECT a FROM t1 IGNORE INDEX(key_b)
187+
WHERE c < 'e' AND b > 't' and d < 'e';
188+
189+
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)