MDEV-12255 innodb_prefix_index_cluster_optimization hits debug build
assert on UTF-8 columns
Problem:
=======
(1) Multi-byte character cases are not considered during prefix index
cluster optimization check. It leads to fetch of improper results during
read operation.
(2) Strict assert in row_sel_field_store_in_mysql_format_func and it asserts
for prefix index record to mysql conversion.
Solution:
========
(1) Consider the case of multi-byte character during prefix index
cluster optimization check.
(2) Relax the assert in row_sel_field_store_in_mysql_format_func to allow
prefix index record to mysql format conversion.
The patch is taken from
1eee538087
This commit is contained in:
parent
bc2e7d7889
commit
eee73ddfbb
@ -30,73 +30,372 @@ id fake_id bigfield
|
||||
33 1033 yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
128 1128 zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
|
||||
# Baseline sanity check: 0, 0.
|
||||
select "no-op query";
|
||||
no-op query
|
||||
no-op query
|
||||
cluster_lookups_matched
|
||||
1
|
||||
cluster_lookups_avoided_matched
|
||||
1
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
0
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
0
|
||||
# Eligible for optimization.
|
||||
select id, bigfield from prefixinno where bigfield = repeat('d', 31);
|
||||
id bigfield
|
||||
31 ddddddddddddddddddddddddddddddd
|
||||
cluster_lookups_matched
|
||||
1
|
||||
cluster_lookups_avoided_matched
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
0
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
1
|
||||
# Eligible for optimization, access via fake_id only.
|
||||
select id, bigfield from prefixinno where fake_id = 1031;
|
||||
id bigfield
|
||||
31 ddddddddddddddddddddddddddddddd
|
||||
cluster_lookups_matched
|
||||
1
|
||||
cluster_lookups_avoided_matched
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
0
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
1
|
||||
# Not eligible for optimization, access via fake_id of big row.
|
||||
select id, bigfield from prefixinno where fake_id = 1033;
|
||||
id bigfield
|
||||
33 yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
cluster_lookups_matched
|
||||
1
|
||||
cluster_lookups_avoided_matched
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
1
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
0
|
||||
# Not eligible for optimization.
|
||||
select id, bigfield from prefixinno where bigfield = repeat('x', 32);
|
||||
id bigfield
|
||||
32 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
cluster_lookups_matched
|
||||
1
|
||||
cluster_lookups_avoided_matched
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
1
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
0
|
||||
# Not eligible for optimization.
|
||||
select id, bigfield from prefixinno where bigfield = repeat('y', 33);
|
||||
id bigfield
|
||||
33 yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
cluster_lookups_matched
|
||||
1
|
||||
cluster_lookups_avoided_matched
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
1
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
0
|
||||
# Eligible, should not increment lookup counter.
|
||||
select id, bigfield from prefixinno where bigfield = repeat('b', 8);
|
||||
id bigfield
|
||||
8 bbbbbbbb
|
||||
cluster_lookups_matched
|
||||
1
|
||||
cluster_lookups_avoided_matched
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
0
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
1
|
||||
# Eligible, should not increment lookup counter.
|
||||
select id, bigfield from prefixinno where bigfield = repeat('c', 24);
|
||||
id bigfield
|
||||
24 cccccccccccccccccccccccc
|
||||
cluster_lookups_matched
|
||||
1
|
||||
cluster_lookups_avoided_matched
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
0
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
1
|
||||
# Should increment lookup counter.
|
||||
select id, bigfield from prefixinno where bigfield = repeat('z', 128);
|
||||
id bigfield
|
||||
128 zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
|
||||
cluster_lookups_matched
|
||||
1
|
||||
cluster_lookups_avoided_matched
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
1
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
0
|
||||
# Disable optimization, confirm we still increment counter.
|
||||
set global innodb_prefix_index_cluster_optimization = OFF;
|
||||
select id, bigfield from prefixinno where fake_id = 1033;
|
||||
id bigfield
|
||||
33 yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
|
||||
cluster_lookups_matched
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
1
|
||||
cluster_lookups_avoided_matched
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
0
|
||||
drop table prefixinno;
|
||||
# Multi-byte handling case
|
||||
set global innodb_prefix_index_cluster_optimization = ON;
|
||||
SET NAMES utf8mb4;
|
||||
CREATE TABLE t1(
|
||||
f1 varchar(10) CHARACTER SET UTF8MB4 COLLATE UTF8MB4_BIN,
|
||||
INDEX (f1(3)))ENGINE=INNODB;
|
||||
INSERT INTO t1 VALUES('a'), ('cccc'), ('až'), ('cčc'), ('ggᵷg'), ('¢¢');
|
||||
INSERT INTO t1 VALUES('தமிழ்'), ('🐱🌑'), ('🌒'), ('🌑');
|
||||
INSERT INTO t1 VALUES('😊me'), ('eu€'), ('ls¢');
|
||||
# Eligible - record length is shorter than prefix
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 = 'a';
|
||||
f1
|
||||
a
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
0
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
1
|
||||
# make test suite happy by cleaning up our mess
|
||||
# Not eligible - record length longer than prefix length
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 like 'c%';
|
||||
f1
|
||||
cccc
|
||||
cčc
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
3
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
0
|
||||
# Eligible - record length shorter than prefix length
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 = 'až';
|
||||
f1
|
||||
až
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
0
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
1
|
||||
# Not eligible - record length longer than prefix length
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 = 'தமிழ்';
|
||||
f1
|
||||
தமிழ்
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
1
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
0
|
||||
# Not eligible - record length longer than prefix length
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 like 'ggᵷ%';
|
||||
f1
|
||||
ggᵷg
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
1
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
0
|
||||
# Not eligible - record length longer than prefix length
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 like '😊%';
|
||||
f1
|
||||
😊me
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
1
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
0
|
||||
# Not eligible - record length longer than prefix length
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 = 'ls¢';
|
||||
f1
|
||||
ls¢
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
1
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
0
|
||||
# Eligible - record length shorter than prefix length
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 like '¢¢%';
|
||||
f1
|
||||
¢¢
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
1
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
1
|
||||
# Eligible - record length shorter than prefix length
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 like '🐱🌑%';
|
||||
f1
|
||||
🐱🌑
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
1
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
1
|
||||
# Not eligible - record length longer than prefix length
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 like '🌑%';
|
||||
f1
|
||||
🌑
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
0
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
2
|
||||
# Not eligible - record length longer than prefix length
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 like '🌒%';
|
||||
f1
|
||||
🌒
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
0
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
2
|
||||
DROP TABLE t1;
|
||||
# Multi-byte with minimum character length > 1 bytes
|
||||
CREATE TABLE t1(
|
||||
f1 varchar(10) CHARACTER SET UTF16 COLLATE UTF16_BIN,
|
||||
INDEX (f1(3)))ENGINE=INNODB;
|
||||
INSERT INTO t1 VALUES('a'), ('cccc'), ('až'), ('cčc'), ('ggᵷg'), ('¢¢');
|
||||
INSERT INTO t1 VALUES('தமிழ்'), ('🐱🌑'), ('🌒'), ('🌑');
|
||||
INSERT INTO t1 VALUES('😊me'), ('eu€'), ('ls¢');
|
||||
# Eligible - record length is shorter than prefix
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 = 'a';
|
||||
f1
|
||||
a
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
0
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
1
|
||||
# Not eligible - record length longer than prefix length
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 like 'c%';
|
||||
f1
|
||||
cccc
|
||||
cčc
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
3
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
0
|
||||
# Eligible - record length shorter than prefix length
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 = 'až';
|
||||
f1
|
||||
až
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
0
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
1
|
||||
# Not eligible - record length longer than prefix length
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 = 'தமிழ்';
|
||||
f1
|
||||
தமிழ்
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
1
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
0
|
||||
# Not eligible - record length longer than prefix length
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 like 'ggᵷ%';
|
||||
f1
|
||||
ggᵷg
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
2
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
0
|
||||
# Not eligible - record length longer than prefix length
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 like '😊%';
|
||||
f1
|
||||
😊me
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
1
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
0
|
||||
# Not eligible - record length longer than prefix length
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 = 'ls¢';
|
||||
f1
|
||||
ls¢
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
1
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
0
|
||||
# Eligible - record length shorter than prefix length
|
||||
SELECT f1 FROM t1 FORCE INDEX(`f1`) WHERE f1 like '¢¢%';
|
||||
f1
|
||||
¢¢
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
1
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
1
|
||||
# Eligible - record length shorter than prefix length
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 like '🐱🌑%';
|
||||
f1
|
||||
🐱🌑
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
2
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
0
|
||||
# Eligible - record length is shorter than prefix length
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 like '🌑%';
|
||||
f1
|
||||
🌑
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
0
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
2
|
||||
# Eligible - record length is shorter than prefix length
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 like '🌒%';
|
||||
f1
|
||||
🌒
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
1
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
1
|
||||
DROP TABLE t1;
|
||||
CREATE TABLE t1(
|
||||
col1 INT,
|
||||
col2 BLOB DEFAULT NULL,
|
||||
INDEX `idx1`(col2(4), col1))ENGINE=INNODB;
|
||||
INSERT INTO t1 VALUES (2, 'test'), (3, repeat('test1', 2000));
|
||||
INSERT INTO t1(col1) VALUES(1);
|
||||
# Eligible - record length is shorter than prefix length
|
||||
SELECT col1 FROM t1 FORCE INDEX (`idx1`) WHERE col2 is NULL;
|
||||
col1
|
||||
1
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
0
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
1
|
||||
# Not eligible - record length longer than prefix index
|
||||
SELECT col1 FROM t1 FORCE INDEX (`idx1`) WHERE col2 like 'test1%';
|
||||
col1
|
||||
3
|
||||
select @cluster_lookups;
|
||||
@cluster_lookups
|
||||
2
|
||||
select @cluster_lookups_avoided;
|
||||
@cluster_lookups_avoided
|
||||
0
|
||||
DROP TABLE t1;
|
||||
set global innodb_prefix_index_cluster_optimization = OFF;
|
||||
|
@ -31,120 +31,638 @@ select * from prefixinno;
|
||||
let $show_count_statement = show status like 'innodb_secondary_index_triggered_cluster_reads';
|
||||
let $show_opt_statement = show status like 'innodb_secondary_index_triggered_cluster_reads_avoided';
|
||||
|
||||
--disable_query_log
|
||||
|
||||
--echo # Baseline sanity check: 0, 0.
|
||||
--let $base_count = query_get_value($show_count_statement, Value, 1)
|
||||
--let $base_opt = query_get_value($show_opt_statement, Value, 1)
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
select "no-op query";
|
||||
--let $count = query_get_value($show_count_statement, Value, 1)
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval select $count - $base_count into @cluster_lookups;
|
||||
select @cluster_lookups = 0 as cluster_lookups_matched;
|
||||
--let $opt = query_get_value($show_opt_statement, Value, 1)
|
||||
eval select $opt - $base_opt into @cluster_lookups;
|
||||
select @cluster_lookups = 0 as cluster_lookups_avoided_matched;
|
||||
eval select $opt - $base_opt into @cluster_lookups_avoided;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Eligible for optimization.
|
||||
--let $base_count = query_get_value($show_count_statement, Value, 1)
|
||||
--let $base_opt = query_get_value($show_opt_statement, Value, 1)
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
select id, bigfield from prefixinno where bigfield = repeat('d', 31);
|
||||
--let $count = query_get_value($show_count_statement, Value, 1)
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval select $count - $base_count into @cluster_lookups;
|
||||
select @cluster_lookups = 0 as cluster_lookups_matched;
|
||||
--let $opt = query_get_value($show_opt_statement, Value, 1)
|
||||
eval select $opt - $base_opt into @cluster_lookups;
|
||||
select @cluster_lookups = 1 as cluster_lookups_avoided_matched;
|
||||
eval select $opt - $base_opt into @cluster_lookups_avoided;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Eligible for optimization, access via fake_id only.
|
||||
--let $base_count = query_get_value($show_count_statement, Value, 1)
|
||||
--let $base_opt = query_get_value($show_opt_statement, Value, 1)
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
select id, bigfield from prefixinno where fake_id = 1031;
|
||||
--let $count = query_get_value($show_count_statement, Value, 1)
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval select $count - $base_count into @cluster_lookups;
|
||||
select @cluster_lookups = 0 as cluster_lookups_matched;
|
||||
--let $opt = query_get_value($show_opt_statement, Value, 1)
|
||||
eval select $opt - $base_opt into @cluster_lookups;
|
||||
select @cluster_lookups = 1 as cluster_lookups_avoided_matched;
|
||||
eval select $opt - $base_opt into @cluster_lookups_avoided;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Not eligible for optimization, access via fake_id of big row.
|
||||
--let $base_count = query_get_value($show_count_statement, Value, 1)
|
||||
--let $base_opt = query_get_value($show_opt_statement, Value, 1)
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
select id, bigfield from prefixinno where fake_id = 1033;
|
||||
--let $count = query_get_value($show_count_statement, Value, 1)
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval select $count - $base_count into @cluster_lookups;
|
||||
select @cluster_lookups = 1 as cluster_lookups_matched;
|
||||
--let $opt = query_get_value($show_opt_statement, Value, 1)
|
||||
eval select $opt - $base_opt into @cluster_lookups;
|
||||
select @cluster_lookups = 0 as cluster_lookups_avoided_matched;
|
||||
eval select $opt - $base_opt into @cluster_lookups_avoided;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Not eligible for optimization.
|
||||
--let $base_count = query_get_value($show_count_statement, Value, 1)
|
||||
--let $base_opt = query_get_value($show_opt_statement, Value, 1)
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
select id, bigfield from prefixinno where bigfield = repeat('x', 32);
|
||||
--let $count = query_get_value($show_count_statement, Value, 1)
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval select $count - $base_count into @cluster_lookups;
|
||||
select @cluster_lookups = 1 as cluster_lookups_matched;
|
||||
--let $opt = query_get_value($show_opt_statement, Value, 1)
|
||||
eval select $opt - $base_opt into @cluster_lookups;
|
||||
select @cluster_lookups = 0 as cluster_lookups_avoided_matched;
|
||||
eval select $opt - $base_opt into @cluster_lookups_avoided;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Not eligible for optimization.
|
||||
--let $base_count = query_get_value($show_count_statement, Value, 1)
|
||||
--let $base_opt = query_get_value($show_opt_statement, Value, 1)
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
select id, bigfield from prefixinno where bigfield = repeat('y', 33);
|
||||
--let $count = query_get_value($show_count_statement, Value, 1)
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval select $count - $base_count into @cluster_lookups;
|
||||
select @cluster_lookups = 1 as cluster_lookups_matched;
|
||||
--let $opt = query_get_value($show_opt_statement, Value, 1)
|
||||
eval select $opt - $base_opt into @cluster_lookups;
|
||||
select @cluster_lookups = 0 as cluster_lookups_avoided_matched;
|
||||
eval select $opt - $base_opt into @cluster_lookups_avoided;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Eligible, should not increment lookup counter.
|
||||
--let $base_count = query_get_value($show_count_statement, Value, 1)
|
||||
--let $base_opt = query_get_value($show_opt_statement, Value, 1)
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
select id, bigfield from prefixinno where bigfield = repeat('b', 8);
|
||||
--let $count = query_get_value($show_count_statement, Value, 1)
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval select $count - $base_count into @cluster_lookups;
|
||||
select @cluster_lookups = 0 as cluster_lookups_matched;
|
||||
--let $opt = query_get_value($show_opt_statement, Value, 1)
|
||||
eval select $opt - $base_opt into @cluster_lookups;
|
||||
select @cluster_lookups = 1 as cluster_lookups_avoided_matched;
|
||||
eval select $opt - $base_opt into @cluster_lookups_avoided;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Eligible, should not increment lookup counter.
|
||||
--let $base_count = query_get_value($show_count_statement, Value, 1)
|
||||
--let $base_opt = query_get_value($show_opt_statement, Value, 1)
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
select id, bigfield from prefixinno where bigfield = repeat('c', 24);
|
||||
--let $count = query_get_value($show_count_statement, Value, 1)
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval select $count - $base_count into @cluster_lookups;
|
||||
select @cluster_lookups = 0 as cluster_lookups_matched;
|
||||
--let $opt = query_get_value($show_opt_statement, Value, 1)
|
||||
eval select $opt - $base_opt into @cluster_lookups;
|
||||
select @cluster_lookups = 1 as cluster_lookups_avoided_matched;
|
||||
eval select $opt - $base_opt into @cluster_lookups_avoided;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Should increment lookup counter.
|
||||
--let $base_count = query_get_value($show_count_statement, Value, 1)
|
||||
--let $base_opt = query_get_value($show_opt_statement, Value, 1)
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
select id, bigfield from prefixinno where bigfield = repeat('z', 128);
|
||||
--let $count = query_get_value($show_count_statement, Value, 1)
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval select $count - $base_count into @cluster_lookups;
|
||||
select @cluster_lookups = 1 as cluster_lookups_matched;
|
||||
--let $opt = query_get_value($show_opt_statement, Value, 1)
|
||||
eval select $opt - $base_opt into @cluster_lookups;
|
||||
select @cluster_lookups = 0 as cluster_lookups_avoided_matched;
|
||||
eval select $opt - $base_opt into @cluster_lookups_avoided;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Disable optimization, confirm we still increment counter.
|
||||
--let $base_count = query_get_value($show_count_statement, Value, 1)
|
||||
--let $base_opt = query_get_value($show_opt_statement, Value, 1)
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
set global innodb_prefix_index_cluster_optimization = OFF;
|
||||
select id, bigfield from prefixinno where fake_id = 1033;
|
||||
--let $count = query_get_value($show_count_statement, Value, 1)
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval select $count - $base_count into @cluster_lookups;
|
||||
select @cluster_lookups = 1 as cluster_lookups_matched;
|
||||
--let $opt = query_get_value($show_opt_statement, Value, 1)
|
||||
eval select $opt - $base_opt into @cluster_lookups;
|
||||
select @cluster_lookups = 0 as cluster_lookups_avoided_matched;
|
||||
eval select $opt - $base_opt into @cluster_lookups_avoided;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # make test suite happy by cleaning up our mess
|
||||
drop table prefixinno;
|
||||
|
||||
--echo # Multi-byte handling case
|
||||
|
||||
set global innodb_prefix_index_cluster_optimization = ON;
|
||||
SET NAMES utf8mb4;
|
||||
CREATE TABLE t1(
|
||||
f1 varchar(10) CHARACTER SET UTF8MB4 COLLATE UTF8MB4_BIN,
|
||||
INDEX (f1(3)))ENGINE=INNODB;
|
||||
|
||||
INSERT INTO t1 VALUES('a'), ('cccc'), ('až'), ('cčc'), ('ggᵷg'), ('¢¢');
|
||||
INSERT INTO t1 VALUES('தமிழ்'), ('🐱🌑'), ('🌒'), ('🌑');
|
||||
INSERT INTO t1 VALUES('😊me'), ('eu€'), ('ls¢');
|
||||
|
||||
--echo # Eligible - record length is shorter than prefix
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 = 'a';
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval set @cluster_lookups = $count - $base_count;
|
||||
eval set @cluster_lookups_avoided = $opt - $base_opt;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Not eligible - record length longer than prefix length
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 like 'c%';
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval set @cluster_lookups = $count - $base_count;
|
||||
eval set @cluster_lookups_avoided = $opt - $base_opt;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Eligible - record length shorter than prefix length
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 = 'až';
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval set @cluster_lookups = $count - $base_count;
|
||||
eval set @cluster_lookups_avoided = $opt - $base_opt;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Not eligible - record length longer than prefix length
|
||||
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 = 'தமிழ்';
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval set @cluster_lookups = $count - $base_count;
|
||||
eval set @cluster_lookups_avoided = $opt - $base_opt;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Not eligible - record length longer than prefix length
|
||||
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 like 'ggᵷ%';
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval set @cluster_lookups = $count - $base_count;
|
||||
eval set @cluster_lookups_avoided = $opt - $base_opt;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Not eligible - record length longer than prefix length
|
||||
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 like '😊%';
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval set @cluster_lookups = $count - $base_count;
|
||||
eval set @cluster_lookups_avoided = $opt - $base_opt;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Not eligible - record length longer than prefix length
|
||||
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 = 'ls¢';
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval set @cluster_lookups = $count - $base_count;
|
||||
eval set @cluster_lookups_avoided = $opt - $base_opt;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Eligible - record length shorter than prefix length
|
||||
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 like '¢¢%';
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval set @cluster_lookups = $count - $base_count;
|
||||
eval set @cluster_lookups_avoided = $opt - $base_opt;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Eligible - record length shorter than prefix length
|
||||
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 like '🐱🌑%';
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval set @cluster_lookups = $count - $base_count;
|
||||
eval set @cluster_lookups_avoided = $opt - $base_opt;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Not eligible - record length longer than prefix length
|
||||
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 like '🌑%';
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval set @cluster_lookups = $count - $base_count;
|
||||
eval set @cluster_lookups_avoided = $opt - $base_opt;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Not eligible - record length longer than prefix length
|
||||
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 like '🌒%';
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval set @cluster_lookups = $count - $base_count;
|
||||
eval set @cluster_lookups_avoided = $opt - $base_opt;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
DROP TABLE t1;
|
||||
|
||||
--echo # Multi-byte with minimum character length > 1 bytes
|
||||
|
||||
CREATE TABLE t1(
|
||||
f1 varchar(10) CHARACTER SET UTF16 COLLATE UTF16_BIN,
|
||||
INDEX (f1(3)))ENGINE=INNODB;
|
||||
|
||||
INSERT INTO t1 VALUES('a'), ('cccc'), ('až'), ('cčc'), ('ggᵷg'), ('¢¢');
|
||||
INSERT INTO t1 VALUES('தமிழ்'), ('🐱🌑'), ('🌒'), ('🌑');
|
||||
INSERT INTO t1 VALUES('😊me'), ('eu€'), ('ls¢');
|
||||
|
||||
--echo # Eligible - record length is shorter than prefix
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 = 'a';
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval set @cluster_lookups = $count - $base_count;
|
||||
eval set @cluster_lookups_avoided = $opt - $base_opt;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Not eligible - record length longer than prefix length
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 like 'c%';
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval set @cluster_lookups = $count - $base_count;
|
||||
eval set @cluster_lookups_avoided = $opt - $base_opt;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Eligible - record length shorter than prefix length
|
||||
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 = 'až';
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval set @cluster_lookups = $count - $base_count;
|
||||
eval set @cluster_lookups_avoided = $opt - $base_opt;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Not eligible - record length longer than prefix length
|
||||
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 = 'தமிழ்';
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval set @cluster_lookups = $count - $base_count;
|
||||
eval set @cluster_lookups_avoided = $opt - $base_opt;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Not eligible - record length longer than prefix length
|
||||
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 like 'ggᵷ%';
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval set @cluster_lookups = $count - $base_count;
|
||||
eval set @cluster_lookups_avoided = $opt - $base_opt;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Not eligible - record length longer than prefix length
|
||||
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 like '😊%';
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval set @cluster_lookups = $count - $base_count;
|
||||
eval set @cluster_lookups_avoided = $opt - $base_opt;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Not eligible - record length longer than prefix length
|
||||
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 = 'ls¢';
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval set @cluster_lookups = $count - $base_count;
|
||||
eval set @cluster_lookups_avoided = $opt - $base_opt;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Eligible - record length shorter than prefix length
|
||||
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
SELECT f1 FROM t1 FORCE INDEX(`f1`) WHERE f1 like '¢¢%';
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval set @cluster_lookups = $count - $base_count;
|
||||
eval set @cluster_lookups_avoided = $opt - $base_opt;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Eligible - record length shorter than prefix length
|
||||
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 like '🐱🌑%';
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval set @cluster_lookups = $count - $base_count;
|
||||
eval set @cluster_lookups_avoided = $opt - $base_opt;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Eligible - record length is shorter than prefix length
|
||||
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 like '🌑%';
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval set @cluster_lookups = $count - $base_count;
|
||||
eval set @cluster_lookups_avoided = $opt - $base_opt;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Eligible - record length is shorter than prefix length
|
||||
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
SELECT f1 FROM t1 FORCE INDEX (`f1`) WHERE f1 like '🌒%';
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval set @cluster_lookups = $count - $base_count;
|
||||
eval set @cluster_lookups_avoided = $opt - $base_opt;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
DROP TABLE t1;
|
||||
|
||||
CREATE TABLE t1(
|
||||
col1 INT,
|
||||
col2 BLOB DEFAULT NULL,
|
||||
INDEX `idx1`(col2(4), col1))ENGINE=INNODB;
|
||||
INSERT INTO t1 VALUES (2, 'test'), (3, repeat('test1', 2000));
|
||||
INSERT INTO t1(col1) VALUES(1);
|
||||
|
||||
--echo # Eligible - record length is shorter than prefix length
|
||||
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
SELECT col1 FROM t1 FORCE INDEX (`idx1`) WHERE col2 is NULL;
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval set @cluster_lookups = $count - $base_count;
|
||||
eval set @cluster_lookups_avoided = $opt - $base_opt;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
--echo # Not eligible - record length longer than prefix index
|
||||
|
||||
let $base_count = query_get_value($show_count_statement, Value, 1);
|
||||
let $base_opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
SELECT col1 FROM t1 FORCE INDEX (`idx1`) WHERE col2 like 'test1%';
|
||||
|
||||
let $count = query_get_value($show_count_statement, Value, 1);
|
||||
let $opt = query_get_value($show_opt_statement, Value, 1);
|
||||
|
||||
--disable_query_log
|
||||
eval set @cluster_lookups = $count - $base_count;
|
||||
eval set @cluster_lookups_avoided = $opt - $base_opt;
|
||||
--enable_query_log
|
||||
|
||||
select @cluster_lookups;
|
||||
select @cluster_lookups_avoided;
|
||||
|
||||
DROP TABLE t1;
|
||||
set global innodb_prefix_index_cluster_optimization = OFF;
|
||||
|
@ -2707,7 +2707,9 @@ row_sel_field_store_in_mysql_format_func(
|
||||
|| !(templ->mysql_col_len % templ->mbmaxlen));
|
||||
ut_ad(len * templ->mbmaxlen >= templ->mysql_col_len
|
||||
|| (field_no == templ->icp_rec_field_no
|
||||
&& field->prefix_len > 0));
|
||||
&& field->prefix_len > 0)
|
||||
|| templ->rec_field_is_prefix);
|
||||
|
||||
ut_ad(!(field->prefix_len % templ->mbmaxlen));
|
||||
|
||||
if (templ->mbminlen == 1 && templ->mbmaxlen != 1) {
|
||||
@ -3667,6 +3669,118 @@ row_search_idx_cond_check(
|
||||
return(result);
|
||||
}
|
||||
|
||||
/** Return the record field length in characters.
|
||||
@param[in] col table column of the field
|
||||
@param[in] field_no field number
|
||||
@param[in] rec physical record
|
||||
@param[in] offsets field offsets in the physical record
|
||||
@return field length in characters. */
|
||||
static
|
||||
size_t
|
||||
rec_field_len_in_chars(
|
||||
const dict_col_t* col,
|
||||
const ulint field_no,
|
||||
const rec_t* rec,
|
||||
const ulint* offsets)
|
||||
{
|
||||
const ulint cset = dtype_get_charset_coll(col->prtype);
|
||||
const CHARSET_INFO* cs = all_charsets[cset];
|
||||
ulint rec_field_len;
|
||||
const char* rec_field = reinterpret_cast<const char *>(
|
||||
rec_get_nth_field(
|
||||
rec, offsets, field_no, &rec_field_len));
|
||||
|
||||
if (UNIV_UNLIKELY(!cs)) {
|
||||
ib_logf(IB_LOG_LEVEL_WARN, "Missing collation " ULINTPF, cset);
|
||||
return SIZE_T_MAX;
|
||||
}
|
||||
|
||||
return(cs->cset->numchars(cs, rec_field, rec_field + rec_field_len));
|
||||
}
|
||||
|
||||
/** Avoid the clustered index lookup if all the following conditions
|
||||
are true:
|
||||
1) all columns are in secondary index
|
||||
2) all values for columns that are prefix-only indexes are shorter
|
||||
than the prefix size. This optimization can avoid many IOs for certain schemas.
|
||||
@return true, to avoid clustered index lookup. */
|
||||
static
|
||||
bool row_search_with_covering_prefix(
|
||||
row_prebuilt_t* prebuilt,
|
||||
const rec_t* rec,
|
||||
const ulint* offsets)
|
||||
{
|
||||
const dict_index_t* index = prebuilt->index;
|
||||
ut_ad(!dict_index_is_clust(index));
|
||||
|
||||
if (!srv_prefix_index_cluster_optimization) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Optimization only applicable if there the number of secondary index
|
||||
fields are greater than or equal to number of clustered index fields. */
|
||||
if (prebuilt->n_template > index->n_fields) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (ulint i = 0; i < prebuilt->n_template; i++) {
|
||||
mysql_row_templ_t* templ = prebuilt->mysql_template + i;
|
||||
ulint j = templ->rec_prefix_field_no;
|
||||
|
||||
/** Condition (1) : is the field in the index. */
|
||||
if (j == ULINT_UNDEFINED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Condition (2): If this is a prefix index then
|
||||
row's value size shorter than prefix length. */
|
||||
|
||||
if (!templ->rec_field_is_prefix) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ulint rec_size = rec_offs_nth_size(offsets, j);
|
||||
const dict_field_t* field = dict_index_get_nth_field(index, j);
|
||||
ulint max_chars = field->prefix_len / templ->mbmaxlen;
|
||||
|
||||
ut_a(field->prefix_len > 0);
|
||||
|
||||
if (rec_size < max_chars) {
|
||||
/* Record in bytes shorter than the index
|
||||
prefix length in char. */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rec_size * templ->mbminlen >= field->prefix_len) {
|
||||
/* Shortest representation string by the
|
||||
byte length of the record is longer than the
|
||||
maximum possible index prefix. */
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t num_chars = rec_field_len_in_chars(
|
||||
field->col, j, rec, offsets);
|
||||
|
||||
if (num_chars >= max_chars) {
|
||||
/* No of chars to store the record exceeds
|
||||
the index prefix character length. */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* If prefix index optimization condition satisfied then
|
||||
for all columns above, use rec_prefix_field_no instead of
|
||||
rec_field_no, and skip the clustered lookup below. */
|
||||
for (ulint i = 0; i < prebuilt->n_template; i++) {
|
||||
mysql_row_templ_t* templ = prebuilt->mysql_template + i;
|
||||
templ->rec_field_no = templ->rec_prefix_field_no;
|
||||
ut_a(templ->rec_field_no != ULINT_UNDEFINED);
|
||||
}
|
||||
|
||||
srv_stats.n_sec_rec_cluster_reads_avoided.inc();
|
||||
return true;
|
||||
}
|
||||
|
||||
/********************************************************************//**
|
||||
Searches for rows in the database. This is used in the interface to
|
||||
MySQL. This function opens a cursor, and also implements fetch next
|
||||
@ -3729,7 +3843,6 @@ row_search_for_mysql(
|
||||
ulint* offsets = offsets_;
|
||||
ibool table_lock_waited = FALSE;
|
||||
byte* next_buf = 0;
|
||||
ibool use_clustered_index = FALSE;
|
||||
|
||||
rec_offs_init(offsets_);
|
||||
|
||||
@ -4790,69 +4903,10 @@ locks_ok:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Get the clustered index record if needed, if we did not do the
|
||||
search using the clustered index... */
|
||||
|
||||
use_clustered_index =
|
||||
(index != clust_index && prebuilt->need_to_access_clustered);
|
||||
|
||||
if (use_clustered_index && srv_prefix_index_cluster_optimization
|
||||
&& prebuilt->n_template <= index->n_fields) {
|
||||
/* ...but, perhaps avoid the clustered index lookup if
|
||||
all of the following are true:
|
||||
1) all columns are in the secondary index
|
||||
2) all values for columns that are prefix-only
|
||||
indexes are shorter than the prefix size
|
||||
This optimization can avoid many IOs for certain schemas.
|
||||
*/
|
||||
ibool row_contains_all_values = TRUE;
|
||||
int i;
|
||||
for (i = 0; i < prebuilt->n_template; i++) {
|
||||
/* Condition (1) from above: is the field in the
|
||||
index (prefix or not)? */
|
||||
mysql_row_templ_t* templ =
|
||||
prebuilt->mysql_template + i;
|
||||
ulint secondary_index_field_no =
|
||||
templ->rec_prefix_field_no;
|
||||
if (secondary_index_field_no == ULINT_UNDEFINED) {
|
||||
row_contains_all_values = FALSE;
|
||||
break;
|
||||
}
|
||||
/* Condition (2) from above: if this is a
|
||||
prefix, is this row's value size shorter
|
||||
than the prefix? */
|
||||
if (templ->rec_field_is_prefix) {
|
||||
ulint record_size = rec_offs_nth_size(
|
||||
offsets,
|
||||
secondary_index_field_no);
|
||||
const dict_field_t *field =
|
||||
dict_index_get_nth_field(
|
||||
index,
|
||||
secondary_index_field_no);
|
||||
ut_a(field->prefix_len > 0);
|
||||
if (record_size >= field->prefix_len) {
|
||||
row_contains_all_values = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index != clust_index && prebuilt->need_to_access_clustered) {
|
||||
if (row_search_with_covering_prefix(prebuilt, rec, offsets)) {
|
||||
goto use_covering_index;
|
||||
}
|
||||
/* If (1) and (2) were true for all columns above, use
|
||||
rec_prefix_field_no instead of rec_field_no, and skip
|
||||
the clustered lookup below. */
|
||||
if (row_contains_all_values) {
|
||||
for (i = 0; i < prebuilt->n_template; i++) {
|
||||
mysql_row_templ_t* templ =
|
||||
prebuilt->mysql_template + i;
|
||||
templ->rec_field_no =
|
||||
templ->rec_prefix_field_no;
|
||||
ut_a(templ->rec_field_no != ULINT_UNDEFINED);
|
||||
}
|
||||
use_clustered_index = FALSE;
|
||||
srv_stats.n_sec_rec_cluster_reads_avoided.inc();
|
||||
}
|
||||
}
|
||||
|
||||
if (use_clustered_index) {
|
||||
requires_clust_rec:
|
||||
ut_ad(index != clust_index);
|
||||
/* We use a 'goto' to the preceding label if a consistent
|
||||
@ -4938,6 +4992,7 @@ requires_clust_rec:
|
||||
}
|
||||
}
|
||||
} else {
|
||||
use_covering_index:
|
||||
result_rec = rec;
|
||||
}
|
||||
|
||||
|
@ -3685,6 +3685,117 @@ row_search_idx_cond_check(
|
||||
return(result);
|
||||
}
|
||||
|
||||
/** Return the record field length in characters.
|
||||
@param[in] col table column of the field
|
||||
@param[in] field_no field number
|
||||
@param[in] rec physical record
|
||||
@param[in] offsets field offsets in the physical record
|
||||
@return field length in characters. */
|
||||
static
|
||||
size_t
|
||||
rec_field_len_in_chars(
|
||||
const dict_col_t* col,
|
||||
const ulint field_no,
|
||||
const rec_t* rec,
|
||||
const ulint* offsets)
|
||||
{
|
||||
const ulint cset = dtype_get_charset_coll(col->prtype);
|
||||
const CHARSET_INFO* cs = all_charsets[cset];
|
||||
ulint rec_field_len;
|
||||
const char* rec_field = reinterpret_cast<const char *>(
|
||||
rec_get_nth_field(
|
||||
rec, offsets, field_no, &rec_field_len));
|
||||
|
||||
if (UNIV_UNLIKELY(!cs)) {
|
||||
ib_logf(IB_LOG_LEVEL_WARN, "Missing collation " ULINTPF, cset);
|
||||
return SIZE_T_MAX;
|
||||
}
|
||||
|
||||
return(cs->cset->numchars(cs, rec_field, rec_field + rec_field_len));
|
||||
}
|
||||
|
||||
|
||||
/** Avoid the clustered index lookup if all the following conditions
|
||||
are true:
|
||||
1) all columns are in secondary index
|
||||
2) all values for columns that are prefix-only indexes are shorter
|
||||
than the prefix size. This optimization can avoid many IOs for certain schemas.
|
||||
@return true, to avoid clustered index lookup. */
|
||||
static
|
||||
bool row_search_with_covering_prefix(
|
||||
row_prebuilt_t* prebuilt,
|
||||
const rec_t* rec,
|
||||
const ulint* offsets)
|
||||
{
|
||||
const dict_index_t* index = prebuilt->index;
|
||||
ut_ad(!dict_index_is_clust(index));
|
||||
|
||||
if (!srv_prefix_index_cluster_optimization) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Optimization only applicable if the number of secondary index
|
||||
fields are greater than or equal to number of clustered index fields. */
|
||||
if (prebuilt->n_template > index->n_fields) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (ulint i = 0; i < prebuilt->n_template; i++) {
|
||||
mysql_row_templ_t* templ = prebuilt->mysql_template + i;
|
||||
ulint j = templ->rec_prefix_field_no;
|
||||
|
||||
/** Condition (1) : is the field in the index. */
|
||||
if (j == ULINT_UNDEFINED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Condition (2): If this is a prefix index then
|
||||
row's value size shorter than prefix length. */
|
||||
|
||||
if (!templ->rec_field_is_prefix) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ulint rec_size = rec_offs_nth_size(offsets, j);
|
||||
const dict_field_t* field = dict_index_get_nth_field(index, j);
|
||||
ulint max_chars = field->prefix_len / templ->mbmaxlen;
|
||||
|
||||
ut_a(field->prefix_len > 0);
|
||||
|
||||
if (rec_size < max_chars) {
|
||||
/* Record in bytes shorter than the index
|
||||
prefix length in char. */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rec_size * templ->mbminlen >= field->prefix_len) {
|
||||
/* Shortest representation string by the
|
||||
byte length of the record is longer than the
|
||||
maximum possible index prefix. */
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
size_t num_chars = rec_field_len_in_chars(
|
||||
field->col, j, rec, offsets);
|
||||
|
||||
if (num_chars >= max_chars) {
|
||||
/* No of chars to store the record exceeds
|
||||
the index prefix character length. */
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (ulint i = 0; i < prebuilt->n_template; i++) {
|
||||
mysql_row_templ_t* templ = prebuilt->mysql_template + i;
|
||||
templ->rec_field_no = templ->rec_prefix_field_no;
|
||||
ut_a(templ->rec_field_no != ULINT_UNDEFINED);
|
||||
}
|
||||
|
||||
srv_stats.n_sec_rec_cluster_reads_avoided.inc();
|
||||
return true;
|
||||
}
|
||||
|
||||
/********************************************************************//**
|
||||
Searches for rows in the database. This is used in the interface to
|
||||
MySQL. This function opens a cursor, and also implements fetch next
|
||||
@ -3748,7 +3859,6 @@ row_search_for_mysql(
|
||||
ulint* offsets = offsets_;
|
||||
ibool table_lock_waited = FALSE;
|
||||
byte* next_buf = 0;
|
||||
bool use_clustered_index = false;
|
||||
|
||||
rec_offs_init(offsets_);
|
||||
|
||||
@ -4810,71 +4920,10 @@ locks_ok:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Get the clustered index record if needed, if we did not do the
|
||||
search using the clustered index... */
|
||||
|
||||
use_clustered_index =
|
||||
(index != clust_index && prebuilt->need_to_access_clustered);
|
||||
|
||||
if (use_clustered_index && srv_prefix_index_cluster_optimization
|
||||
&& prebuilt->n_template <= index->n_fields) {
|
||||
/* ...but, perhaps avoid the clustered index lookup if
|
||||
all of the following are true:
|
||||
1) all columns are in the secondary index
|
||||
2) all values for columns that are prefix-only
|
||||
indexes are shorter than the prefix size
|
||||
This optimization can avoid many IOs for certain schemas.
|
||||
*/
|
||||
bool row_contains_all_values = true;
|
||||
unsigned int i;
|
||||
for (i = 0; i < prebuilt->n_template; i++) {
|
||||
/* Condition (1) from above: is the field in the
|
||||
index (prefix or not)? */
|
||||
const mysql_row_templ_t* templ =
|
||||
prebuilt->mysql_template + i;
|
||||
ulint secondary_index_field_no =
|
||||
templ->rec_prefix_field_no;
|
||||
if (secondary_index_field_no == ULINT_UNDEFINED) {
|
||||
row_contains_all_values = false;
|
||||
break;
|
||||
}
|
||||
/* Condition (2) from above: if this is a
|
||||
prefix, is this row's value size shorter
|
||||
than the prefix? */
|
||||
if (templ->rec_field_is_prefix) {
|
||||
ulint record_size = rec_offs_nth_size(
|
||||
offsets,
|
||||
secondary_index_field_no);
|
||||
const dict_field_t *field =
|
||||
dict_index_get_nth_field(
|
||||
index,
|
||||
secondary_index_field_no);
|
||||
ut_a(field->prefix_len > 0);
|
||||
if (record_size >= field->prefix_len
|
||||
/ templ->mbmaxlen) {
|
||||
row_contains_all_values = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index != clust_index && prebuilt->need_to_access_clustered) {
|
||||
if (row_search_with_covering_prefix(prebuilt, rec, offsets)) {
|
||||
goto use_covering_index;
|
||||
}
|
||||
/* If (1) and (2) were true for all columns above, use
|
||||
rec_prefix_field_no instead of rec_field_no, and skip
|
||||
the clustered lookup below. */
|
||||
if (row_contains_all_values) {
|
||||
for (i = 0; i < prebuilt->n_template; i++) {
|
||||
mysql_row_templ_t* templ =
|
||||
prebuilt->mysql_template + i;
|
||||
templ->rec_field_no =
|
||||
templ->rec_prefix_field_no;
|
||||
ut_a(templ->rec_field_no != ULINT_UNDEFINED);
|
||||
}
|
||||
use_clustered_index = false;
|
||||
srv_stats.n_sec_rec_cluster_reads_avoided.inc();
|
||||
}
|
||||
}
|
||||
|
||||
if (use_clustered_index) {
|
||||
|
||||
requires_clust_rec:
|
||||
ut_ad(index != clust_index);
|
||||
/* We use a 'goto' to the preceding label if a consistent
|
||||
@ -4960,6 +5009,7 @@ requires_clust_rec:
|
||||
}
|
||||
}
|
||||
} else {
|
||||
use_covering_index:
|
||||
result_rec = rec;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user