From 1f867a140951652eeaa1d75c61c7d4b754280c06 Mon Sep 17 00:00:00 2001 From: DerZc <798604270@qq.com> Date: Mon, 28 Sep 2026 15:37:27 +0800 Subject: [PATCH] Bug#120580: Keep ignorable prefix characters in string range candidates A partial key containing only primary-ignorable characters can sort below its complete value. For the affected utf8mb4_0900_ai_ci prefix range, retain all non-NULL candidates and recheck the full comparison. Update the MyISAM and InnoDB ICP EXPLAIN expectations for the widened scan bounds; query-result rows remain unchanged. Add native regressions for the reported query, equivalent controls, and repeated prepared-statement execution. Preserve observable results across the affected execution paths. Bug report: https://bugs.mysql.com/bug.php?id=120580 --- mysql-test/r/bug_120580.result | 235 ++++++++++++++++++++++++++ mysql-test/r/innodb_icp.result | 6 +- mysql-test/r/myisam_icp.result | 6 +- mysql-test/t/bug_120580.test | 143 ++++++++++++++++ sql/range_optimizer/range_analysis.cc | 25 ++- 5 files changed, 407 insertions(+), 8 deletions(-) create mode 100644 mysql-test/r/bug_120580.result create mode 100644 mysql-test/t/bug_120580.test diff --git a/mysql-test/r/bug_120580.result b/mysql-test/r/bug_120580.result new file mode 100644 index 000000000000..58b9cbc3c685 --- /dev/null +++ b/mysql-test/r/bug_120580.result @@ -0,0 +1,235 @@ +# +# Bug#120580 Regression coverage +# +SET @save_optimizer_switch=@@optimizer_switch; +SET @save_sql_mode=@@sql_mode; +SET @save_default_storage_engine=@@default_storage_engine; +SET @save_character_set_client=@@character_set_client; +SET @save_character_set_results=@@character_set_results; +SET @save_character_set_connection=@@character_set_connection; +SET @save_collation_connection=@@collation_connection; +CREATE DATABASE bug_120580 CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +USE bug_120580; +SET sql_mode=DEFAULT; +SET optimizer_switch=DEFAULT; +SET default_storage_engine=InnoDB; +SET NAMES utf8mb4 COLLATE utf8mb4_general_ci; +SET NAMES utf8mb4; +CREATE TABLE t0 (c0 LONGTEXT,KEY i0(c0(1))) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +INSERT INTO t0(c0) VALUES ('꙰dKc%z'); +REPLACE INTO t0(c0) VALUES ('kQ'); +SELECT ALL t0.c0 FROM t0 WHERE 'CZ'<=t0.c0; +c0 +kQ +꙰dKc%z +PREPARE regression_stmt FROM 'SELECT ALL t0.c0 FROM t0 WHERE ''CZ''<=t0.c0;'; +EXECUTE regression_stmt; +c0 +kQ +꙰dKc%z +EXECUTE regression_stmt; +c0 +kQ +꙰dKc%z +DEALLOCATE PREPARE regression_stmt; +SELECT ALL t0.c0 FROM t0 WHERE NOT (NOT ('CZ' <= t0.c0)); +c0 +kQ +꙰dKc%z +SELECT ALL t0.c0 FROM t0 FORCE INDEX (i0) WHERE 'CZ' <= t0.c0; +c0 +kQ +꙰dKc%z +SELECT ALL d.c0 FROM (SELECT t0.c0 FROM t0) AS d WHERE 'CZ' <= d.c0; +c0 +kQ +꙰dKc%z +SELECT ALL d.c0 FROM (SELECT t0.c0, 'CZ' <= t0.c0 AS matches_filter FROM t0) AS d WHERE d.matches_filter; +c0 +kQ +꙰dKc%z +SELECT ALL t0.c0 FROM t0 WHERE 'CZ' <= t0.c0 AND t0.c0 IS NULL UNION ALL SELECT ALL t0.c0 FROM t0 WHERE 'CZ' <= t0.c0 AND NOT (t0.c0 IS NULL) UNION ALL SELECT ALL t0.c0 FROM t0 WHERE 'CZ' <= t0.c0 AND (t0.c0 IS NULL) IS NULL; +c0 +kQ +꙰dKc%z +SELECT ALL t0.c0 FROM t0 WHERE CASE WHEN 'CZ' <= t0.c0 THEN 'CZ' <= t0.c0 ELSE 'CZ' <= t0.c0 END; +c0 +kQ +꙰dKc%z +SELECT ALL t0.c0 FROM t0 IGNORE INDEX (i0) WHERE 'CZ' <= t0.c0; +c0 +kQ +꙰dKc%z +SELECT ALL t0.c0 FROM t0 WHERE ((('CZ' <= t0.c0) OR NOT ('CZ' <= t0.c0)) OR ('CZ' <= t0.c0) IS NULL) AND ('CZ' <= t0.c0); +c0 +kQ +꙰dKc%z +SELECT ALL t0.c0 FROM t0 WHERE ((('CZ' <= t0.c0) AND NOT ('CZ' <= t0.c0)) AND ('CZ' <= t0.c0) IS NOT NULL) OR ('CZ' <= t0.c0); +c0 +kQ +꙰dKc%z +SELECT ALL t0.c0 FROM t0 WHERE t0.c0 >= 'CZ'; +c0 +kQ +꙰dKc%z +SELECT ALL t0.c0 FROM t0 WHERE NOT NOT ('CZ' <= t0.c0); +c0 +kQ +꙰dKc%z +SELECT ALL d.c0 FROM (SELECT t0.c0 AS c0, ('CZ' <= t0.c0) AS p FROM t0) AS d WHERE d.p; +c0 +kQ +꙰dKc%z +SELECT ALL t0.c0 FROM t0 WHERE 'CZ' <= t0.c0 AND t0.c0 = 'kQ' UNION ALL SELECT ALL t0.c0 FROM t0 WHERE 'CZ' <= t0.c0 AND NOT (t0.c0 = 'kQ') UNION ALL SELECT ALL t0.c0 FROM t0 WHERE 'CZ' <= t0.c0 AND (t0.c0 = 'kQ') IS NULL; +c0 +kQ +꙰dKc%z +USE test; +DROP DATABASE bug_120580; +CREATE DATABASE bug_120580 CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +USE bug_120580; +SET sql_mode=DEFAULT; +SET optimizer_switch=DEFAULT; +SET default_storage_engine=InnoDB; +SET NAMES utf8mb4 COLLATE utf8mb4_general_ci; +CREATE TABLE t_a (id INT,c VARCHAR(64) COLLATE utf8mb4_0900_ai_ci ,KEY k(c(1))) ENGINE=MyISAM; +INSERT INTO t_a VALUES (1,'A'),(2,'-804207268'),(3,'ٛRIRS/*'),(4,''),(5,NULL); +CREATE TABLE t_b (id INT,c VARCHAR(64) COLLATE utf8mb4_0900_ai_ci NOT NULL ,KEY k(c(1))) ENGINE=InnoDB; +INSERT INTO t_b VALUES (1,'A'),(2,'-804207268'),(3,'ٛRIRS/*'),(4,''); +CREATE TABLE t_c (id INT,c VARCHAR(64) COLLATE utf8mb4_0900_ai_ci ,KEY k(c(1))) ENGINE=InnoDB; +INSERT INTO t_c VALUES (1,'A'),(2,'-804207268'),(3,'ٛRIRS/*'),(4,''),(5,NULL); +CREATE TABLE t_d (id INT,c VARCHAR(64) COLLATE utf8mb4_bin ,KEY k(c(1))) ENGINE=InnoDB; +INSERT INTO t_d VALUES (1,'A'),(2,'-804207268'),(3,'ٛRIRS/*'),(4,''),(5,NULL); +SELECT id FROM t_a FORCE INDEX(k) WHERE c>'-804207268'; +id +1 +3 +PREPARE regression_stmt FROM 'SELECT id FROM t_a FORCE INDEX(k) WHERE c>''-804207268'';'; +EXECUTE regression_stmt; +id +1 +3 +EXECUTE regression_stmt; +id +1 +3 +DEALLOCATE PREPARE regression_stmt; +SELECT id FROM t_a FORCE INDEX(k) WHERE c>='-804207268'; +id +1 +2 +3 +PREPARE regression_stmt FROM 'SELECT id FROM t_a FORCE INDEX(k) WHERE c>=''-804207268'';'; +EXECUTE regression_stmt; +id +1 +2 +3 +EXECUTE regression_stmt; +id +1 +2 +3 +DEALLOCATE PREPARE regression_stmt; +SELECT id FROM t_b FORCE INDEX(k) WHERE c>'-804207268'; +id +1 +3 +PREPARE regression_stmt FROM 'SELECT id FROM t_b FORCE INDEX(k) WHERE c>''-804207268'';'; +EXECUTE regression_stmt; +id +1 +3 +EXECUTE regression_stmt; +id +1 +3 +DEALLOCATE PREPARE regression_stmt; +SELECT id FROM t_b FORCE INDEX(k) WHERE c>='-804207268'; +id +1 +2 +3 +PREPARE regression_stmt FROM 'SELECT id FROM t_b FORCE INDEX(k) WHERE c>=''-804207268'';'; +EXECUTE regression_stmt; +id +1 +2 +3 +EXECUTE regression_stmt; +id +1 +2 +3 +DEALLOCATE PREPARE regression_stmt; +SELECT id FROM t_c FORCE INDEX(k) WHERE c>'-804207268'; +id +1 +3 +PREPARE regression_stmt FROM 'SELECT id FROM t_c FORCE INDEX(k) WHERE c>''-804207268'';'; +EXECUTE regression_stmt; +id +1 +3 +EXECUTE regression_stmt; +id +1 +3 +DEALLOCATE PREPARE regression_stmt; +SELECT id FROM t_c FORCE INDEX(k) WHERE c>='-804207268'; +id +1 +2 +3 +PREPARE regression_stmt FROM 'SELECT id FROM t_c FORCE INDEX(k) WHERE c>=''-804207268'';'; +EXECUTE regression_stmt; +id +1 +2 +3 +EXECUTE regression_stmt; +id +1 +2 +3 +DEALLOCATE PREPARE regression_stmt; +SELECT id FROM t_d FORCE INDEX(k) WHERE c>'-804207268'; +id +1 +3 +PREPARE regression_stmt FROM 'SELECT id FROM t_d FORCE INDEX(k) WHERE c>''-804207268'';'; +EXECUTE regression_stmt; +id +1 +3 +EXECUTE regression_stmt; +id +1 +3 +DEALLOCATE PREPARE regression_stmt; +SELECT id FROM t_d FORCE INDEX(k) WHERE c>='-804207268'; +id +1 +2 +3 +PREPARE regression_stmt FROM 'SELECT id FROM t_d FORCE INDEX(k) WHERE c>=''-804207268'';'; +EXECUTE regression_stmt; +id +1 +2 +3 +EXECUTE regression_stmt; +id +1 +2 +3 +DEALLOCATE PREPARE regression_stmt; +USE test; +DROP DATABASE bug_120580; +SET optimizer_switch=@save_optimizer_switch; +SET sql_mode=@save_sql_mode; +SET default_storage_engine=@save_default_storage_engine; +SET character_set_client=@save_character_set_client; +SET character_set_results=@save_character_set_results; +SET character_set_connection=@save_character_set_connection; +SET collation_connection=@save_collation_connection; diff --git a/mysql-test/r/innodb_icp.result b/mysql-test/r/innodb_icp.result index 0ff3e2338115..df56d969cdd9 100644 --- a/mysql-test/r/innodb_icp.result +++ b/mysql-test/r/innodb_icp.result @@ -393,13 +393,13 @@ KEY (c1(6),i1) INSERT INTO t3 SELECT CONCAT('c-',1000+t2.a,'=w'), 1 FROM t2; EXPLAIN SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w' and i1 > 2; id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t3 NULL range c1 c1 30 NULL 2 33.33 Using index condition; Using where +1 SIMPLE t3 NULL range c1 c1 26 NULL 7 33.33 Using index condition; Using where Warnings: Note 1003 /* select#1 */ select `test`.`t3`.`c1` AS `c1` from `test`.`t3` where ((`test`.`t3`.`c1` >= 'c-1004=w') and (`test`.`t3`.`c1` <= 'c-1006=w') and (`test`.`t3`.`i1` > 2)) EXPLAIN FORMAT=tree SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w' and i1 > 2; EXPLAIN --> Filter: ((t3.c1 >= 'c-1004=w') and (t3.c1 <= 'c-1006=w')) (cost=1.16 rows=0.667) - -> Index range scan on t3 using c1 over (unprintable_blob_value <= c1 <= unprintable_blob_value AND 2 < i1), with index condition: (t3.i1 > 2) (cost=1.16 rows=2) +-> Filter: ((t3.c1 >= 'c-1004=w') and (t3.c1 <= 'c-1006=w')) (cost=3.41 rows=2.33) + -> Index range scan on t3 using c1 over (c1 <= unprintable_blob_value), with index condition: (t3.i1 > 2) (cost=3.41 rows=7) Warnings: Note 1003 /* select#1 */ select `test`.`t3`.`c1` AS `c1` from `test`.`t3` where ((`test`.`t3`.`c1` >= 'c-1004=w') and (`test`.`t3`.`c1` <= 'c-1006=w') and (`test`.`t3`.`i1` > 2)) diff --git a/mysql-test/r/myisam_icp.result b/mysql-test/r/myisam_icp.result index f624c57cafeb..30bf5989d702 100644 --- a/mysql-test/r/myisam_icp.result +++ b/mysql-test/r/myisam_icp.result @@ -391,13 +391,13 @@ KEY (c1(6),i1) INSERT INTO t3 SELECT CONCAT('c-',1000+t2.a,'=w'), 1 FROM t2; EXPLAIN SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w' and i1 > 2; id select_type table partitions type possible_keys key key_len ref rows filtered Extra -1 SIMPLE t3 NULL range c1 c1 30 NULL 1 33.33 Using where +1 SIMPLE t3 NULL range c1 c1 26 NULL 5 33.33 Using where Warnings: Note 1003 /* select#1 */ select `test`.`t3`.`c1` AS `c1` from `test`.`t3` where ((`test`.`t3`.`c1` >= 'c-1004=w') and (`test`.`t3`.`c1` <= 'c-1006=w') and (`test`.`t3`.`i1` > 2)) EXPLAIN FORMAT=tree SELECT c1 FROM t3 WHERE c1 >= 'c-1004=w' and c1 <= 'c-1006=w' and i1 > 2; EXPLAIN --> Filter: ((t3.c1 >= 'c-1004=w') and (t3.c1 <= 'c-1006=w') and (t3.i1 > 2)) (cost=0.71 rows=0.333) - -> Index range scan on t3 using c1 over (unprintable_blob_value <= c1 <= unprintable_blob_value AND 2 < i1) (cost=0.71 rows=1) +-> Filter: ((t3.c1 >= 'c-1004=w') and (t3.c1 <= 'c-1006=w') and (t3.i1 > 2)) (cost=2.51 rows=1.67) + -> Index range scan on t3 using c1 over (c1 <= unprintable_blob_value) (cost=2.51 rows=5) Warnings: Note 1003 /* select#1 */ select `test`.`t3`.`c1` AS `c1` from `test`.`t3` where ((`test`.`t3`.`c1` >= 'c-1004=w') and (`test`.`t3`.`c1` <= 'c-1006=w') and (`test`.`t3`.`i1` > 2)) diff --git a/mysql-test/t/bug_120580.test b/mysql-test/t/bug_120580.test new file mode 100644 index 000000000000..f8f7d25d9f65 --- /dev/null +++ b/mysql-test/t/bug_120580.test @@ -0,0 +1,143 @@ +--echo # +--echo # Bug#120580 Regression coverage +--echo # +SET @save_optimizer_switch=@@optimizer_switch; +SET @save_sql_mode=@@sql_mode; +SET @save_default_storage_engine=@@default_storage_engine; +SET @save_character_set_client=@@character_set_client; +SET @save_character_set_results=@@character_set_results; +SET @save_character_set_connection=@@character_set_connection; +SET @save_collation_connection=@@collation_connection; +CREATE DATABASE bug_120580 CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +USE bug_120580; +SET sql_mode=DEFAULT; +SET optimizer_switch=DEFAULT; +SET default_storage_engine=InnoDB; +SET NAMES utf8mb4 COLLATE utf8mb4_general_ci; +SET NAMES utf8mb4; +CREATE TABLE t0 (c0 LONGTEXT,KEY i0(c0(1))) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; +INSERT INTO t0(c0) VALUES ('꙰dKc%z'); +REPLACE INTO t0(c0) VALUES ('kQ'); +--sorted_result +SELECT ALL t0.c0 FROM t0 WHERE 'CZ'<=t0.c0; +PREPARE regression_stmt FROM 'SELECT ALL t0.c0 FROM t0 WHERE ''CZ''<=t0.c0;'; +--sorted_result +EXECUTE regression_stmt; +--sorted_result +EXECUTE regression_stmt; +DEALLOCATE PREPARE regression_stmt; +--sorted_result +SELECT ALL t0.c0 FROM t0 WHERE NOT (NOT ('CZ' <= t0.c0)); +--sorted_result +SELECT ALL t0.c0 FROM t0 FORCE INDEX (i0) WHERE 'CZ' <= t0.c0; +--sorted_result +SELECT ALL d.c0 FROM (SELECT t0.c0 FROM t0) AS d WHERE 'CZ' <= d.c0; +--sorted_result +SELECT ALL d.c0 FROM (SELECT t0.c0, 'CZ' <= t0.c0 AS matches_filter FROM t0) AS d WHERE d.matches_filter; +--sorted_result +SELECT ALL t0.c0 FROM t0 WHERE 'CZ' <= t0.c0 AND t0.c0 IS NULL UNION ALL SELECT ALL t0.c0 FROM t0 WHERE 'CZ' <= t0.c0 AND NOT (t0.c0 IS NULL) UNION ALL SELECT ALL t0.c0 FROM t0 WHERE 'CZ' <= t0.c0 AND (t0.c0 IS NULL) IS NULL; +--sorted_result +SELECT ALL t0.c0 FROM t0 WHERE CASE WHEN 'CZ' <= t0.c0 THEN 'CZ' <= t0.c0 ELSE 'CZ' <= t0.c0 END; +--sorted_result +SELECT ALL t0.c0 FROM t0 IGNORE INDEX (i0) WHERE 'CZ' <= t0.c0; +--sorted_result +SELECT ALL t0.c0 FROM t0 WHERE ((('CZ' <= t0.c0) OR NOT ('CZ' <= t0.c0)) OR ('CZ' <= t0.c0) IS NULL) AND ('CZ' <= t0.c0); +--sorted_result +SELECT ALL t0.c0 FROM t0 WHERE ((('CZ' <= t0.c0) AND NOT ('CZ' <= t0.c0)) AND ('CZ' <= t0.c0) IS NOT NULL) OR ('CZ' <= t0.c0); +--sorted_result +SELECT ALL t0.c0 FROM t0 WHERE t0.c0 >= 'CZ'; +--sorted_result +SELECT ALL t0.c0 FROM t0 WHERE NOT NOT ('CZ' <= t0.c0); +--sorted_result +SELECT ALL d.c0 FROM (SELECT t0.c0 AS c0, ('CZ' <= t0.c0) AS p FROM t0) AS d WHERE d.p; +--sorted_result +SELECT ALL t0.c0 FROM t0 WHERE 'CZ' <= t0.c0 AND t0.c0 = 'kQ' UNION ALL SELECT ALL t0.c0 FROM t0 WHERE 'CZ' <= t0.c0 AND NOT (t0.c0 = 'kQ') UNION ALL SELECT ALL t0.c0 FROM t0 WHERE 'CZ' <= t0.c0 AND (t0.c0 = 'kQ') IS NULL; +USE test; +DROP DATABASE bug_120580; +CREATE DATABASE bug_120580 CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +USE bug_120580; +SET sql_mode=DEFAULT; +SET optimizer_switch=DEFAULT; +SET default_storage_engine=InnoDB; +SET NAMES utf8mb4 COLLATE utf8mb4_general_ci; +CREATE TABLE t_a (id INT,c VARCHAR(64) COLLATE utf8mb4_0900_ai_ci ,KEY k(c(1))) ENGINE=MyISAM; +INSERT INTO t_a VALUES (1,'A'),(2,'-804207268'),(3,'ٛRIRS/*'),(4,''),(5,NULL); +CREATE TABLE t_b (id INT,c VARCHAR(64) COLLATE utf8mb4_0900_ai_ci NOT NULL ,KEY k(c(1))) ENGINE=InnoDB; +INSERT INTO t_b VALUES (1,'A'),(2,'-804207268'),(3,'ٛRIRS/*'),(4,''); +CREATE TABLE t_c (id INT,c VARCHAR(64) COLLATE utf8mb4_0900_ai_ci ,KEY k(c(1))) ENGINE=InnoDB; +INSERT INTO t_c VALUES (1,'A'),(2,'-804207268'),(3,'ٛRIRS/*'),(4,''),(5,NULL); +CREATE TABLE t_d (id INT,c VARCHAR(64) COLLATE utf8mb4_bin ,KEY k(c(1))) ENGINE=InnoDB; +INSERT INTO t_d VALUES (1,'A'),(2,'-804207268'),(3,'ٛRIRS/*'),(4,''),(5,NULL); +--sorted_result +SELECT id FROM t_a FORCE INDEX(k) WHERE c>'-804207268'; +PREPARE regression_stmt FROM 'SELECT id FROM t_a FORCE INDEX(k) WHERE c>''-804207268'';'; +--sorted_result +EXECUTE regression_stmt; +--sorted_result +EXECUTE regression_stmt; +DEALLOCATE PREPARE regression_stmt; +--sorted_result +SELECT id FROM t_a FORCE INDEX(k) WHERE c>='-804207268'; +PREPARE regression_stmt FROM 'SELECT id FROM t_a FORCE INDEX(k) WHERE c>=''-804207268'';'; +--sorted_result +EXECUTE regression_stmt; +--sorted_result +EXECUTE regression_stmt; +DEALLOCATE PREPARE regression_stmt; +--sorted_result +SELECT id FROM t_b FORCE INDEX(k) WHERE c>'-804207268'; +PREPARE regression_stmt FROM 'SELECT id FROM t_b FORCE INDEX(k) WHERE c>''-804207268'';'; +--sorted_result +EXECUTE regression_stmt; +--sorted_result +EXECUTE regression_stmt; +DEALLOCATE PREPARE regression_stmt; +--sorted_result +SELECT id FROM t_b FORCE INDEX(k) WHERE c>='-804207268'; +PREPARE regression_stmt FROM 'SELECT id FROM t_b FORCE INDEX(k) WHERE c>=''-804207268'';'; +--sorted_result +EXECUTE regression_stmt; +--sorted_result +EXECUTE regression_stmt; +DEALLOCATE PREPARE regression_stmt; +--sorted_result +SELECT id FROM t_c FORCE INDEX(k) WHERE c>'-804207268'; +PREPARE regression_stmt FROM 'SELECT id FROM t_c FORCE INDEX(k) WHERE c>''-804207268'';'; +--sorted_result +EXECUTE regression_stmt; +--sorted_result +EXECUTE regression_stmt; +DEALLOCATE PREPARE regression_stmt; +--sorted_result +SELECT id FROM t_c FORCE INDEX(k) WHERE c>='-804207268'; +PREPARE regression_stmt FROM 'SELECT id FROM t_c FORCE INDEX(k) WHERE c>=''-804207268'';'; +--sorted_result +EXECUTE regression_stmt; +--sorted_result +EXECUTE regression_stmt; +DEALLOCATE PREPARE regression_stmt; +--sorted_result +SELECT id FROM t_d FORCE INDEX(k) WHERE c>'-804207268'; +PREPARE regression_stmt FROM 'SELECT id FROM t_d FORCE INDEX(k) WHERE c>''-804207268'';'; +--sorted_result +EXECUTE regression_stmt; +--sorted_result +EXECUTE regression_stmt; +DEALLOCATE PREPARE regression_stmt; +--sorted_result +SELECT id FROM t_d FORCE INDEX(k) WHERE c>='-804207268'; +PREPARE regression_stmt FROM 'SELECT id FROM t_d FORCE INDEX(k) WHERE c>=''-804207268'';'; +--sorted_result +EXECUTE regression_stmt; +--sorted_result +EXECUTE regression_stmt; +DEALLOCATE PREPARE regression_stmt; +USE test; +DROP DATABASE bug_120580; +SET optimizer_switch=@save_optimizer_switch; +SET sql_mode=@save_sql_mode; +SET default_storage_engine=@save_default_storage_engine; +SET character_set_client=@save_character_set_client; +SET character_set_results=@save_character_set_results; +SET character_set_connection=@save_character_set_connection; +SET collation_connection=@save_collation_connection; diff --git a/sql/range_optimizer/range_analysis.cc b/sql/range_optimizer/range_analysis.cc index 672c5564054e..9fd6dcc1d202 100644 --- a/sql/range_optimizer/range_analysis.cc +++ b/sql/range_optimizer/range_analysis.cc @@ -1698,8 +1698,29 @@ static SEL_ROOT *get_mm_leaf(THD *thd, RANGE_OPT_PARAM *param, Item *cond_func, break; case Item_func::GT_FUNC: case Item_func::GE_FUNC: - /* Don't use open ranges for partial key_segments */ - if (!(key_part->flag & HA_PART_KEY_SEG)) { + /* Don't use an open range at the stored value for partial key segments. + */ + if ((key_part->flag & HA_PART_KEY_SEG) && + field->charset() == &my_charset_utf8mb4_0900_ai_ci) { + /* + A prefix of a value can consist entirely of characters that have no + primary weight in this collation. The rest of the value can then + compare above the bound even though its indexed prefix sorts below + the bound. Start at the first non-NULL key instead, and recheck the + full comparison after the range scan. + */ + if (!field->is_nullable()) + tree->root->min_flag = NO_MIN_RANGE; + else { + if (!(tree->root->min_value = static_cast( + alloc->Alloc(key_part->store_length + 1)))) + goto end; + TRASH(tree->root->min_value, key_part->store_length + 1); + memcpy(tree->root->min_value, is_null_string, sizeof(is_null_string)); + tree->root->min_flag = NEAR_MIN; + } + *inexact = true; + } else if (!(key_part->flag & HA_PART_KEY_SEG)) { /* Set NEAR_MIN to read values greater than the stored value. */