From 805c9f9ec9524c1fa99878fb0170a8a00b81af0c Mon Sep 17 00:00:00 2001 From: DerZc <34330257+DerZc@users.noreply.github.com> Date: Mon, 28 Sep 2026 14:18:20 +0800 Subject: [PATCH] MDEV-39644 Wrong result with ICP in reverse ordered partition scans Ordered partition scans keep one row per partition in queue buffers. However, pushed index conditions use Field objects bound to table->record[0]. Reading directly into a queue buffer can therefore evaluate the condition against a different row and skip matching rows. Route ha_index_last() and ha_index_prev() through read_with_icp(), as already done for forward ordered reads. With ICP, read into table->record[0] and copy the row to the queue buffer only on success. Preserve the queue buffer when a read returns EOF or another error. Full forward index scans do not use ICP. Assert this invariant before calling ha_index_first() directly. Add a minimal MyISAM regression with matching and rejected rows in two partitions. Bug report: https://jira.mariadb.org/browse/MDEV-39644 --- mysql-test/main/partition_order.result | 10 ++++++++++ mysql-test/main/partition_order.test | 6 ++++++ sql/ha_partition.cc | 25 ++++++++++++++++++++++--- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/mysql-test/main/partition_order.result b/mysql-test/main/partition_order.result index d4a0c133d1091..998e4c01b430d 100644 --- a/mysql-test/main/partition_order.result +++ b/mysql-test/main/partition_order.result @@ -796,3 +796,13 @@ a b 2 2 3 3 drop table t1; +# MDEV-39644 Evaluate ICP on the current row in reverse partition scans +CREATE TABLE t1 (c1 INT,c2 INT,c3 INT,KEY k(c2,c3)) +ENGINE=MyISAM PARTITION BY HASH(c1) PARTITIONS 2; +INSERT INTO t1 VALUES (1,4,1),(2,3,1),(3,2,0),(4,1,1); +SELECT * FROM t1 WHERE c2>=1 AND c3=1 ORDER BY c2 DESC; +c1 c2 c3 +1 4 1 +2 3 1 +4 1 1 +DROP TABLE t1; diff --git a/mysql-test/main/partition_order.test b/mysql-test/main/partition_order.test index f5cd4c2507417..3fee60a3e3d4d 100644 --- a/mysql-test/main/partition_order.test +++ b/mysql-test/main/partition_order.test @@ -846,3 +846,9 @@ insert into t1 values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6); explain select * from t1 order by a limit 3; select * from t1 order by a limit 3; drop table t1; +--echo # MDEV-39644 Evaluate ICP on the current row in reverse partition scans +CREATE TABLE t1 (c1 INT,c2 INT,c3 INT,KEY k(c2,c3)) + ENGINE=MyISAM PARTITION BY HASH(c1) PARTITIONS 2; +INSERT INTO t1 VALUES (1,4,1),(2,3,1),(3,2,0),(4,1,1); +SELECT * FROM t1 WHERE c2>=1 AND c3=1 ORDER BY c2 DESC; +DROP TABLE t1; diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 343ab395ffc32..ca3d6211e1e35 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -104,7 +104,8 @@ static int read_with_icp(TABLE* table, return func(record_buf); int error= func(table->record[0]); - memcpy(record_buf, table->record[0], record_buf_len); + if (likely(!error)) + memcpy(record_buf, table->record[0], record_buf_len); return error; } @@ -8208,11 +8209,22 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order) /* Caller has specified reverse_order */ break; case partition_index_first: + /* + We don't call read_with_icp() here as full index scans are not done + with ICP. + */ + DBUG_ASSERT(!pushed_idx_cond); error= file->ha_index_first(rec_buf_ptr); reverse_order= FALSE; break; case partition_index_last: - error= file->ha_index_last(rec_buf_ptr); + error= read_with_icp(table, + pushed_idx_cond != nullptr, + rec_buf_ptr, + m_rec_length, + [file] (uchar* read_buf) { + return file->ha_index_last(read_buf); + }); reverse_order= TRUE; break; case partition_read_range: @@ -8738,7 +8750,14 @@ int ha_partition::handle_ordered_prev(uchar *buf) uchar *rec_buf= queue_top(&m_queue) + ORDERED_REC_OFFSET; handler *file= m_file[part_id]; - if (unlikely((error= file->ha_index_prev(rec_buf)))) + error= read_with_icp(table, + pushed_idx_cond != nullptr, + rec_buf, + m_rec_length, + [file] (uchar* read_buf) { + return file->ha_index_prev(read_buf); + }); + if (unlikely(error)) { if (error == HA_ERR_END_OF_FILE && m_queue.elements) {