From 4f46bae86c62b48036b056ec5040a5c5aaafbe4b Mon Sep 17 00:00:00 2001 From: Martin Hansson Date: Tue, 3 Mar 2026 16:26:59 +0000 Subject: [PATCH] PS-10481: Fix range optimizer full table scan for IN() with oversized values The range optimizer did not handle the case of search keys being longer than the index's key length. In this case, a search key could not be created, and in the case of WHERE clauses with multiple sufficient conditions - either OR or IN conditions - it had to fall back to table scan. This happens because we were using the column buffer normally used for INSERT or UPDATE, and it was not really meant to handle this case, at least not for non-binary collations. For binary collations, this is fine: an equality condition involving a value that wouldn't have fit the column can never match, and so we can safely just ignore it in a disjunction expression. For other collations, however, a longer sequence - in code points - can match a shorter one, and there is no upper limit on the length. Fixed by allocating a larger buffer in case the common key creation fails due to truncation. We allocate as much space as the worst-case scenario and then some, heeding the advice in comment above `strnxfrmlen()`. For full indexes we just render the entire string, and for prefix indexes we render a string with the same amount of characters (really code points) as the index's declared key length. We give up in case of any character set conversion errors. --- mysql-test/include/oversized_varchar_key.inc | 174 +++++ mysql-test/r/type_varchar.result | 582 --------------- mysql-test/suite/percona/r/bug118009.result | 744 +++++++++++++++++++ mysql-test/suite/percona/t/bug118009.test | 54 ++ mysql-test/t/type_varchar.test | 180 ----- sql/range_optimizer/range_analysis.cc | 101 ++- 6 files changed, 1066 insertions(+), 769 deletions(-) create mode 100644 mysql-test/include/oversized_varchar_key.inc delete mode 100644 mysql-test/r/type_varchar.result create mode 100644 mysql-test/suite/percona/r/bug118009.result create mode 100644 mysql-test/suite/percona/t/bug118009.test delete mode 100644 mysql-test/t/type_varchar.test diff --git a/mysql-test/include/oversized_varchar_key.inc b/mysql-test/include/oversized_varchar_key.inc new file mode 100644 index 000000000000..39f847915c98 --- /dev/null +++ b/mysql-test/include/oversized_varchar_key.inc @@ -0,0 +1,174 @@ +eval CREATE TABLE t1 ( pk VARCHAR(16) NOT NULL PRIMARY KEY ) CHARSET=$charset COLLATE=$collation; + +INSERT INTO t1 VALUES ('abc'), ('def'), ('ghi'), ('jkl'), ('mno'); +ANALYZE TABLE t1; + +EXPLAIN +SELECT * FROM t1 WHERE pk IN ('abc', 'this_value_is_>16', 'def'); +SELECT * FROM t1 WHERE pk IN ('abc', 'this_value_is_>16', 'def'); + +EXPLAIN +SELECT * FROM t1 WHERE pk IN ('abc', 'this_value_is_>16', 'def'); +SELECT * FROM t1 WHERE pk IN ('abc', 'this_value_is_>16', 'def'); + +EXPLAIN +SELECT * FROM t1 WHERE pk IN ('this_value_is_>16'); +SELECT * FROM t1 WHERE pk IN ('this_value_is_>16'); + +EXPLAIN +SELECT * FROM t1 WHERE pk IN ('this_value_is_>16', 'and_so_is_this_one'); +SELECT * FROM t1 WHERE pk IN ('this_value_is_>16', 'and_so_is_this_one'); + + +eval CREATE TABLE t2 ( a VARCHAR(1), KEY (a) ) CHARSET=$charset COLLATE=$collation; + +INSERT INTO t2 VALUES (NULL), ('a'), ('b'), ('æ'), ('ß'), ('s'); +INSERT INTO t2 SELECT 'x' FROM t2; +INSERT INTO t2 SELECT 'x' FROM t2; + +ANALYZE TABLE t2; + +--echo # Strings containing soft hyphen (U+00AD) will not match for UTF-16 + +EXPLAIN +SELECT * FROM t2 WHERE a IN ('ae', concat(_utf8mb4 x'c2ad', 'b'), 'ss'); +SELECT * FROM t2 WHERE a IN ('ae', concat(_utf8mb4 x'c2ad', 'b'), 'ss'); + +EXPLAIN +SELECT * FROM t2 WHERE a IN (concat(_utf8mb4 x'c2ad', 'ae'), 'ss'); +SELECT * FROM t2 WHERE a IN (concat(_utf8mb4 x'c2ad', 'ae'), 'ss'); + + +EXPLAIN +SELECT * FROM t2 WHERE a IN (concat(_utf8mb4 x'c2ad', 'ae', _utf8mb4 x'c2ad'), 'ss'); +SELECT * FROM t2 WHERE a IN (concat(_utf8mb4 x'c2ad', 'ae', _utf8mb4 x'c2ad'), 'ss'); + +EXPLAIN +SELECT * FROM t2 WHERE a = 'ae' OR a = concat(_utf8mb4 x'c2ad', 'b') OR a = 'ss'; +SELECT * FROM t2 WHERE a = 'ae' OR a = concat(_utf8mb4 x'c2ad', 'b') OR a = 'ss'; + +--echo # The plan is unstable in this case, however it always uses an index, and results are consistent. +--replace_column 5 X 10 X +EXPLAIN +SELECT * FROM t2 WHERE a IN ('ae', concat(repeat(_utf8mb4 x'c2ad', 2), 'b', repeat(_utf8mb4 x'c2ad', 2))); +--echo # Adding handler statistics to show that execution is also consistent. +FLUSH STATUS; +SELECT * FROM t2 WHERE a IN ('ae', concat(repeat(_utf8mb4 x'c2ad', 2), 'b', repeat(_utf8mb4 x'c2ad', 2))); +SHOW STATUS LIKE 'Handler_read_%'; + + +eval CREATE TABLE t3 ( a VARCHAR(768), KEY(a)) CHARSET=$charset COLLATE=$collation; + +INSERT INTO t3 VALUES( repeat('æ', 767) ), ( repeat('æ', 768) ); + +ANALYZE TABLE t3; + +EXPLAIN +SELECT 1 FROM t3 WHERE a IN ( 'a', repeat('æ', 767) ); +SELECT 1 FROM t3 WHERE a IN ( 'a', repeat('æ', 767) ); + +EXPLAIN +SELECT 1 FROM t3 WHERE a IN ( 'a', repeat('ae', 768) ); +SELECT 1 FROM t3 WHERE a IN ( 'a', repeat('ae', 768) ); + +EXPLAIN +SELECT 1 FROM t3 WHERE a IN ( 'a', repeat('ae', 769) ); +SELECT 1 FROM t3 WHERE a IN ( 'a', repeat('ae', 769) ); + + +eval CREATE TABLE t4 ( a VARCHAR(100), KEY(a(50))) CHARSET=$charset COLLATE=$collation; + +INSERT INTO t4 VALUES ( repeat('æ', 98) ), ( repeat('æ', 99) ), ( repeat('æ', 100) ); +INSERT INTO t4 VALUES ( concat(repeat( 'a', 50 ), repeat( 'æ', 50 )) ); + +ANALYZE TABLE t4; + +EXPLAIN +SELECT 1 FROM t4 WHERE a IN ( 'a', repeat('æ', 98) ); +SELECT 1 FROM t4 WHERE a IN ( 'a', repeat('æ', 98) ); + + +eval CREATE TABLE t5 ( a VARCHAR(16383), KEY(a(768))) CHARSET=$charset COLLATE=$collation; + +INSERT INTO t5 VALUES( repeat('æ', 16383) ), ( repeat('æ', 16382) ), ( repeat('æ', 16381) ); + +ANALYZE TABLE t5; + +EXPLAIN +SELECT 1 FROM t5 WHERE a IN ( 'a', repeat('æ', 16381) ); +SELECT 1 FROM t5 WHERE a IN ( 'a', repeat('æ', 16381) ); + +EXPLAIN +SELECT 1 FROM t5 WHERE a IN ( 'a', repeat('æ', 16382) ); +SELECT 1 FROM t5 WHERE a IN ( 'a', repeat('æ', 16382) ); + +EXPLAIN +SELECT 1 FROM t5 WHERE a IN ( 'a', repeat('æ', 16383) ); +SELECT 1 FROM t5 WHERE a IN ( 'a', repeat('æ', 16383) ); + + +eval CREATE TABLE t6 ( a VARCHAR(6), KEY(a(3)) ) CHARSET=$charset COLLATE=$collation; + +INSERT INTO t6 VALUES ('aaaaaa'), ('æaaaaa'), ('ææaaaa'), ('æææaaa'), ('ææææaa'), ('æææææa'), ('ææææææ'), + ('😊aaaaa'), ('😊æaaaa'), ('😊😊aaaa'), ('😊😊æaaa'), ('😊😊😊aaa'); + +ANALYZE TABLE t6; + +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', 'aaaaaa' ); +SELECT * FROM t6 WHERE a IN ( '', 'aaaaaa' ); + +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', 'æaaaaa' ); +SELECT * FROM t6 WHERE a IN ( '', 'æaaaaa' ); + +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', 'ææaaaa' ); +SELECT * FROM t6 WHERE a IN ( '', 'ææaaaa' ); + +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', 'æææaaa' ); +SELECT * FROM t6 WHERE a IN ( '', 'æææaaa' ); + +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', 'ææææaa' ); +SELECT * FROM t6 WHERE a IN ( '', 'ææææaa' ); + +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', 'æææææa' ); +SELECT * FROM t6 WHERE a IN ( '', 'æææææa' ); + +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', 'ææææææ' ); +SELECT * FROM t6 WHERE a IN ( '', 'ææææææ' ); + +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', '😊aaaaa'); +SELECT * FROM t6 WHERE a IN ( '', '😊aaaaa'); + +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', '😊æaaaa'); +SELECT * FROM t6 WHERE a IN ( '', '😊æaaaa'); + +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', '😊😊aaaa'); +SELECT * FROM t6 WHERE a IN ( '', '😊😊aaaa'); + +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', '😊😊æaaa'); +SELECT * FROM t6 WHERE a IN ( '', '😊😊😊aaa'); + +EXPLAIN +SELECT * FROM t6 WHERE a IN ('aaaaaa', 'æaaaaa', 'ææaaaa', 'æææaaa', 'ææææaa', 'æææææa', 'ææææææ'); +SELECT * FROM t6 WHERE a IN ('aaaaaa', 'æaaaaa', 'ææaaaa', 'æææaaa', 'ææææaa', 'æææææa', 'ææææææ'); + +EXPLAIN +SELECT * FROM t6 WHERE a IN + ('aaaaaa', 'æaaaaa', 'ææaaaa', 'æææaaa', 'ææææaa', 'æææææa', 'ææææææ', + '😊aaaaa', '😊æaaaa', '😊😊aaaa', '😊😊æaaa', '😊😊😊aaa'); +SELECT * FROM t6 WHERE a IN + ('aaaaaa', 'æaaaaa', 'ææaaaa', 'æææaaa', 'ææææaa', 'æææææa', 'ææææææ', + '😊aaaaa', '😊æaaaa', '😊😊aaaa', '😊😊æaaa', '😊😊😊aaa'); + + +DROP TABLE t1, t2, t3, t4, t5, t6; diff --git a/mysql-test/r/type_varchar.result b/mysql-test/r/type_varchar.result deleted file mode 100644 index 0a84407c1b93..000000000000 --- a/mysql-test/r/type_varchar.result +++ /dev/null @@ -1,582 +0,0 @@ -drop table if exists t1, t2; -create table t1 (v varchar(30), c char(3), e enum('abc','def','ghi'), t text); -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `v` varchar(30) DEFAULT NULL, - `c` char(3) DEFAULT NULL, - `e` enum('abc','def','ghi') DEFAULT NULL, - `t` text -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci -insert into t1 values ('abc', 'de', 'ghi', 'jkl'); -insert into t1 values ('abc ', 'de ', 'ghi', 'jkl '); -insert into t1 values ('abc ', 'd ', 'ghi', 'jkl '); -select length(v),length(c),length(e),length(t) from t1; -length(v) length(c) length(e) length(t) -3 2 3 3 -4 2 3 4 -7 1 3 7 -drop table t1; -create table t1 (v varchar(20)) charset latin1; -insert into t1 values('a '); -select v='a' from t1; -v='a' -1 -select binary v='a' from t1; -binary v='a' -0 -Warnings: -Warning 1287 'BINARY expr' is deprecated and will be removed in a future release. Please use CAST instead -select binary v='a ' from t1; -binary v='a ' -1 -Warnings: -Warning 1287 'BINARY expr' is deprecated and will be removed in a future release. Please use CAST instead -insert into t1 values('a'); -alter table t1 add primary key (v); -ERROR 23000: Duplicate entry 'a ' for key 't1.PRIMARY' -drop table t1; -create table t1 (v varbinary(20)); -insert into t1 values('a'); -insert into t1 values('a '); -alter table t1 add primary key (v); -drop table t1; -create table t1 (v varchar(254), index (v)) charset latin1; -insert into t1 values ("This is a test "); -insert into t1 values ("Some sample data"); -insert into t1 values (" garbage "); -insert into t1 values (" This is a test "); -insert into t1 values ("This is a test"); -insert into t1 values ("Hello world"); -insert into t1 values ("Foo bar"); -insert into t1 values ("This is a test"); -insert into t1 values ("MySQL varchar test"); -insert into t1 values ("test MySQL varchar"); -insert into t1 values ("This is a long string to have some random length data included"); -insert into t1 values ("Short string"); -insert into t1 values ("VSS"); -insert into t1 values ("Some samples"); -insert into t1 values ("Bar foo"); -insert into t1 values ("Bye"); -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status OK -select * from t1 where v like 'This is a test' order by v; -v -This is a test -This is a test -select * from t1 where v='This is a test' order by v; -v -This is a test -This is a test -This is a test -select * from t1 where v like 'S%' order by v; -v -Short string -Some sample data -Some samples -explain select * from t1 where v like 'This is a test' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL range v v 257 NULL 3 100.00 Using where; Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` like 'This is a test') order by `test`.`t1`.`v` -Helper line 1 for --skip_if_hypergraph diffing -explain select * from t1 where v='This is a test' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL ref v v 257 const 3 100.00 Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` = 'This is a test') order by `test`.`t1`.`v` -Helper line 2 for --skip_if_hypergraph diffing -explain select * from t1 where v like 'S%' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL range v v 257 NULL 3 100.00 Using where; Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` like 'S%') order by `test`.`t1`.`v` -alter table t1 change v v varchar(255); -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status OK -select * from t1 where v like 'This is a test' order by v; -v -This is a test -This is a test -select * from t1 where v='This is a test' order by v; -v -This is a test -This is a test -This is a test -select * from t1 where v like 'S%' order by v; -v -Short string -Some sample data -Some samples -explain select * from t1 where v like 'This is a test' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL range v v 258 NULL 3 100.00 Using where; Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` like 'This is a test') order by `test`.`t1`.`v` -Helper line 1 for --skip_if_hypergraph diffing -explain select * from t1 where v='This is a test' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL ref v v 258 const 3 100.00 Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` = 'This is a test') order by `test`.`t1`.`v` -Helper line 2 for --skip_if_hypergraph diffing -explain select * from t1 where v like 'S%' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL range v v 258 NULL 3 100.00 Using where; Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` like 'S%') order by `test`.`t1`.`v` -alter table t1 change v v varchar(256); -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status OK -select * from t1 where v like 'This is a test' order by v; -v -This is a test -This is a test -select * from t1 where v='This is a test' order by v; -v -This is a test -This is a test -This is a test -select * from t1 where v like 'S%' order by v; -v -Short string -Some sample data -Some samples -explain select * from t1 where v like 'This is a test' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL range v v 259 NULL 3 100.00 Using where; Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` like 'This is a test') order by `test`.`t1`.`v` -Helper line 1 for --skip_if_hypergraph diffing -explain select * from t1 where v='This is a test' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL ref v v 259 const 3 100.00 Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` = 'This is a test') order by `test`.`t1`.`v` -Helper line 2 for --skip_if_hypergraph diffing -explain select * from t1 where v like 'S%' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL range v v 259 NULL 3 100.00 Using where; Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` like 'S%') order by `test`.`t1`.`v` -alter table t1 change v v varchar(257); -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status OK -select * from t1 where v like 'This is a test' order by v; -v -This is a test -This is a test -select * from t1 where v='This is a test' order by v; -v -This is a test -This is a test -This is a test -select * from t1 where v like 'S%' order by v; -v -Short string -Some sample data -Some samples -explain select * from t1 where v like 'This is a test' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL range v v 260 NULL 3 100.00 Using where; Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` like 'This is a test') order by `test`.`t1`.`v` -Helper line 1 for --skip_if_hypergraph diffing -explain select * from t1 where v='This is a test' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL ref v v 260 const 3 100.00 Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` = 'This is a test') order by `test`.`t1`.`v` -Helper line 2 for --skip_if_hypergraph diffing -explain select * from t1 where v like 'S%' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL range v v 260 NULL 3 100.00 Using where; Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` like 'S%') order by `test`.`t1`.`v` -alter table t1 change v v varchar(258); -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status OK -select * from t1 where v like 'This is a test' order by v; -v -This is a test -This is a test -select * from t1 where v='This is a test' order by v; -v -This is a test -This is a test -This is a test -select * from t1 where v like 'S%' order by v; -v -Short string -Some sample data -Some samples -explain select * from t1 where v like 'This is a test' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL range v v 261 NULL 3 100.00 Using where; Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` like 'This is a test') order by `test`.`t1`.`v` -Helper line 1 for --skip_if_hypergraph diffing -explain select * from t1 where v='This is a test' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL ref v v 261 const 3 100.00 Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` = 'This is a test') order by `test`.`t1`.`v` -Helper line 2 for --skip_if_hypergraph diffing -explain select * from t1 where v like 'S%' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL range v v 261 NULL 3 100.00 Using where; Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` like 'S%') order by `test`.`t1`.`v` -alter table t1 change v v varchar(259); -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status OK -select * from t1 where v like 'This is a test' order by v; -v -This is a test -This is a test -select * from t1 where v='This is a test' order by v; -v -This is a test -This is a test -This is a test -select * from t1 where v like 'S%' order by v; -v -Short string -Some sample data -Some samples -explain select * from t1 where v like 'This is a test' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL range v v 262 NULL 3 100.00 Using where; Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` like 'This is a test') order by `test`.`t1`.`v` -Helper line 3 for --skip_if_hypergraph diffing -explain select * from t1 where v='This is a test' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL ref v v 262 const 3 100.00 Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` = 'This is a test') order by `test`.`t1`.`v` -Helper line 4 for --skip_if_hypergraph diffing -explain select * from t1 where v like 'S%' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL range v v 262 NULL 3 100.00 Using where; Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` like 'S%') order by `test`.`t1`.`v` -alter table t1 change v v varchar(258); -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status OK -select * from t1 where v like 'This is a test' order by v; -v -This is a test -This is a test -select * from t1 where v='This is a test' order by v; -v -This is a test -This is a test -This is a test -select * from t1 where v like 'S%' order by v; -v -Short string -Some sample data -Some samples -explain select * from t1 where v like 'This is a test' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL range v v 261 NULL 3 100.00 Using where; Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` like 'This is a test') order by `test`.`t1`.`v` -Helper line 3 for --skip_if_hypergraph diffing -explain select * from t1 where v='This is a test' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL ref v v 261 const 3 100.00 Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` = 'This is a test') order by `test`.`t1`.`v` -Helper line 4 for --skip_if_hypergraph diffing -explain select * from t1 where v like 'S%' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL range v v 261 NULL 3 100.00 Using where; Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` like 'S%') order by `test`.`t1`.`v` -alter table t1 change v v varchar(257); -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status OK -select * from t1 where v like 'This is a test' order by v; -v -This is a test -This is a test -select * from t1 where v='This is a test' order by v; -v -This is a test -This is a test -This is a test -select * from t1 where v like 'S%' order by v; -v -Short string -Some sample data -Some samples -explain select * from t1 where v like 'This is a test' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL range v v 260 NULL 3 100.00 Using where; Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` like 'This is a test') order by `test`.`t1`.`v` -Helper line 3 for --skip_if_hypergraph diffing -explain select * from t1 where v='This is a test' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL ref v v 260 const 3 100.00 Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` = 'This is a test') order by `test`.`t1`.`v` -Helper line 4 for --skip_if_hypergraph diffing -explain select * from t1 where v like 'S%' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL range v v 260 NULL 3 100.00 Using where; Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` like 'S%') order by `test`.`t1`.`v` -alter table t1 change v v varchar(256); -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status OK -select * from t1 where v like 'This is a test' order by v; -v -This is a test -This is a test -select * from t1 where v='This is a test' order by v; -v -This is a test -This is a test -This is a test -select * from t1 where v like 'S%' order by v; -v -Short string -Some sample data -Some samples -explain select * from t1 where v like 'This is a test' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL range v v 259 NULL 3 100.00 Using where; Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` like 'This is a test') order by `test`.`t1`.`v` -Helper line 3 for --skip_if_hypergraph diffing -explain select * from t1 where v='This is a test' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL ref v v 259 const 3 100.00 Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` = 'This is a test') order by `test`.`t1`.`v` -Helper line 4 for --skip_if_hypergraph diffing -explain select * from t1 where v like 'S%' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL range v v 259 NULL 3 100.00 Using where; Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` like 'S%') order by `test`.`t1`.`v` -alter table t1 change v v varchar(255); -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status OK -select * from t1 where v like 'This is a test' order by v; -v -This is a test -This is a test -select * from t1 where v='This is a test' order by v; -v -This is a test -This is a test -This is a test -select * from t1 where v like 'S%' order by v; -v -Short string -Some sample data -Some samples -explain select * from t1 where v like 'This is a test' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL range v v 258 NULL 3 100.00 Using where; Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` like 'This is a test') order by `test`.`t1`.`v` -Helper line 3 for --skip_if_hypergraph diffing -explain select * from t1 where v='This is a test' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL ref v v 258 const 3 100.00 Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` = 'This is a test') order by `test`.`t1`.`v` -Helper line 4 for --skip_if_hypergraph diffing -explain select * from t1 where v like 'S%' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL range v v 258 NULL 3 100.00 Using where; Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` like 'S%') order by `test`.`t1`.`v` -alter table t1 change v v varchar(254); -ANALYZE TABLE t1; -Table Op Msg_type Msg_text -test.t1 analyze status OK -select * from t1 where v like 'This is a test' order by v; -v -This is a test -This is a test -select * from t1 where v='This is a test' order by v; -v -This is a test -This is a test -This is a test -select * from t1 where v like 'S%' order by v; -v -Short string -Some sample data -Some samples -explain select * from t1 where v like 'This is a test' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL range v v 257 NULL 3 100.00 Using where; Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` like 'This is a test') order by `test`.`t1`.`v` -Helper line 3 for --skip_if_hypergraph diffing -explain select * from t1 where v='This is a test' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL ref v v 257 const 3 100.00 Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` = 'This is a test') order by `test`.`t1`.`v` -Helper line 4 for --skip_if_hypergraph diffing -explain select * from t1 where v like 'S%' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL range v v 257 NULL 3 100.00 Using where; Using index -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` like 'S%') order by `test`.`t1`.`v` -alter table t1 change v v varchar(253); -alter table t1 change v v varchar(254), drop key v; -alter table t1 change v v varchar(300), add key (v(10)); -select * from t1 where v like 'This is a test' order by v; -v -This is a test -This is a test -select * from t1 where v='This is a test' order by v; -v -This is a test -This is a test -This is a test -select * from t1 where v like 'S%' order by v; -v -Short string -Some sample data -Some samples -explain select * from t1 where v like 'This is a test' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL range v v 13 NULL 4 100.00 Using where; Using filesort -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` like 'This is a test') order by `test`.`t1`.`v` -Helper line 5 for --skip_if_hypergraph diffing -explain select * from t1 where v='This is a test' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL ref v v 13 const 4 100.00 Using where -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` = 'This is a test') order by `test`.`t1`.`v` -Helper line 6 for --skip_if_hypergraph diffing -explain select * from t1 where v like 'S%' order by v; -id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t1 NULL range v v 13 NULL 3 100.00 Using where; Using filesort -Warnings: -Note 1003 /* select#1 */ select `test`.`t1`.`v` AS `v` from `test`.`t1` where (`test`.`t1`.`v` like 'S%') order by `test`.`t1`.`v` -drop table t1; -create table t1 (pkcol varchar(16), othercol varchar(16), primary key (pkcol)); -insert into t1 values ('test', 'something'); -update t1 set othercol='somethingelse' where pkcol='test'; -select * from t1; -pkcol othercol -test somethingelse -drop table t1; -create table t1 (a int, b varchar(12)); -insert into t1 values (1, 'A'), (22, NULL); -create table t2 (a int); -insert into t2 values (22), (22); -select t1.a, t1.b, min(t1.b) from t1 inner join t2 ON t2.a = t1.a -group by t1.b, t1.a; -a b min(t1.b) -22 NULL NULL -drop table t1, t2; -create table t1 (f1 varchar(65500)) charset latin1; -create index index1 on t1(f1(10)); -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `f1` varchar(65500) DEFAULT NULL, - KEY `index1` (`f1`(10)) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 -alter table t1 modify f1 varchar(255); -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `f1` varchar(255) DEFAULT NULL, - KEY `index1` (`f1`(10)) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 -alter table t1 modify f1 tinytext; -show create table t1; -Table Create Table -t1 CREATE TABLE `t1` ( - `f1` tinytext, - KEY `index1` (`f1`(10)) -) ENGINE=InnoDB DEFAULT CHARSET=latin1 -drop table t1; -DROP TABLE IF EXISTS t1; -CREATE TABLE t1(f1 VARCHAR(100) DEFAULT 'test'); -INSERT INTO t1 VALUES(SUBSTR(f1, 1, 3)); -DROP TABLE IF EXISTS t1; -CREATE TABLE t1(f1 CHAR(100) DEFAULT 'test'); -INSERT INTO t1 VALUES(SUBSTR(f1, 1, 3)); -DROP TABLE IF EXISTS t1; -CREATE TABLE t1 (a CHAR(2)); -INSERT INTO t1 VALUES (10), (50), (30), ('1a'), (60), ('t'); -SELECT a,(a + 0) FROM t1 ORDER BY a; -a (a + 0) -10 10 -1a 1 -30 30 -50 50 -60 60 -t 0 -Warnings: -Warning 1292 Truncated incorrect DOUBLE value: '1a' -Warning 1292 Truncated incorrect DOUBLE value: 't' -SELECT a,(a DIV 2) FROM t1 ORDER BY a; -a (a DIV 2) -10 5 -1a 0 -30 15 -50 25 -60 30 -t 0 -Warnings: -Warning 1292 Truncated incorrect DECIMAL value: '1a' -Warning 1366 Incorrect DECIMAL value: '0' for column '' at row -1 -Warning 1292 Truncated incorrect DECIMAL value: 't' -SELECT a,CAST(a AS SIGNED) FROM t1 ORDER BY a; -a CAST(a AS SIGNED) -10 10 -1a 1 -30 30 -50 50 -60 60 -t 0 -Warnings: -Warning 1292 Truncated incorrect INTEGER value: '1a' -Warning 1292 Truncated incorrect INTEGER value: 't' -DROP TABLE t1; -CREATE TABLE t1 (a VARCHAR(16)); -INSERT INTO t1 VALUES ('5'), ('s'), (''); -SELECT 5 = a FROM t1; -5 = a -1 -0 -0 -Warnings: -Warning 1292 Truncated incorrect DOUBLE value: 's' -DROP TABLE t1; -CREATE TABLE t1 (a CHAR(16)); -INSERT INTO t1 VALUES ('5'), ('s'), (''); -SELECT 5 = a FROM t1; -5 = a -1 -0 -0 -Warnings: -Warning 1292 Truncated incorrect DOUBLE value: 's' -DROP TABLE t1; diff --git a/mysql-test/suite/percona/r/bug118009.result b/mysql-test/suite/percona/r/bug118009.result new file mode 100644 index 000000000000..dde7a456352e --- /dev/null +++ b/mysql-test/suite/percona/r/bug118009.result @@ -0,0 +1,744 @@ +# +# Bug#118009: SELECT query on primary key column deteriorates to full +# table scan when filter involves too long string +# +CREATE TABLE t1 ( pk VARCHAR(16) NOT NULL PRIMARY KEY ) CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +INSERT INTO t1 VALUES ('abc'), ('def'), ('ghi'), ('jkl'), ('mno'); +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze status OK +EXPLAIN +SELECT * FROM t1 WHERE pk IN ('abc', 'this_value_is_>16', 'def'); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 NULL index PRIMARY PRIMARY 66 NULL 5 60.00 Using where; Using index +Warnings: +Note 1003 /* select#1 */ select `test`.`t1`.`pk` AS `pk` from `test`.`t1` where (`test`.`t1`.`pk` in ('abc','this_value_is_>16','def')) +SELECT * FROM t1 WHERE pk IN ('abc', 'this_value_is_>16', 'def'); +pk +abc +def +EXPLAIN +SELECT * FROM t1 WHERE pk IN ('abc', 'this_value_is_>16', 'def'); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 NULL index PRIMARY PRIMARY 66 NULL 5 60.00 Using where; Using index +Warnings: +Note 1003 /* select#1 */ select `test`.`t1`.`pk` AS `pk` from `test`.`t1` where (`test`.`t1`.`pk` in ('abc','this_value_is_>16','def')) +SELECT * FROM t1 WHERE pk IN ('abc', 'this_value_is_>16', 'def'); +pk +abc +def +EXPLAIN +SELECT * FROM t1 WHERE pk IN ('this_value_is_>16'); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL NULL no matching row in const table +Warnings: +Note 1003 /* select#1 */ select NULL AS `pk` from `test`.`t1` where multiple equal('this_value_is_>16', NULL) +SELECT * FROM t1 WHERE pk IN ('this_value_is_>16'); +pk +EXPLAIN +SELECT * FROM t1 WHERE pk IN ('this_value_is_>16', 'and_so_is_this_one'); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 NULL range PRIMARY PRIMARY 66 NULL 2 100.00 Using where; Using index +Warnings: +Note 1003 /* select#1 */ select `test`.`t1`.`pk` AS `pk` from `test`.`t1` where (`test`.`t1`.`pk` in ('this_value_is_>16','and_so_is_this_one')) +SELECT * FROM t1 WHERE pk IN ('this_value_is_>16', 'and_so_is_this_one'); +pk +CREATE TABLE t2 ( a VARCHAR(1), KEY (a) ) CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +INSERT INTO t2 VALUES (NULL), ('a'), ('b'), ('æ'), ('ß'), ('s'); +INSERT INTO t2 SELECT 'x' FROM t2; +INSERT INTO t2 SELECT 'x' FROM t2; +ANALYZE TABLE t2; +Table Op Msg_type Msg_text +test.t2 analyze status OK +# Strings containing soft hyphen (U+00AD) will not match for UTF-16 +EXPLAIN +SELECT * FROM t2 WHERE a IN ('ae', concat(_utf8mb4 x'c2ad', 'b'), 'ss'); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 NULL range a a 7 NULL 3 100.00 Using where; Using index +Warnings: +Note 1003 /* select#1 */ select `test`.`t2`.`a` AS `a` from `test`.`t2` where (`test`.`t2`.`a` in ('ae',(concat(_utf8mb4'\xC2\xAD','b')),'ss')) +SELECT * FROM t2 WHERE a IN ('ae', concat(_utf8mb4 x'c2ad', 'b'), 'ss'); +a +æ +b +ß +EXPLAIN +SELECT * FROM t2 WHERE a IN (concat(_utf8mb4 x'c2ad', 'ae'), 'ss'); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 NULL range a a 7 NULL 2 100.00 Using where; Using index +Warnings: +Note 1003 /* select#1 */ select `test`.`t2`.`a` AS `a` from `test`.`t2` where (`test`.`t2`.`a` in ((concat(_utf8mb4'\xC2\xAD','ae')),'ss')) +SELECT * FROM t2 WHERE a IN (concat(_utf8mb4 x'c2ad', 'ae'), 'ss'); +a +æ +ß +EXPLAIN +SELECT * FROM t2 WHERE a IN (concat(_utf8mb4 x'c2ad', 'ae', _utf8mb4 x'c2ad'), 'ss'); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 NULL range a a 7 NULL 2 100.00 Using where; Using index +Warnings: +Note 1003 /* select#1 */ select `test`.`t2`.`a` AS `a` from `test`.`t2` where (`test`.`t2`.`a` in ((concat(_utf8mb4'\xC2\xAD','ae',_utf8mb4'\xC2\xAD')),'ss')) +SELECT * FROM t2 WHERE a IN (concat(_utf8mb4 x'c2ad', 'ae', _utf8mb4 x'c2ad'), 'ss'); +a +æ +ß +EXPLAIN +SELECT * FROM t2 WHERE a = 'ae' OR a = concat(_utf8mb4 x'c2ad', 'b') OR a = 'ss'; +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 NULL range a a 7 NULL 3 100.00 Using where; Using index +Warnings: +Note 1003 /* select#1 */ select `test`.`t2`.`a` AS `a` from `test`.`t2` where ((`test`.`t2`.`a` = 'ae') or (`test`.`t2`.`a` = (concat(_utf8mb4'\xC2\xAD','b'))) or (`test`.`t2`.`a` = 'ss')) +SELECT * FROM t2 WHERE a = 'ae' OR a = concat(_utf8mb4 x'c2ad', 'b') OR a = 'ss'; +a +æ +b +ß +# The plan is unstable in this case, however it always uses an index, and results are consistent. +EXPLAIN +SELECT * FROM t2 WHERE a IN ('ae', concat(repeat(_utf8mb4 x'c2ad', 2), 'b', repeat(_utf8mb4 x'c2ad', 2))); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 NULL X a a 7 NULL X 100.00 Using where; Using index +Warnings: +Note 1003 /* select#1 */ select `test`.`t2`.`a` AS `a` from `test`.`t2` where (`test`.`t2`.`a` in ('ae',(concat(repeat(_utf8mb4'\xC2\xAD',2),'b',repeat(_utf8mb4'\xC2\xAD',2))))) +# Adding handler statistics to show that execution is also consistent. +FLUSH STATUS; +SELECT * FROM t2 WHERE a IN ('ae', concat(repeat(_utf8mb4 x'c2ad', 2), 'b', repeat(_utf8mb4 x'c2ad', 2))); +a +æ +SHOW STATUS LIKE 'Handler_read_%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 2 +Handler_read_last 0 +Handler_read_next 1 +Handler_read_prev 0 +Handler_read_rnd 0 +Handler_read_rnd_next 0 +CREATE TABLE t3 ( a VARCHAR(768), KEY(a)) CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +INSERT INTO t3 VALUES( repeat('æ', 767) ), ( repeat('æ', 768) ); +ANALYZE TABLE t3; +Table Op Msg_type Msg_text +test.t3 analyze status OK +EXPLAIN +SELECT 1 FROM t3 WHERE a IN ( 'a', repeat('æ', 767) ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t3 NULL index a a 3075 NULL 2 100.00 Using where; Using index +Warnings: +Note 1003 /* select#1 */ select 1 AS `1` from `test`.`t3` where (`test`.`t3`.`a` in ('a',(repeat('æ',767)))) +SELECT 1 FROM t3 WHERE a IN ( 'a', repeat('æ', 767) ); +1 +1 +EXPLAIN +SELECT 1 FROM t3 WHERE a IN ( 'a', repeat('ae', 768) ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t3 NULL index a a 3075 NULL 2 100.00 Using where; Using index +Warnings: +Note 1003 /* select#1 */ select 1 AS `1` from `test`.`t3` where (`test`.`t3`.`a` in ('a',(repeat('ae',768)))) +SELECT 1 FROM t3 WHERE a IN ( 'a', repeat('ae', 768) ); +1 +1 +EXPLAIN +SELECT 1 FROM t3 WHERE a IN ( 'a', repeat('ae', 769) ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t3 NULL index a a 3075 NULL 2 100.00 Using where; Using index +Warnings: +Note 1003 /* select#1 */ select 1 AS `1` from `test`.`t3` where (`test`.`t3`.`a` in ('a',(repeat('ae',769)))) +SELECT 1 FROM t3 WHERE a IN ( 'a', repeat('ae', 769) ); +1 +CREATE TABLE t4 ( a VARCHAR(100), KEY(a(50))) CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +INSERT INTO t4 VALUES ( repeat('æ', 98) ), ( repeat('æ', 99) ), ( repeat('æ', 100) ); +INSERT INTO t4 VALUES ( concat(repeat( 'a', 50 ), repeat( 'æ', 50 )) ); +ANALYZE TABLE t4; +Table Op Msg_type Msg_text +test.t4 analyze status OK +EXPLAIN +SELECT 1 FROM t4 WHERE a IN ( 'a', repeat('æ', 98) ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t4 NULL range a a 203 NULL 4 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select 1 AS `1` from `test`.`t4` where (`test`.`t4`.`a` in ('a',(repeat('æ',98)))) +SELECT 1 FROM t4 WHERE a IN ( 'a', repeat('æ', 98) ); +1 +1 +CREATE TABLE t5 ( a VARCHAR(16383), KEY(a(768))) CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +INSERT INTO t5 VALUES( repeat('æ', 16383) ), ( repeat('æ', 16382) ), ( repeat('æ', 16381) ); +ANALYZE TABLE t5; +Table Op Msg_type Msg_text +test.t5 analyze status OK +EXPLAIN +SELECT 1 FROM t5 WHERE a IN ( 'a', repeat('æ', 16381) ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t5 NULL range a a 3075 NULL 4 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select 1 AS `1` from `test`.`t5` where (`test`.`t5`.`a` in ('a',(repeat('æ',16381)))) +SELECT 1 FROM t5 WHERE a IN ( 'a', repeat('æ', 16381) ); +1 +1 +EXPLAIN +SELECT 1 FROM t5 WHERE a IN ( 'a', repeat('æ', 16382) ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t5 NULL range a a 3075 NULL 4 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select 1 AS `1` from `test`.`t5` where (`test`.`t5`.`a` in ('a',(repeat('æ',16382)))) +SELECT 1 FROM t5 WHERE a IN ( 'a', repeat('æ', 16382) ); +1 +1 +EXPLAIN +SELECT 1 FROM t5 WHERE a IN ( 'a', repeat('æ', 16383) ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t5 NULL range a a 3075 NULL 4 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select 1 AS `1` from `test`.`t5` where (`test`.`t5`.`a` in ('a',(repeat('æ',16383)))) +SELECT 1 FROM t5 WHERE a IN ( 'a', repeat('æ', 16383) ); +1 +1 +CREATE TABLE t6 ( a VARCHAR(6), KEY(a(3)) ) CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +INSERT INTO t6 VALUES ('aaaaaa'), ('æaaaaa'), ('ææaaaa'), ('æææaaa'), ('ææææaa'), ('æææææa'), ('ææææææ'), +('😊aaaaa'), ('😊æaaaa'), ('😊😊aaaa'), ('😊😊æaaa'), ('😊😊😊aaa'); +ANALYZE TABLE t6; +Table Op Msg_type Msg_text +test.t6 analyze status OK +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', 'aaaaaa' ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t6 NULL range a a 15 NULL 2 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select `test`.`t6`.`a` AS `a` from `test`.`t6` where (`test`.`t6`.`a` in ('','aaaaaa')) +SELECT * FROM t6 WHERE a IN ( '', 'aaaaaa' ); +a +aaaaaa +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', 'æaaaaa' ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t6 NULL range a a 15 NULL 2 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select `test`.`t6`.`a` AS `a` from `test`.`t6` where (`test`.`t6`.`a` in ('','æaaaaa')) +SELECT * FROM t6 WHERE a IN ( '', 'æaaaaa' ); +a +æaaaaa +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', 'ææaaaa' ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t6 NULL range a a 15 NULL 2 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select `test`.`t6`.`a` AS `a` from `test`.`t6` where (`test`.`t6`.`a` in ('','ææaaaa')) +SELECT * FROM t6 WHERE a IN ( '', 'ææaaaa' ); +a +ææaaaa +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', 'æææaaa' ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t6 NULL range a a 15 NULL 5 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select `test`.`t6`.`a` AS `a` from `test`.`t6` where (`test`.`t6`.`a` in ('','æææaaa')) +SELECT * FROM t6 WHERE a IN ( '', 'æææaaa' ); +a +æææaaa +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', 'ææææaa' ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t6 NULL range a a 15 NULL 5 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select `test`.`t6`.`a` AS `a` from `test`.`t6` where (`test`.`t6`.`a` in ('','ææææaa')) +SELECT * FROM t6 WHERE a IN ( '', 'ææææaa' ); +a +ææææaa +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', 'æææææa' ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t6 NULL range a a 15 NULL 5 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select `test`.`t6`.`a` AS `a` from `test`.`t6` where (`test`.`t6`.`a` in ('','æææææa')) +SELECT * FROM t6 WHERE a IN ( '', 'æææææa' ); +a +æææææa +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', 'ææææææ' ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t6 NULL range a a 15 NULL 5 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select `test`.`t6`.`a` AS `a` from `test`.`t6` where (`test`.`t6`.`a` in ('','ææææææ')) +SELECT * FROM t6 WHERE a IN ( '', 'ææææææ' ); +a +ææææææ +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', '😊aaaaa'); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t6 NULL range a a 15 NULL 2 100.00 Using where +Warnings: +Warning 1300 Cannot convert string '\xF0\x9F\x98\x8Aaa...' from utf8mb4 to utf8mb3 +Note 1003 /* select#1 */ select `test`.`t6`.`a` AS `a` from `test`.`t6` where (`test`.`t6`.`a` in ('','?aaaaa')) +SELECT * FROM t6 WHERE a IN ( '', '😊aaaaa'); +a +😊aaaaa +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', '😊æaaaa'); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t6 NULL range a a 15 NULL 2 100.00 Using where +Warnings: +Warning 1300 Cannot convert string '\xF0\x9F\x98\x8A\xC3\xA6...' from utf8mb4 to utf8mb3 +Note 1003 /* select#1 */ select `test`.`t6`.`a` AS `a` from `test`.`t6` where (`test`.`t6`.`a` in ('','?æaaaa')) +SELECT * FROM t6 WHERE a IN ( '', '😊æaaaa'); +a +😊æaaaa +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', '😊😊aaaa'); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t6 NULL range a a 15 NULL 2 100.00 Using where +Warnings: +Warning 1300 Cannot convert string '\xF0\x9F\x98\x8A\xF0\x9F...' from utf8mb4 to utf8mb3 +Note 1003 /* select#1 */ select `test`.`t6`.`a` AS `a` from `test`.`t6` where (`test`.`t6`.`a` in ('','??aaaa')) +SELECT * FROM t6 WHERE a IN ( '', '😊😊aaaa'); +a +😊😊aaaa +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', '😊😊æaaa'); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t6 NULL range a a 15 NULL 2 100.00 Using where +Warnings: +Warning 1300 Cannot convert string '\xF0\x9F\x98\x8A\xF0\x9F...' from utf8mb4 to utf8mb3 +Note 1003 /* select#1 */ select `test`.`t6`.`a` AS `a` from `test`.`t6` where (`test`.`t6`.`a` in ('','??æaaa')) +SELECT * FROM t6 WHERE a IN ( '', '😊😊😊aaa'); +a +😊😊😊aaa +EXPLAIN +SELECT * FROM t6 WHERE a IN ('aaaaaa', 'æaaaaa', 'ææaaaa', 'æææaaa', 'ææææaa', 'æææææa', 'ææææææ'); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t6 NULL range a a 15 NULL 7 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select `test`.`t6`.`a` AS `a` from `test`.`t6` where (`test`.`t6`.`a` in ('aaaaaa','æaaaaa','ææaaaa','æææaaa','ææææaa','æææææa','ææææææ')) +SELECT * FROM t6 WHERE a IN ('aaaaaa', 'æaaaaa', 'ææaaaa', 'æææaaa', 'ææææaa', 'æææææa', 'ææææææ'); +a +aaaaaa +æaaaaa +ææaaaa +æææaaa +ææææaa +æææææa +ææææææ +EXPLAIN +SELECT * FROM t6 WHERE a IN +('aaaaaa', 'æaaaaa', 'ææaaaa', 'æææaaa', 'ææææaa', 'æææææa', 'ææææææ', +'😊aaaaa', '😊æaaaa', '😊😊aaaa', '😊😊æaaa', '😊😊😊aaa'); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t6 NULL ALL a NULL NULL NULL 12 100.00 Using where +Warnings: +Warning 1300 Cannot convert string '\xF0\x9F\x98\x8Aaa...' from utf8mb4 to utf8mb3 +Warning 1300 Cannot convert string '\xF0\x9F\x98\x8A\xC3\xA6...' from utf8mb4 to utf8mb3 +Warning 1300 Cannot convert string '\xF0\x9F\x98\x8A\xF0\x9F...' from utf8mb4 to utf8mb3 +Warning 1300 Cannot convert string '\xF0\x9F\x98\x8A\xF0\x9F...' from utf8mb4 to utf8mb3 +Warning 1300 Cannot convert string '\xF0\x9F\x98\x8A\xF0\x9F...' from utf8mb4 to utf8mb3 +Note 1003 /* select#1 */ select `test`.`t6`.`a` AS `a` from `test`.`t6` where (`test`.`t6`.`a` in ('aaaaaa','æaaaaa','ææaaaa','æææaaa','ææææaa','æææææa','ææææææ','?aaaaa','?æaaaa','??aaaa','??æaaa','???aaa')) +SELECT * FROM t6 WHERE a IN +('aaaaaa', 'æaaaaa', 'ææaaaa', 'æææaaa', 'ææææaa', 'æææææa', 'ææææææ', +'😊aaaaa', '😊æaaaa', '😊😊aaaa', '😊😊æaaa', '😊😊😊aaa'); +a +aaaaaa +æaaaaa +ææaaaa +æææaaa +ææææaa +æææææa +ææææææ +😊aaaaa +😊æaaaa +😊😊aaaa +😊😊æaaa +😊😊😊aaa +DROP TABLE t1, t2, t3, t4, t5, t6; +CREATE TABLE t1 ( pk VARCHAR(16) NOT NULL PRIMARY KEY ) CHARSET=utf16 COLLATE=utf16_unicode_520_ci; +INSERT INTO t1 VALUES ('abc'), ('def'), ('ghi'), ('jkl'), ('mno'); +ANALYZE TABLE t1; +Table Op Msg_type Msg_text +test.t1 analyze status OK +EXPLAIN +SELECT * FROM t1 WHERE pk IN ('abc', 'this_value_is_>16', 'def'); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 NULL index PRIMARY PRIMARY 66 NULL 5 60.00 Using where; Using index +Warnings: +Note 1003 /* select#1 */ select `test`.`t1`.`pk` AS `pk` from `test`.`t1` where (`test`.`t1`.`pk` in ('abc','this_value_is_>16','def')) +SELECT * FROM t1 WHERE pk IN ('abc', 'this_value_is_>16', 'def'); +pk +abc +def +EXPLAIN +SELECT * FROM t1 WHERE pk IN ('abc', 'this_value_is_>16', 'def'); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 NULL index PRIMARY PRIMARY 66 NULL 5 60.00 Using where; Using index +Warnings: +Note 1003 /* select#1 */ select `test`.`t1`.`pk` AS `pk` from `test`.`t1` where (`test`.`t1`.`pk` in ('abc','this_value_is_>16','def')) +SELECT * FROM t1 WHERE pk IN ('abc', 'this_value_is_>16', 'def'); +pk +abc +def +EXPLAIN +SELECT * FROM t1 WHERE pk IN ('this_value_is_>16'); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL NULL no matching row in const table +Warnings: +Note 1003 /* select#1 */ select NULL AS `pk` from `test`.`t1` where (NULL = 'this_value_is_>16') +SELECT * FROM t1 WHERE pk IN ('this_value_is_>16'); +pk +EXPLAIN +SELECT * FROM t1 WHERE pk IN ('this_value_is_>16', 'and_so_is_this_one'); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 NULL range PRIMARY PRIMARY 66 NULL 2 100.00 Using where; Using index +Warnings: +Note 1003 /* select#1 */ select `test`.`t1`.`pk` AS `pk` from `test`.`t1` where (`test`.`t1`.`pk` in ('this_value_is_>16','and_so_is_this_one')) +SELECT * FROM t1 WHERE pk IN ('this_value_is_>16', 'and_so_is_this_one'); +pk +CREATE TABLE t2 ( a VARCHAR(1), KEY (a) ) CHARSET=utf16 COLLATE=utf16_unicode_520_ci; +INSERT INTO t2 VALUES (NULL), ('a'), ('b'), ('æ'), ('ß'), ('s'); +INSERT INTO t2 SELECT 'x' FROM t2; +INSERT INTO t2 SELECT 'x' FROM t2; +ANALYZE TABLE t2; +Table Op Msg_type Msg_text +test.t2 analyze status OK +# Strings containing soft hyphen (U+00AD) will not match for UTF-16 +EXPLAIN +SELECT * FROM t2 WHERE a IN ('ae', concat(_utf8mb4 x'c2ad', 'b'), 'ss'); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 NULL range a a 7 NULL 3 100.00 Using where; Using index +Warnings: +Note 1003 /* select#1 */ select `test`.`t2`.`a` AS `a` from `test`.`t2` where (`test`.`t2`.`a` in ('ae',(convert(concat(_utf8mb4'\xC2\xAD','b') using utf16)),'ss')) +SELECT * FROM t2 WHERE a IN ('ae', concat(_utf8mb4 x'c2ad', 'b'), 'ss'); +a +æ +ß +EXPLAIN +SELECT * FROM t2 WHERE a IN (concat(_utf8mb4 x'c2ad', 'ae'), 'ss'); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 NULL range a a 7 NULL 2 100.00 Using where; Using index +Warnings: +Note 1003 /* select#1 */ select `test`.`t2`.`a` AS `a` from `test`.`t2` where (`test`.`t2`.`a` in ((convert(concat(_utf8mb4'\xC2\xAD','ae') using utf16)),'ss')) +SELECT * FROM t2 WHERE a IN (concat(_utf8mb4 x'c2ad', 'ae'), 'ss'); +a +ß +EXPLAIN +SELECT * FROM t2 WHERE a IN (concat(_utf8mb4 x'c2ad', 'ae', _utf8mb4 x'c2ad'), 'ss'); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 NULL range a a 7 NULL 2 100.00 Using where; Using index +Warnings: +Note 1003 /* select#1 */ select `test`.`t2`.`a` AS `a` from `test`.`t2` where (`test`.`t2`.`a` in ((convert(concat(_utf8mb4'\xC2\xAD','ae',_utf8mb4'\xC2\xAD') using utf16)),'ss')) +SELECT * FROM t2 WHERE a IN (concat(_utf8mb4 x'c2ad', 'ae', _utf8mb4 x'c2ad'), 'ss'); +a +ß +EXPLAIN +SELECT * FROM t2 WHERE a = 'ae' OR a = concat(_utf8mb4 x'c2ad', 'b') OR a = 'ss'; +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 NULL range a a 7 NULL 3 100.00 Using where; Using index +Warnings: +Note 1003 /* select#1 */ select `test`.`t2`.`a` AS `a` from `test`.`t2` where ((`test`.`t2`.`a` = 'ae') or (`test`.`t2`.`a` = (convert(concat(_utf8mb4'\xC2\xAD','b') using utf16))) or (`test`.`t2`.`a` = 'ss')) +SELECT * FROM t2 WHERE a = 'ae' OR a = concat(_utf8mb4 x'c2ad', 'b') OR a = 'ss'; +a +æ +ß +# The plan is unstable in this case, however it always uses an index, and results are consistent. +EXPLAIN +SELECT * FROM t2 WHERE a IN ('ae', concat(repeat(_utf8mb4 x'c2ad', 2), 'b', repeat(_utf8mb4 x'c2ad', 2))); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t2 NULL X a a 7 NULL X 100.00 Using where; Using index +Warnings: +Note 1003 /* select#1 */ select `test`.`t2`.`a` AS `a` from `test`.`t2` where (`test`.`t2`.`a` in ('ae',(convert(concat(repeat(_utf8mb4'\xC2\xAD',2),'b',repeat(_utf8mb4'\xC2\xAD',2)) using utf16)))) +# Adding handler statistics to show that execution is also consistent. +FLUSH STATUS; +SELECT * FROM t2 WHERE a IN ('ae', concat(repeat(_utf8mb4 x'c2ad', 2), 'b', repeat(_utf8mb4 x'c2ad', 2))); +a +æ +SHOW STATUS LIKE 'Handler_read_%'; +Variable_name Value +Handler_read_first 0 +Handler_read_key 2 +Handler_read_last 0 +Handler_read_next 1 +Handler_read_prev 0 +Handler_read_rnd 0 +Handler_read_rnd_next 0 +CREATE TABLE t3 ( a VARCHAR(768), KEY(a)) CHARSET=utf16 COLLATE=utf16_unicode_520_ci; +INSERT INTO t3 VALUES( repeat('æ', 767) ), ( repeat('æ', 768) ); +ANALYZE TABLE t3; +Table Op Msg_type Msg_text +test.t3 analyze status OK +EXPLAIN +SELECT 1 FROM t3 WHERE a IN ( 'a', repeat('æ', 767) ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t3 NULL index a a 3075 NULL 2 100.00 Using where; Using index +Warnings: +Note 1003 /* select#1 */ select 1 AS `1` from `test`.`t3` where (`test`.`t3`.`a` in ('a',(convert(repeat('æ',767) using utf16)))) +SELECT 1 FROM t3 WHERE a IN ( 'a', repeat('æ', 767) ); +1 +1 +EXPLAIN +SELECT 1 FROM t3 WHERE a IN ( 'a', repeat('ae', 768) ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t3 NULL index a a 3075 NULL 2 100.00 Using where; Using index +Warnings: +Note 1003 /* select#1 */ select 1 AS `1` from `test`.`t3` where (`test`.`t3`.`a` in ('a',(convert(repeat('ae',768) using utf16)))) +SELECT 1 FROM t3 WHERE a IN ( 'a', repeat('ae', 768) ); +1 +1 +EXPLAIN +SELECT 1 FROM t3 WHERE a IN ( 'a', repeat('ae', 769) ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t3 NULL index a a 3075 NULL 2 100.00 Using where; Using index +Warnings: +Note 1003 /* select#1 */ select 1 AS `1` from `test`.`t3` where (`test`.`t3`.`a` in ('a',(convert(repeat('ae',769) using utf16)))) +SELECT 1 FROM t3 WHERE a IN ( 'a', repeat('ae', 769) ); +1 +CREATE TABLE t4 ( a VARCHAR(100), KEY(a(50))) CHARSET=utf16 COLLATE=utf16_unicode_520_ci; +INSERT INTO t4 VALUES ( repeat('æ', 98) ), ( repeat('æ', 99) ), ( repeat('æ', 100) ); +INSERT INTO t4 VALUES ( concat(repeat( 'a', 50 ), repeat( 'æ', 50 )) ); +ANALYZE TABLE t4; +Table Op Msg_type Msg_text +test.t4 analyze status OK +EXPLAIN +SELECT 1 FROM t4 WHERE a IN ( 'a', repeat('æ', 98) ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t4 NULL range a a 203 NULL 4 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select 1 AS `1` from `test`.`t4` where (`test`.`t4`.`a` in ('a',(convert(repeat('æ',98) using utf16)))) +SELECT 1 FROM t4 WHERE a IN ( 'a', repeat('æ', 98) ); +1 +1 +CREATE TABLE t5 ( a VARCHAR(16383), KEY(a(768))) CHARSET=utf16 COLLATE=utf16_unicode_520_ci; +INSERT INTO t5 VALUES( repeat('æ', 16383) ), ( repeat('æ', 16382) ), ( repeat('æ', 16381) ); +ANALYZE TABLE t5; +Table Op Msg_type Msg_text +test.t5 analyze status OK +EXPLAIN +SELECT 1 FROM t5 WHERE a IN ( 'a', repeat('æ', 16381) ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t5 NULL range a a 3075 NULL 4 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select 1 AS `1` from `test`.`t5` where (`test`.`t5`.`a` in ('a',(convert(repeat('æ',16381) using utf16)))) +SELECT 1 FROM t5 WHERE a IN ( 'a', repeat('æ', 16381) ); +1 +1 +EXPLAIN +SELECT 1 FROM t5 WHERE a IN ( 'a', repeat('æ', 16382) ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t5 NULL range a a 3075 NULL 4 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select 1 AS `1` from `test`.`t5` where (`test`.`t5`.`a` in ('a',(convert(repeat('æ',16382) using utf16)))) +SELECT 1 FROM t5 WHERE a IN ( 'a', repeat('æ', 16382) ); +1 +1 +EXPLAIN +SELECT 1 FROM t5 WHERE a IN ( 'a', repeat('æ', 16383) ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t5 NULL range a a 3075 NULL 4 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select 1 AS `1` from `test`.`t5` where (`test`.`t5`.`a` in ('a',(convert(repeat('æ',16383) using utf16)))) +SELECT 1 FROM t5 WHERE a IN ( 'a', repeat('æ', 16383) ); +1 +1 +CREATE TABLE t6 ( a VARCHAR(6), KEY(a(3)) ) CHARSET=utf16 COLLATE=utf16_unicode_520_ci; +INSERT INTO t6 VALUES ('aaaaaa'), ('æaaaaa'), ('ææaaaa'), ('æææaaa'), ('ææææaa'), ('æææææa'), ('ææææææ'), +('😊aaaaa'), ('😊æaaaa'), ('😊😊aaaa'), ('😊😊æaaa'), ('😊😊😊aaa'); +ANALYZE TABLE t6; +Table Op Msg_type Msg_text +test.t6 analyze status OK +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', 'aaaaaa' ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t6 NULL range a a 15 NULL 2 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select `test`.`t6`.`a` AS `a` from `test`.`t6` where (`test`.`t6`.`a` in ('','aaaaaa')) +SELECT * FROM t6 WHERE a IN ( '', 'aaaaaa' ); +a +aaaaaa +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', 'æaaaaa' ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t6 NULL range a a 15 NULL 2 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select `test`.`t6`.`a` AS `a` from `test`.`t6` where (`test`.`t6`.`a` in ('','æaaaaa')) +SELECT * FROM t6 WHERE a IN ( '', 'æaaaaa' ); +a +æaaaaa +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', 'ææaaaa' ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t6 NULL range a a 15 NULL 2 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select `test`.`t6`.`a` AS `a` from `test`.`t6` where (`test`.`t6`.`a` in ('','ææaaaa')) +SELECT * FROM t6 WHERE a IN ( '', 'ææaaaa' ); +a +ææaaaa +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', 'æææaaa' ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t6 NULL range a a 15 NULL 5 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select `test`.`t6`.`a` AS `a` from `test`.`t6` where (`test`.`t6`.`a` in ('','æææaaa')) +SELECT * FROM t6 WHERE a IN ( '', 'æææaaa' ); +a +æææaaa +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', 'ææææaa' ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t6 NULL range a a 15 NULL 5 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select `test`.`t6`.`a` AS `a` from `test`.`t6` where (`test`.`t6`.`a` in ('','ææææaa')) +SELECT * FROM t6 WHERE a IN ( '', 'ææææaa' ); +a +ææææaa +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', 'æææææa' ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t6 NULL range a a 15 NULL 5 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select `test`.`t6`.`a` AS `a` from `test`.`t6` where (`test`.`t6`.`a` in ('','æææææa')) +SELECT * FROM t6 WHERE a IN ( '', 'æææææa' ); +a +æææææa +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', 'ææææææ' ); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t6 NULL range a a 15 NULL 5 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select `test`.`t6`.`a` AS `a` from `test`.`t6` where (`test`.`t6`.`a` in ('','ææææææ')) +SELECT * FROM t6 WHERE a IN ( '', 'ææææææ' ); +a +ææææææ +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', '😊aaaaa'); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t6 NULL range a a 15 NULL 2 100.00 Using where +Warnings: +Warning 1300 Cannot convert string '\xD8\x3D\xDE\x0A\x00\x61...' from utf16 to utf8mb3 +Note 1003 /* select#1 */ select `test`.`t6`.`a` AS `a` from `test`.`t6` where (`test`.`t6`.`a` in ('','?aaaaa')) +SELECT * FROM t6 WHERE a IN ( '', '😊aaaaa'); +a +😊aaaaa +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', '😊æaaaa'); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t6 NULL range a a 15 NULL 2 100.00 Using where +Warnings: +Warning 1300 Cannot convert string '\xD8\x3D\xDE\x0A\x00\xE6...' from utf16 to utf8mb3 +Note 1003 /* select#1 */ select `test`.`t6`.`a` AS `a` from `test`.`t6` where (`test`.`t6`.`a` in ('','?æaaaa')) +SELECT * FROM t6 WHERE a IN ( '', '😊æaaaa'); +a +😊æaaaa +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', '😊😊aaaa'); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t6 NULL range a a 15 NULL 2 100.00 Using where +Warnings: +Warning 1300 Cannot convert string '\xD8\x3D\xDE\x0A\xD8\x3D...' from utf16 to utf8mb3 +Note 1003 /* select#1 */ select `test`.`t6`.`a` AS `a` from `test`.`t6` where (`test`.`t6`.`a` in ('','??aaaa')) +SELECT * FROM t6 WHERE a IN ( '', '😊😊aaaa'); +a +😊😊aaaa +EXPLAIN +SELECT * FROM t6 WHERE a IN ( '', '😊😊æaaa'); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t6 NULL range a a 15 NULL 2 100.00 Using where +Warnings: +Warning 1300 Cannot convert string '\xD8\x3D\xDE\x0A\xD8\x3D...' from utf16 to utf8mb3 +Note 1003 /* select#1 */ select `test`.`t6`.`a` AS `a` from `test`.`t6` where (`test`.`t6`.`a` in ('','??æaaa')) +SELECT * FROM t6 WHERE a IN ( '', '😊😊😊aaa'); +a +😊😊😊aaa +EXPLAIN +SELECT * FROM t6 WHERE a IN ('aaaaaa', 'æaaaaa', 'ææaaaa', 'æææaaa', 'ææææaa', 'æææææa', 'ææææææ'); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t6 NULL range a a 15 NULL 7 100.00 Using where +Warnings: +Note 1003 /* select#1 */ select `test`.`t6`.`a` AS `a` from `test`.`t6` where (`test`.`t6`.`a` in ('aaaaaa','æaaaaa','ææaaaa','æææaaa','ææææaa','æææææa','ææææææ')) +SELECT * FROM t6 WHERE a IN ('aaaaaa', 'æaaaaa', 'ææaaaa', 'æææaaa', 'ææææaa', 'æææææa', 'ææææææ'); +a +aaaaaa +æaaaaa +ææaaaa +æææaaa +ææææaa +æææææa +ææææææ +EXPLAIN +SELECT * FROM t6 WHERE a IN +('aaaaaa', 'æaaaaa', 'ææaaaa', 'æææaaa', 'ææææaa', 'æææææa', 'ææææææ', +'😊aaaaa', '😊æaaaa', '😊😊aaaa', '😊😊æaaa', '😊😊😊aaa'); +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t6 NULL ALL a NULL NULL NULL 12 100.00 Using where +Warnings: +Warning 1300 Cannot convert string '\xD8\x3D\xDE\x0A\x00\x61...' from utf16 to utf8mb3 +Warning 1300 Cannot convert string '\xD8\x3D\xDE\x0A\x00\xE6...' from utf16 to utf8mb3 +Warning 1300 Cannot convert string '\xD8\x3D\xDE\x0A\xD8\x3D...' from utf16 to utf8mb3 +Warning 1300 Cannot convert string '\xD8\x3D\xDE\x0A\xD8\x3D...' from utf16 to utf8mb3 +Warning 1300 Cannot convert string '\xD8\x3D\xDE\x0A\xD8\x3D...' from utf16 to utf8mb3 +Note 1003 /* select#1 */ select `test`.`t6`.`a` AS `a` from `test`.`t6` where (`test`.`t6`.`a` in ('aaaaaa','æaaaaa','ææaaaa','æææaaa','ææææaa','æææææa','ææææææ','?aaaaa','?æaaaa','??aaaa','??æaaa','???aaa')) +SELECT * FROM t6 WHERE a IN +('aaaaaa', 'æaaaaa', 'ææaaaa', 'æææaaa', 'ææææaa', 'æææææa', 'ææææææ', +'😊aaaaa', '😊æaaaa', '😊😊aaaa', '😊😊æaaa', '😊😊😊aaa'); +a +aaaaaa +æaaaaa +ææaaaa +æææaaa +ææææaa +æææææa +ææææææ +😊aaaaa +😊æaaaa +😊😊aaaa +😊😊æaaa +😊😊😊aaa +DROP TABLE t1, t2, t3, t4, t5, t6; +CREATE TABLE t1 ( +a VARCHAR(3), +KEY(a) +); +INSERT INTO t1 VALUES ('aaa'), ('bbb'), ('ccc'); +CREATE FUNCTION bad_str() RETURNS VARBINARY(10) DETERMINISTIC NO SQL +BEGIN +RETURN concat('aaaaaaaaaa', UNHEX('73C080')); # invalid overlong UTF-8 NUL +END // +SELECT * FROM t1 WHERE a < bad_str(); +ERROR 22001: Data too long for column 'bad_str' at row 1 +DROP FUNCTION bad_str; +DROP TABLE t1; +SET NAMES utf8 COLLATE utf8_general_ci; +Warnings: +Warning 3719 'utf8' is currently an alias for the character set UTF8MB3, but will be an alias for UTF8MB4 in a future release. Please consider using UTF8MB4 in order to be unambiguous. +Warning 3778 'utf8mb3_general_ci' is a collation of the deprecated character set UTF8MB3. Please consider using UTF8MB4 with an appropriate collation instead. +CREATE TABLE t1 ( +a VARCHAR(2) CHARACTER SET utf8mb3, +KEY ( a ) +); +Warnings: +Warning 1287 'utf8mb3' is deprecated and will be removed in a future release. Please use utf8mb4 instead +INSERT INTO t1 VALUES ( 'a' ), ( 'b' ); +EXPLAIN +SELECT a FROM t1 WHERE a < 'a😊'; +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 NULL index a a 9 NULL 2 50.00 Using where; Using index +Warnings: +Warning 1300 Invalid utf8mb3 character string: 'F09F98' +Warning 1366 Incorrect string value: '\xF0\x9F\x98\x8A' for column 'a' at row 1 +Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` where (`test`.`t1`.`a` < 'a😊') +SELECT a FROM t1 WHERE a < 'a😊'; +a +a +Warnings: +Warning 1300 Invalid utf8mb3 character string: 'F09F98' +Warning 1366 Incorrect string value: '\xF0\x9F\x98\x8A' for column 'a' at row 1 +EXPLAIN +SELECT a FROM t1 FORCE KEY ( a ) WHERE a < CHAR( 'a😊' USING utf8mb3 ) ORDER BY a; +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE t1 NULL range a a 9 NULL 1 100.00 Using where; Using index +Warnings: +Warning 1287 'utf8mb3' is deprecated and will be removed in a future release. Please use utf8mb4 instead +Warning 1300 Invalid utf8mb3 character string: 'F09F98' +Warning 1292 Truncated incorrect INTEGER value: 'a????' +Warning 1292 Truncated incorrect INTEGER value: 'a????' +Note 1003 /* select#1 */ select `test`.`t1`.`a` AS `a` from `test`.`t1` FORCE INDEX (`a`) where (`test`.`t1`.`a` < (char('a😊'))) order by `test`.`t1`.`a` +SELECT a FROM t1 FORCE KEY ( a ) WHERE a < CHAR( 'a😊' USING utf8mb3 ) ORDER BY a; +a +Warnings: +Warning 1287 'utf8mb3' is deprecated and will be removed in a future release. Please use utf8mb4 instead +Warning 1300 Invalid utf8mb3 character string: 'F09F98' +Warning 1292 Truncated incorrect INTEGER value: 'a????' +Warning 1292 Truncated incorrect INTEGER value: 'a????' +DROP TABLE t1; diff --git a/mysql-test/suite/percona/t/bug118009.test b/mysql-test/suite/percona/t/bug118009.test new file mode 100644 index 000000000000..a15d0edd7f30 --- /dev/null +++ b/mysql-test/suite/percona/t/bug118009.test @@ -0,0 +1,54 @@ +--echo # +--echo # Bug#118009: SELECT query on primary key column deteriorates to full +--echo # table scan when filter involves too long string +--echo # + +--let charset=utf8mb4 +--let collation=utf8mb4_0900_ai_ci +--source include/oversized_varchar_key.inc + +--let charset=utf16 +--let collation=utf16_unicode_520_ci +--source include/oversized_varchar_key.inc + +CREATE TABLE t1 ( + a VARCHAR(3), + KEY(a) +); + +INSERT INTO t1 VALUES ('aaa'), ('bbb'), ('ccc'); + +DELIMITER //; + +CREATE FUNCTION bad_str() RETURNS VARBINARY(10) DETERMINISTIC NO SQL +BEGIN + RETURN concat('aaaaaaaaaa', UNHEX('73C080')); # invalid overlong UTF-8 NUL +END // + +DELIMITER ;// + +--error ER_DATA_TOO_LONG +SELECT * FROM t1 WHERE a < bad_str(); + +DROP FUNCTION bad_str; +DROP TABLE t1; + +SET NAMES utf8 COLLATE utf8_general_ci; + + +CREATE TABLE t1 ( + a VARCHAR(2) CHARACTER SET utf8mb3, + KEY ( a ) +); + +INSERT INTO t1 VALUES ( 'a' ), ( 'b' ); + +EXPLAIN +SELECT a FROM t1 WHERE a < 'a😊'; +SELECT a FROM t1 WHERE a < 'a😊'; + +EXPLAIN +SELECT a FROM t1 FORCE KEY ( a ) WHERE a < CHAR( 'a😊' USING utf8mb3 ) ORDER BY a; +SELECT a FROM t1 FORCE KEY ( a ) WHERE a < CHAR( 'a😊' USING utf8mb3 ) ORDER BY a; + +DROP TABLE t1; diff --git a/mysql-test/t/type_varchar.test b/mysql-test/t/type_varchar.test deleted file mode 100644 index 9a52ae98ab76..000000000000 --- a/mysql-test/t/type_varchar.test +++ /dev/null @@ -1,180 +0,0 @@ ---disable_query_log -call mtr.add_suppression(".InnoDB. Compare_key: table=.*, index=.*"); ---enable_query_log - ---disable_warnings -drop table if exists t1, t2; ---enable_warnings - -create table t1 (v varchar(30), c char(3), e enum('abc','def','ghi'), t text); -show create table t1; -insert into t1 values ('abc', 'de', 'ghi', 'jkl'); -insert into t1 values ('abc ', 'de ', 'ghi', 'jkl '); -insert into t1 values ('abc ', 'd ', 'ghi', 'jkl '); -select length(v),length(c),length(e),length(t) from t1; -drop table t1; - -create table t1 (v varchar(20)) charset latin1; -insert into t1 values('a '); -select v='a' from t1; -select binary v='a' from t1; -select binary v='a ' from t1; -insert into t1 values('a'); ---error ER_DUP_ENTRY -alter table t1 add primary key (v); -drop table t1; -create table t1 (v varbinary(20)); -insert into t1 values('a'); -insert into t1 values('a '); -alter table t1 add primary key (v); -drop table t1; - -# -# Test with varchar of lengths 254,255,256,258 & 258 to ensure we do not -# have any problems with varchar with one or two byte length_bytes -# - -create table t1 (v varchar(254), index (v)) charset latin1; -insert into t1 values ("This is a test "); -insert into t1 values ("Some sample data"); -insert into t1 values (" garbage "); -insert into t1 values (" This is a test "); -insert into t1 values ("This is a test"); -insert into t1 values ("Hello world"); -insert into t1 values ("Foo bar"); -insert into t1 values ("This is a test"); -insert into t1 values ("MySQL varchar test"); -insert into t1 values ("test MySQL varchar"); -insert into t1 values ("This is a long string to have some random length data included"); -insert into t1 values ("Short string"); -insert into t1 values ("VSS"); -insert into t1 values ("Some samples"); -insert into t1 values ("Bar foo"); -insert into t1 values ("Bye"); -let $i= 255; -let $j= 5; -while ($j) -{ - ANALYZE TABLE t1; - select * from t1 where v like 'This is a test' order by v; - select * from t1 where v='This is a test' order by v; - select * from t1 where v like 'S%' order by v; - explain select * from t1 where v like 'This is a test' order by v; - --echo Helper line 1 for --skip_if_hypergraph diffing - explain select * from t1 where v='This is a test' order by v; - --echo Helper line 2 for --skip_if_hypergraph diffing - explain select * from t1 where v like 'S%' order by v; - eval alter table t1 change v v varchar($i); - inc $i; - dec $j; -} -let $i= 258; -let $j= 6; -while ($j) -{ - ANALYZE TABLE t1; - select * from t1 where v like 'This is a test' order by v; - select * from t1 where v='This is a test' order by v; - select * from t1 where v like 'S%' order by v; - explain select * from t1 where v like 'This is a test' order by v; - --echo Helper line 3 for --skip_if_hypergraph diffing - explain select * from t1 where v='This is a test' order by v; - --echo Helper line 4 for --skip_if_hypergraph diffing - explain select * from t1 where v like 'S%' order by v; - eval alter table t1 change v v varchar($i); - dec $i; - dec $j; -} -alter table t1 change v v varchar(254), drop key v; - -# Test with length(varchar) > 256 and key < 256 (to ensure things works with -# different kind of packing - -alter table t1 change v v varchar(300), add key (v(10)); -select * from t1 where v like 'This is a test' order by v; -select * from t1 where v='This is a test' order by v; -select * from t1 where v like 'S%' order by v; -explain select * from t1 where v like 'This is a test' order by v; ---echo Helper line 5 for --skip_if_hypergraph diffing -explain select * from t1 where v='This is a test' order by v; ---echo Helper line 6 for --skip_if_hypergraph diffing -explain select * from t1 where v like 'S%' order by v; -drop table t1; - -# -# bug#9339 - meaningless Field_varstring::get_key_image -# -create table t1 (pkcol varchar(16), othercol varchar(16), primary key (pkcol)); -insert into t1 values ('test', 'something'); -update t1 set othercol='somethingelse' where pkcol='test'; -select * from t1; -drop table t1; - -# -# Bug #9489: problems with key handling -# - -create table t1 (a int, b varchar(12)); -insert into t1 values (1, 'A'), (22, NULL); -create table t2 (a int); -insert into t2 values (22), (22); -select t1.a, t1.b, min(t1.b) from t1 inner join t2 ON t2.a = t1.a - group by t1.b, t1.a; -drop table t1, t2; - -# -# Bug #10543: convert varchar with index to text -# -create table t1 (f1 varchar(65500)) charset latin1; -create index index1 on t1(f1(10)); -show create table t1; -alter table t1 modify f1 varchar(255); -show create table t1; -alter table t1 modify f1 tinytext; -show create table t1; -drop table t1; - -# -# BUG#15588: String overrun -# - ---disable_warnings -DROP TABLE IF EXISTS t1; ---enable_warnings - -CREATE TABLE t1(f1 VARCHAR(100) DEFAULT 'test'); -INSERT INTO t1 VALUES(SUBSTR(f1, 1, 3)); -DROP TABLE IF EXISTS t1; - -CREATE TABLE t1(f1 CHAR(100) DEFAULT 'test'); -INSERT INTO t1 VALUES(SUBSTR(f1, 1, 3)); -DROP TABLE IF EXISTS t1; - - -# -# Bug #11927: Warnings shown for CAST( chr as signed) but not (chr + 0) -# -CREATE TABLE t1 (a CHAR(2)); -INSERT INTO t1 VALUES (10), (50), (30), ('1a'), (60), ('t'); -SELECT a,(a + 0) FROM t1 ORDER BY a; -SELECT a,(a DIV 2) FROM t1 ORDER BY a; -SELECT a,CAST(a AS SIGNED) FROM t1 ORDER BY a; -DROP TABLE t1; - -# -# Bug #28299: To-number conversion warnings work differenly with CHAR -# and VARCHAR sp variables -# -# * Verify that 'Truncated incorrect DOUBLE value' is shown for 's' -# when using both CHAR and VARCHAR. -# - -CREATE TABLE t1 (a VARCHAR(16)); -INSERT INTO t1 VALUES ('5'), ('s'), (''); -SELECT 5 = a FROM t1; -DROP TABLE t1; - -CREATE TABLE t1 (a CHAR(16)); -INSERT INTO t1 VALUES ('5'), ('s'), (''); -SELECT 5 = a FROM t1; -DROP TABLE t1; diff --git a/sql/range_optimizer/range_analysis.cc b/sql/range_optimizer/range_analysis.cc index d2c981330784..331b04ff9ab9 100644 --- a/sql/range_optimizer/range_analysis.cc +++ b/sql/range_optimizer/range_analysis.cc @@ -24,6 +24,7 @@ #include #include #include +#include #include "field_types.h" #include "memory_debugging.h" @@ -70,6 +71,8 @@ #include "sql_string.h" #include "template_utils.h" +bool is_prefix_index(TABLE *table, uint idx); + /* A null_sel_tree is used in get_func_mm_tree_from_in_predicate to pass as an argument to tree_or. It is used only to influence the return @@ -1392,6 +1395,76 @@ static bool save_value_and_handle_conversion( return true; } +/** + Creates a search key for a value that may be larger than the index it + searches. + + The code inside save_value_and_handle_conversion() cannot create a + search key if the value is larger than the declared size of the index. This + happens because the code instantiating search keys is tied to the Field + class, which doesn't allocate room for keys larger than the index. For unicode + strings, there is no limit for how large the search key can be. For instance, + multiple character can match a single character, for example "ae" can match + "æ" in many collations. A more contrived example is that any number of SMALL + HYPHEN (U+00AD) can be in the search string as they are ignored in string + matching. + + If any one search key cannot be created, the optimizer falls back + to table scan. This function act as a second chance, allocating as large a + buffer as needed and creates the search key there instead. Since the scenario + can only happen for strings, the procedure is much simpler than for arbitrary + types. + + @param value The search value. + @param field The column the value is being compared against. + @param key_part The index column for the index being considered. + @param alloc MEM_ROOT used to allocate the key buffer from. + + @return Pointer to the key buffer, or nullptr if the search key could not be + created. +*/ +static uchar *make_oversized_search_key(Item *value, const Field *field, + const KEY_PART *key_part, + MEM_ROOT *alloc) { + const size_t null_bytes = field->is_nullable() ? 1 : 0; + const size_t size_bytes = 2; + String buf; + auto *str_value = value->val_str(&buf); + if (str_value == nullptr) return nullptr; + + const CHARSET_INFO *cs = field->charset(); + size_t key_len = + cs->coll->strnxfrmlen(cs, str_value->length() * cs->mbmaxlen); + auto *key_buf = + static_cast(alloc->Alloc(null_bytes + size_bytes + key_len)); + if (key_buf == nullptr) return nullptr; + + int nchars = is_prefix_index(field->table, key_part->key) + ? key_part->length / cs->mbmaxlen + : key_len / cs->mbmaxlen; + + const char *well_formed_error_pos; + const char *cannot_convert_error_pos; + const char *from_end_pos; + + auto num_bytes_in_key = well_formed_copy_nchars( + cs, key_buf + null_bytes + size_bytes, str_value->length(), cs, + str_value->ptr(), str_value->length(), nchars, &well_formed_error_pos, + &cannot_convert_error_pos, &from_end_pos); + + if (well_formed_error_pos != nullptr || cannot_convert_error_pos != nullptr) { + return nullptr; + } + + if (field->is_nullable()) { + key_buf[0] = char{field->is_real_null()}; + } + + int2store(key_buf + null_bytes, num_bytes_in_key); + + return reinterpret_cast(key_buf); +} + static SEL_ROOT *get_mm_leaf(THD *thd, RANGE_OPT_PARAM *param, Item *cond_func, Field *field, KEY_PART *key_part, Item_func::Functype type, Item *value, @@ -1400,7 +1473,7 @@ static SEL_ROOT *get_mm_leaf(THD *thd, RANGE_OPT_PARAM *param, Item *cond_func, bool optimize_range; SEL_ROOT *tree = nullptr; MEM_ROOT *const alloc = param->temp_mem_root; - uchar *str; + uchar *str{nullptr}; const char *impossible_cond_cause = nullptr; DBUG_TRACE; @@ -1584,6 +1657,17 @@ static SEL_ROOT *get_mm_leaf(THD *thd, RANGE_OPT_PARAM *param, Item *cond_func, &tree, value, type, field, &impossible_cond_cause, alloc, param->query_block, inexact); + if (always_true_or_false && *inexact && is_string_type(field->type()) && + (!is_prefix_index(field->table, key_part->key) || + type == Item_func::EQ_FUNC)) { + // The handler cannot currently handle non-equality ranges on prefix + // indexes. + str = make_oversized_search_key(value, field, key_part, alloc); + if (str != nullptr) { + always_true_or_false = false; + } + } + if (field->type() == MYSQL_TYPE_GEOMETRY && save_geom_type != Field::GEOM_GEOMETRY) { down_cast(field)->geom_type = save_geom_type; @@ -1602,12 +1686,15 @@ static SEL_ROOT *get_mm_leaf(THD *thd, RANGE_OPT_PARAM *param, Item *cond_func, goto end; } - str = (uchar *)alloc->Alloc(key_part->store_length + 1); - if (!str) goto end; - if (field->is_nullable()) - *str = (uchar)field->is_real_null(); // Set to 1 if null - field->get_key_image(str + null_bytes, key_part->length, - key_part->image_type); + if (str == nullptr) { + // Create the search key if we didn't already. + str = (uchar *)alloc->Alloc(key_part->store_length + 1); + if (!str) goto end; + if (field->is_nullable()) + *str = (uchar)field->is_real_null(); // Set to 1 if null + field->get_key_image(str + null_bytes, key_part->length, + key_part->image_type); + } SEL_ARG *root; root = new (alloc) SEL_ARG(field, str, str, !(key_part->flag & HA_REVERSE_SORT));