mariadb/mysql-test/main/index_merge_innodb_notembedded.test
bsrikanth-mariadb 6af171f3bf MDEV-36410: Wrong Result with Desc Primary Key in Index
If a table had a primary_key index, non_primary_key index,
and an extended index with primary key in desc order,
then the query with where clause predicates containing these
index fields was picking an index_merge plan which was causing
a wrong result.

The plan shouldn't have contained index_merge in the first place,
because the rows produced by one index have rows ordered by rowid
in ascending order, and the rows coming from the other index
were in a different order.

The solution is to not produce index_merge plan in such scenarios.
is_key_scan_ror() would now return false, for non_primary_key indexes
if any primary key part with a reverse sort is present in them.
2025-06-21 06:21:26 -04:00

90 lines
3 KiB
Text

--source include/have_innodb.inc
--source include/have_sequence.inc
--source include/not_embedded.inc
--echo
--echo # MDEV-36410 wrong result with index_merge on indexes having descending primary key
--echo #
set optimizer_trace='enabled=on';
SET @save_sort_buffer_size=@@sort_buffer_size;
SET SESSION sort_buffer_size = 1024*16;
CREATE TABLE t1 (
id bigint(20) NOT NULL,
title varchar(255) NOT NULL,
status tinyint(4) DEFAULT 0,
country_code varchar(5) DEFAULT NULL,
PRIMARY KEY (id),
KEY idx_status (status),
KEY idx_country_code_status_id (country_code,status,id DESC)
) ENGINE=InnoDB;
INSERT INTO t1(id,title,status,country_code)
SELECT seq, CONCAT('abc', seq), seq%10, CONCAT('C', seq%5) FROM seq_1_to_500;
--echo # This must not use index_merge:
EXPLAIN
SELECT * FROM t1 WHERE country_code ='C1' and `status` =1;
set @trace= (select JSON_EXTRACT(trace, '$**.range_scan_alternatives[*]')
from INFORMATION_SCHEMA.OPTIMIZER_TRACE);
select json_detailed(json_extract(@trace, '$[*].index')) as INDEXES;
select json_detailed(json_extract(@trace, '$[*].rowid_ordered')) as ROR;
#select JSON_DETAILED(JSON_EXTRACT(trace, '$**.range_scan_alternatives[*].index')) AS JS from INFORMATION_SCHEMA.OPTIMIZER_TRACE;
DROP table t1;
--echo # Now, try with indexes using ASC ordering and PK using DESC
CREATE TABLE t1 (
id bigint(20) NOT NULL,
title varchar(255) NOT NULL,
status tinyint(4) DEFAULT 0,
country_code varchar(5) DEFAULT NULL,
PRIMARY KEY (id DESC),
KEY idx_status (status),
KEY idx_country_code_status_id (country_code,status,id)
) ENGINE=InnoDB;
INSERT INTO t1(id,title,status,country_code)
SELECT seq, CONCAT('abc', seq), seq%10, CONCAT('C', seq%5) FROM seq_1_to_500;
--echo # Must not use index_merge:
EXPLAIN
SELECT * FROM t1 WHERE country_code ='C1' and status = 1;
set @trace= (select JSON_EXTRACT(trace, '$**.range_scan_alternatives[*]')
from INFORMATION_SCHEMA.OPTIMIZER_TRACE);
select json_detailed(json_extract(@trace, '$[*].index')) as INDEXES;
select json_detailed(json_extract(@trace, '$[*].rowid_ordered')) as ROR;
DROP TABLE t1;
--echo # Now, try with indexes using DESC ordering and PK using DESC
CREATE TABLE t1 (
id bigint(20) NOT NULL,
title varchar(255) NOT NULL,
status tinyint(4) DEFAULT 0,
country_code varchar(5) DEFAULT NULL,
PRIMARY KEY (id DESC),
KEY idx_status (status),
KEY idx_country_code_status_id (country_code,status,id DESC)
) ENGINE=InnoDB;
INSERT INTO t1(id,title,status,country_code)
SELECT seq, CONCAT('abc', seq), seq%10, CONCAT('C', seq%5) FROM seq_1_to_500;
--echo # Must not use index_merge:
EXPLAIN
SELECT * FROM t1 WHERE country_code ='C1' and status = 1;
set @trace= (select JSON_EXTRACT(trace, '$**.range_scan_alternatives[*]')
from INFORMATION_SCHEMA.OPTIMIZER_TRACE);
select json_detailed(json_extract(@trace, '$[*].index')) as INDEXES;
select json_detailed(json_extract(@trace, '$[*].rowid_ordered')) as ROR;
DROP TABLE t1;
SET sort_buffer_size= @save_sort_buffer_size;