From e821ddd545d15ba0a792944bf7d351d0dc536f4b Mon Sep 17 00:00:00 2001 From: DerZc <798604270@qq.com> Date: Mon, 28 Sep 2026 14:18:19 +0800 Subject: [PATCH] Bug#118624: Preserve PAD SPACE binary LIKE range matches Use the collation padding attribute in the multibyte, simple and generic LIKE range generators. Under PAD SPACE, extend the lower bound with the minimum collation character so suffixes below a space are not excluded. Keep the prefix length for NO PAD collations. Cover the three collation families and a NO PAD control with minimal indexed and table-scan queries in the existing func_like test. Bug report: https://bugs.mysql.com/bug.php?id=118624 --- mysql-test/r/func_like.result | 42 +++++++++++++++++++++++++++++++++++ mysql-test/t/func_like.test | 22 ++++++++++++++++++ strings/ctype-mb.cc | 11 +++++---- strings/ctype-simple.cc | 7 +++--- 4 files changed, 75 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/func_like.result b/mysql-test/r/func_like.result index 3e0e23419a10..996ec9cd4a37 100644 --- a/mysql-test/r/func_like.result +++ b/mysql-test/r/func_like.result @@ -499,3 +499,45 @@ l 0 1 DROP TABLE t1; +# +# Bug#118624: PAD SPACE binary LIKE ranges include control suffixes +# +CREATE TABLE t1 (a VARCHAR(10), KEY(a)) +CHARACTER SET utf8mb4 COLLATE utf8mb4_bin; +INSERT INTO t1 VALUES ('a'), (CONCAT('a', CHAR(10))), ('b'); +SELECT HEX(a) FROM t1 FORCE INDEX(a) WHERE a LIKE 'a%' ORDER BY a; +HEX(a) +610A +61 +SELECT HEX(a) FROM t1 IGNORE INDEX(a) WHERE a LIKE 'a%' ORDER BY a; +HEX(a) +610A +61 +ALTER TABLE t1 CONVERT TO CHARACTER SET latin1 COLLATE latin1_bin; +SELECT HEX(a) FROM t1 FORCE INDEX(a) WHERE a LIKE 'a%' ORDER BY a; +HEX(a) +610A +61 +SELECT HEX(a) FROM t1 IGNORE INDEX(a) WHERE a LIKE 'a%' ORDER BY a; +HEX(a) +610A +61 +ALTER TABLE t1 CONVERT TO CHARACTER SET utf16 COLLATE utf16_bin; +SELECT HEX(a) FROM t1 FORCE INDEX(a) WHERE a LIKE 'a%' ORDER BY a; +HEX(a) +0061000A +0061 +SELECT HEX(a) FROM t1 IGNORE INDEX(a) WHERE a LIKE 'a%' ORDER BY a; +HEX(a) +0061000A +0061 +ALTER TABLE t1 CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin; +SELECT HEX(a) FROM t1 FORCE INDEX(a) WHERE a LIKE 'a%' ORDER BY a; +HEX(a) +61 +610A +SELECT HEX(a) FROM t1 IGNORE INDEX(a) WHERE a LIKE 'a%' ORDER BY a; +HEX(a) +61 +610A +DROP TABLE t1; diff --git a/mysql-test/t/func_like.test b/mysql-test/t/func_like.test index aa9dd0d8d448..0af1c43871b3 100644 --- a/mysql-test/t/func_like.test +++ b/mysql-test/t/func_like.test @@ -394,3 +394,25 @@ SELECT c1 LIKE c1 ESCAPE '1' AS l FROM t1; SELECT * FROM t1 WHERE c1 LIKE c1 ESCAPE 1; SELECT c1 LIKE c1 ESCAPE 1 AS l FROM t1; DROP TABLE t1; + +--echo # +--echo # Bug#118624: PAD SPACE binary LIKE ranges include control suffixes +--echo # +CREATE TABLE t1 (a VARCHAR(10), KEY(a)) + CHARACTER SET utf8mb4 COLLATE utf8mb4_bin; +INSERT INTO t1 VALUES ('a'), (CONCAT('a', CHAR(10))), ('b'); +SELECT HEX(a) FROM t1 FORCE INDEX(a) WHERE a LIKE 'a%' ORDER BY a; +SELECT HEX(a) FROM t1 IGNORE INDEX(a) WHERE a LIKE 'a%' ORDER BY a; + +ALTER TABLE t1 CONVERT TO CHARACTER SET latin1 COLLATE latin1_bin; +SELECT HEX(a) FROM t1 FORCE INDEX(a) WHERE a LIKE 'a%' ORDER BY a; +SELECT HEX(a) FROM t1 IGNORE INDEX(a) WHERE a LIKE 'a%' ORDER BY a; + +ALTER TABLE t1 CONVERT TO CHARACTER SET utf16 COLLATE utf16_bin; +SELECT HEX(a) FROM t1 FORCE INDEX(a) WHERE a LIKE 'a%' ORDER BY a; +SELECT HEX(a) FROM t1 IGNORE INDEX(a) WHERE a LIKE 'a%' ORDER BY a; + +ALTER TABLE t1 CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_bin; +SELECT HEX(a) FROM t1 FORCE INDEX(a) WHERE a LIKE 'a%' ORDER BY a; +SELECT HEX(a) FROM t1 IGNORE INDEX(a) WHERE a LIKE 'a%' ORDER BY a; +DROP TABLE t1; diff --git a/strings/ctype-mb.cc b/strings/ctype-mb.cc index 174c80be6ab2..f924534ab151 100644 --- a/strings/ctype-mb.cc +++ b/strings/ctype-mb.cc @@ -669,7 +669,7 @@ bool my_like_range_mb(const CHARSET_INFO *cs, const char *ptr, "a\0\0..." is the smallest possible string for PAD SPACE. "a\xff\xff..." is the biggest possible string. */ - if ((cs->state & MY_CS_BINSORT) || cs->pad_attribute == NO_PAD) { + if (cs->pad_attribute == NO_PAD) { *min_length = static_cast(min_str - min_org); /* @@ -680,6 +680,7 @@ bool my_like_range_mb(const CHARSET_INFO *cs, const char *ptr, *min_str++ = ' '; } while (min_str != min_end); } else { + /* With PAD SPACE, a suffix below ' ' sorts before the bare prefix. */ *min_length = res_length; do { *min_str++ = static_cast(cs->min_sort_char); @@ -859,11 +860,13 @@ bool my_like_range_generic(const CHARSET_INFO *cs, const char *ptr, if (wc == (my_wc_t)w_many) { /* Calculate length of keys: - a\min\min... is the smallest possible string + a is the smallest possible string for NO PAD + a\min\min... is the smallest possible string for PAD SPACE a\max\max... is the biggest possible string */ - *min_length = ((cs->state & MY_CS_BINSORT) ? (size_t)(min_str - min_org) - : res_length); + *min_length = (cs->pad_attribute == NO_PAD) + ? static_cast(min_str - min_org) + : res_length; *max_length = res_length; goto pad_min_max; } diff --git a/strings/ctype-simple.cc b/strings/ctype-simple.cc index 77b6c09fd719..ff737b96cad0 100644 --- a/strings/ctype-simple.cc +++ b/strings/ctype-simple.cc @@ -855,9 +855,10 @@ bool my_like_range_simple(const CHARSET_INFO *cs, const char *ptr, } if (*ptr == w_many) /* '%' in SQL */ { - /* Calculate length of keys */ - *min_length = ((cs->state & MY_CS_BINSORT) ? (size_t)(min_str - min_org) - : res_length); + /* With PAD SPACE, a suffix below space can sort before the prefix. */ + *min_length = (cs->pad_attribute == NO_PAD) + ? static_cast(min_str - min_org) + : res_length; *max_length = res_length; do { *min_str++ = 0;