From 6f4302f51e35227dbf3937a32333ca4e308d76f8 Mon Sep 17 00:00:00 2001 From: Somrak Monpengpinij Date: Tue, 21 Oct 2025 02:51:30 +0700 Subject: [PATCH] Bug#119198 fix bug optimizer reverse index scan give incorrect result --- ...imizer_reverse_index_range_scan_bug.result | 67 ++++++++++++++++++ ...ptimizer_reverse_index_range_scan_bug.test | 69 +++++++++++++++++++ sql/sql_optimizer.cc | 5 +- 3 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 mysql-test/r/optimizer_reverse_index_range_scan_bug.result create mode 100644 mysql-test/t/optimizer_reverse_index_range_scan_bug.test diff --git a/mysql-test/r/optimizer_reverse_index_range_scan_bug.result b/mysql-test/r/optimizer_reverse_index_range_scan_bug.result new file mode 100644 index 000000000000..e1535341cf8e --- /dev/null +++ b/mysql-test/r/optimizer_reverse_index_range_scan_bug.result @@ -0,0 +1,67 @@ +# +# Bug#119198 Reverse index range scan give incorrect result. +# +CREATE TABLE temp_table ( +id bigint unsigned NOT NULL AUTO_INCREMENT, +col1 tinyint unsigned NOT NULL, +col2 tinyint unsigned NOT NULL, +PRIMARY KEY (id), +KEY temp_table_idx01 (col1) +) ENGINE=InnoDB; +# 2. Insert Data +INSERT INTO temp_table (id, col1, col2) +VALUES (10, 1, 4); +INSERT INTO temp_table (id, col1, col2) +VALUES (20, 1, 4); +# 3. Run Query +SELECT * FROM temp_table; +id col1 col2 +10 1 4 +20 1 4 +# Verify table should contain 2 records id#10, id#20 respectively +# +EXPLAIN +SELECT * FROM temp_table +WHERE col1 >= 1 AND col1 <= 1 +ORDER BY col1 DESC, id DESC +LIMIT 1; +id select_type table partitions type possible_keys key key_len ref rows filtered Extra +1 SIMPLE temp_table NULL range temp_table_idx01 temp_table_idx01 1 NULL 2 100.00 Using index condition; Backward index scan +# Query should use reverse index scan range +# +SELECT * FROM temp_table +WHERE col1 >= 1 AND col1 <= 1 +ORDER BY col1 DESC, id DESC +LIMIT 1; +id col1 col2 +20 1 4 +# Query should return valid result with EQ_RANGE ordering by col1 desc limit 1 which is id#20 +# +SELECT * FROM temp_table +WHERE col1 >= 1 AND col1 <= 1 +ORDER BY col1 DESC, id DESC +LIMIT 2; +id col1 col2 +20 1 4 +10 1 4 +# Query should return valid result with EQ_RANGE ordering by col1 desc limit 2 which are id#20, id#10 +# +SELECT * FROM temp_table +WHERE col1 >= 0 AND col1 <= 5 +ORDER BY col1 DESC, id DESC +LIMIT 1; +id col1 col2 +20 1 4 +# Query should return valid result with EQ_RANGE ordering by col1 desc limit 1 which is id#20 +# +SELECT * FROM temp_table +WHERE col1 >= 0 AND col1 <= 5 +ORDER BY col1 DESC, id DESC +LIMIT 2; +id col1 col2 +20 1 4 +10 1 4 +# Query should return valid result with EQ_RANGE ordering by col1 desc limit 2 which are id#20, id#10 +# +# 4. Clean up +DROP TABLE temp_table; diff --git a/mysql-test/t/optimizer_reverse_index_range_scan_bug.test b/mysql-test/t/optimizer_reverse_index_range_scan_bug.test new file mode 100644 index 000000000000..f91965cd5d4a --- /dev/null +++ b/mysql-test/t/optimizer_reverse_index_range_scan_bug.test @@ -0,0 +1,69 @@ +--source include/have_debug_sync.inc + +--echo # +--echo # Bug#119198 Reverse index range scan give incorrect result. +--echo # + +CREATE TABLE temp_table ( + id bigint unsigned NOT NULL AUTO_INCREMENT, + col1 tinyint unsigned NOT NULL, + col2 tinyint unsigned NOT NULL, + PRIMARY KEY (id), + KEY temp_table_idx01 (col1) +) ENGINE=InnoDB; + +--echo # 2. Insert Data + +INSERT INTO temp_table (id, col1, col2) +VALUES (10, 1, 4); + +INSERT INTO temp_table (id, col1, col2) +VALUES (20, 1, 4); + +--echo # 3. Run Query +--disable_warnings +SELECT * FROM temp_table; +--echo # Verify table should contain 2 records id#10, id#20 respectively +--echo # + + +EXPLAIN +SELECT * FROM temp_table + WHERE col1 >= 1 AND col1 <= 1 + ORDER BY col1 DESC, id DESC + LIMIT 1; +--enable_warnings +--echo # Query should use reverse index scan range +--echo # + + +SELECT * FROM temp_table + WHERE col1 >= 1 AND col1 <= 1 + ORDER BY col1 DESC, id DESC + LIMIT 1; +--echo # Query should return valid result with EQ_RANGE ordering by col1 desc limit 1 which is id#20 +--echo # + +SELECT * FROM temp_table + WHERE col1 >= 1 AND col1 <= 1 + ORDER BY col1 DESC, id DESC + LIMIT 2; +--echo # Query should return valid result with EQ_RANGE ordering by col1 desc limit 2 which are id#20, id#10 +--echo # + +SELECT * FROM temp_table + WHERE col1 >= 0 AND col1 <= 5 + ORDER BY col1 DESC, id DESC + LIMIT 1; +--echo # Query should return valid result with EQ_RANGE ordering by col1 desc limit 1 which is id#20 +--echo # + +SELECT * FROM temp_table + WHERE col1 >= 0 AND col1 <= 5 + ORDER BY col1 DESC, id DESC + LIMIT 2; +--echo # Query should return valid result with EQ_RANGE ordering by col1 desc limit 2 which are id#20, id#10 +--echo # + +--echo # 4. Clean up +DROP TABLE temp_table; \ No newline at end of file diff --git a/sql/sql_optimizer.cc b/sql/sql_optimizer.cc index 32bb4b8a3d74..45c7aa9413b9 100644 --- a/sql/sql_optimizer.cc +++ b/sql/sql_optimizer.cc @@ -9906,8 +9906,9 @@ static bool make_join_query_block(JOIN *join, Item *cond) { used_index(tab->range_scan()) != MAX_KEY) { const uint ref_key = used_index(tab->range_scan()); bool skip_quick; + uint used_key_parts = 0; read_direction = test_if_order_by_key( - &join->order, tab->table(), ref_key, nullptr, &skip_quick); + &join->order, tab->table(), ref_key, &used_key_parts, &skip_quick); if (skip_quick) read_direction = 0; /* If the index provides order there is no need to recheck @@ -9921,7 +9922,7 @@ static bool make_join_query_block(JOIN *join, Item *cond) { if (read_direction == 1 || (read_direction == -1 && reverse_sort_possible(tab->range_scan()) && - !make_reverse(get_used_key_parts(tab->range_scan()), + !make_reverse(std::max(used_key_parts, get_used_key_parts(tab->range_scan())), tab->range_scan()))) { recheck_reason = DONT_RECHECK; }