Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion mysql-test/main/ctype_utf8.result
Original file line number Diff line number Diff line change
Expand Up @@ -1521,9 +1521,9 @@ id a b
11 bbbbbb 60
SELECT id, a, b FROM t1 WHERE a BETWEEN 'aaaa' AND 'bbbbbb';
id a b
6 bbbbbb 40
8 aaaa 10
9 aaaa 50
6 bbbbbb 40
11 bbbbbb 60
SELECT id, a FROM t1 WHERE a='bbbbbb';
id a
Expand Down
2 changes: 1 addition & 1 deletion mysql-test/main/ctype_utf8mb4.result
Original file line number Diff line number Diff line change
Expand Up @@ -1546,9 +1546,9 @@ id a b
11 bbbbbb 60
SELECT id, a, b FROM t1 WHERE a BETWEEN 'aaaa' AND 'bbbbbb';
id a b
6 bbbbbb 40
8 aaaa 10
9 aaaa 50
6 bbbbbb 40
11 bbbbbb 60
SELECT id, a FROM t1 WHERE a='bbbbbb';
id a
Expand Down
23 changes: 23 additions & 0 deletions mysql-test/main/range.result
Original file line number Diff line number Diff line change
Expand Up @@ -3873,3 +3873,26 @@ DROP TABLE lineitem;
#
# End of 11.0 tests
#
#
# MDEV-39843: Prefix index causes incorrect range query result
#
SET NAMES utf8mb4;
CREATE TABLE t0 (c0 LONGTEXT, KEY i0 (c0(1)));
INSERT INTO t0(c0) VALUES ('꙰dcF'), ('kER');
SELECT c0 FROM t0 WHERE 'C0' <= t0.c0;
c0
꙰dcF
kER
explain SELECT c0 FROM t0 WHERE 'C0' <= t0.c0;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t0 ALL i0 NULL NULL NULL 2 Using where
DROP INDEX i0 ON t0;
SELECT c0 FROM t0 WHERE 'C0' <= t0.c0;
c0
꙰dcF
kER
explain SELECT c0 FROM t0 WHERE 'C0' <= t0.c0;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t0 ALL NULL NULL NULL NULL 2 Using where
DROP TABLE t0;
# End of 11.4 tests
21 changes: 21 additions & 0 deletions mysql-test/main/range.test
Original file line number Diff line number Diff line change
Expand Up @@ -2641,3 +2641,24 @@ DROP TABLE lineitem;
--echo #
--echo # End of 11.0 tests
--echo #

--echo #
--echo # MDEV-39843: Prefix index causes incorrect range query result
--echo #

SET NAMES utf8mb4;

CREATE TABLE t0 (c0 LONGTEXT, KEY i0 (c0(1)));
INSERT INTO t0(c0) VALUES ('꙰dcF'), ('kER');

SELECT c0 FROM t0 WHERE 'C0' <= t0.c0;
explain SELECT c0 FROM t0 WHERE 'C0' <= t0.c0;

DROP INDEX i0 ON t0;

SELECT c0 FROM t0 WHERE 'C0' <= t0.c0;
explain SELECT c0 FROM t0 WHERE 'C0' <= t0.c0;

DROP TABLE t0;

--echo # End of 11.4 tests
27 changes: 27 additions & 0 deletions sql/opt_range.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9701,6 +9701,25 @@ SEL_ARG *Field::stored_field_make_mm_leaf_bounded_int(RANGE_OPT_PARAM *param,
}


/*
Check whether a partial (prefix) key segment cannot be used as a range
bound because of the collation.

For a collation with a complex, non one-to-one character-to-weight mapping
(contractions, expansions or ignorable characters: MY_CS_NON1TO1) the sort
key of a character prefix does not preserve the ordering of the full value,
so neither a lower nor an upper prefix bound is sound.
Equality is unaffected and keeps using the prefix (the full condition is
always rechecked).
*/
static bool prefix_key_part_unsafe_for_range(const KEY_PART *key_part,
const Field *field)
{
return (key_part->flag & HA_PART_KEY_SEG) &&
(field->charset()->state & MY_CS_NON1TO1);
}


SEL_ARG *Field::stored_field_make_mm_leaf(RANGE_OPT_PARAM *param,
KEY_PART *key_part,
scalar_comparison_op op,
Expand All @@ -9715,12 +9734,20 @@ SEL_ARG *Field::stored_field_make_mm_leaf(RANGE_OPT_PARAM *param,

switch (op) {
case SCALAR_CMP_LE:
if (prefix_key_part_unsafe_for_range(key_part, this))
DBUG_RETURN(0);
DBUG_RETURN(new (mem_root) SEL_ARG_LE(str, this));
case SCALAR_CMP_LT:
if (prefix_key_part_unsafe_for_range(key_part, this))
DBUG_RETURN(0);
DBUG_RETURN(new (mem_root) SEL_ARG_LT(thd, str, key_part, this, value));
case SCALAR_CMP_GT:
if (prefix_key_part_unsafe_for_range(key_part, this))
DBUG_RETURN(0);
DBUG_RETURN(new (mem_root) SEL_ARG_GT(thd, str, key_part, this, value));
case SCALAR_CMP_GE:
if (prefix_key_part_unsafe_for_range(key_part, this))
DBUG_RETURN(0);
DBUG_RETURN(new (mem_root) SEL_ARG_GE(thd, str, key_part, this, value));
Comment on lines 9735 to 9751

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The check prefix_key_part_unsafe_for_range(key_part, this) is duplicated across all four inequality comparison cases (SCALAR_CMP_LE, SCALAR_CMP_LT, SCALAR_CMP_GT, SCALAR_CMP_GE). Consolidating this check before the switch statement reduces code duplication and improves maintainability.

  if ((op == SCALAR_CMP_LE || op == SCALAR_CMP_LT ||
       op == SCALAR_CMP_GT || op == SCALAR_CMP_GE) &&
      prefix_key_part_unsafe_for_range(key_part, this))
    DBUG_RETURN(0);

  switch (op) {
  case SCALAR_CMP_LE:
    DBUG_RETURN(new (mem_root) SEL_ARG_LE(str, this));
  case SCALAR_CMP_LT:
    DBUG_RETURN(new (mem_root) SEL_ARG_LT(thd, str, key_part, this, value));
  case SCALAR_CMP_GT:
    DBUG_RETURN(new (mem_root) SEL_ARG_GT(thd, str, key_part, this, value));
  case SCALAR_CMP_GE:
    DBUG_RETURN(new (mem_root) SEL_ARG_GE(thd, str, key_part, this, value));

case SCALAR_CMP_EQ:
case SCALAR_CMP_EQUAL:
Expand Down
Loading