1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00

MDEV-41366 Only allow exact access for iterated partition index scan with prefix

The idea of Case 2 in can_skip_merging_scans is that when partition
column key prefix is fixed, and the infix that is also the "partition
by range" column, we can scan each partition in order. To that end, we
restrict access flags to exact accesses.

Also reset m_unordered_prefix_len in the single-partition "unordered"
scan case as it skips can_skip_merging_scans, which would otherwise
leave a stale length for handle_unordered_prev.

Also include HA_READ_KEY_OR_PREV in reverse_order flags, so that it
can be considered and rejected for Case 2.
This commit is contained in:
Yuchen Pei
2026-10-21 17:34:50 +11:00
parent 9a1123dc45
commit b58f8cc79b
4 changed files with 449 additions and 16 deletions

View File

@@ -287,8 +287,8 @@
show status like 'handler_read_%';
Variable_name Value
Handler_read_first 0
-Handler_read_key 6
+Handler_read_key 7
-Handler_read_key 8
+Handler_read_key 11
Handler_read_last 0
Handler_read_next 0
Handler_read_prev 0
@@ -301,9 +301,12 @@
) PARTITION BY RANGE COLUMNS(d) (
PARTITION `p1` VALUES LESS THAN (10),
PARTITION `p2` VALUES LESS THAN (20),
@@ -688,8 +688,8 @@
@@ -686,10 +686,10 @@
show status like 'handler_read_%';
Variable_name Value
Handler_read_first 0
Handler_read_key 1
-Handler_read_key 1
+Handler_read_key 2
Handler_read_last 0
-Handler_read_next 2
-Handler_read_prev 0
@@ -312,6 +315,15 @@
Handler_read_retry 0
Handler_read_rnd 0
Handler_read_rnd_deleted 0
@@ -698,7 +698,7 @@
set @js='$out';
select json_extract(@js,'$**.partitions_index_scan_method');
json_extract(@js,'$**.partitions_index_scan_method')
-["iterate_over_partitions"]
+["merge_ordered_scans"]
## Case 2: Unordered scans in ha_partition::index_read_map
flush status;
select * from t1 where c = 120 ORDER BY d DESC LIMIT 1;
@@ -725,8 +725,8 @@
Handler_read_first 0
Handler_read_key 1
@@ -479,7 +491,7 @@
Handler_read_prev 0
Handler_read_retry 0
Handler_read_rnd 0
@@ -1202,18 +1202,18 @@
@@ -1202,20 +1202,20 @@
#
# MDEV-40479: Reverse prefix scan across partitions
#
@@ -503,3 +515,5 @@
+11
+15
DROP TABLE t1;
#
# MDEV-41366 Wrong result with Case 2 of non-merging partition index scan

View File

@@ -630,8 +630,8 @@ select json_extract(@js,'$**.partitions_index_scan_method');
json_extract(@js,'$**.partitions_index_scan_method')
["iterate_over_partitions"]
DROP TABLE t1;
## Case 2: calls to ha_partition::read_range_first and
## ha_partition::index_read_map, via loose index scan
## Case 2 loose index scan: calls to read_range_first iterates;
## index_read_map with AFTER_KEY is merged
CREATE TABLE t1 (
a INT,
b INT,
@@ -651,7 +651,7 @@ a MIN(b)
show status like 'handler_read_%';
Variable_name Value
Handler_read_first 0
Handler_read_key 6
Handler_read_key 8
Handler_read_last 0
Handler_read_next 0
Handler_read_prev 0
@@ -662,7 +662,7 @@ Handler_read_rnd_next 0
set @js='$out';
select json_extract(@js,'$**.partitions_index_scan_method');
json_extract(@js,'$**.partitions_index_scan_method')
["iterate_over_partitions"]
["both"]
DROP TABLE t1;
CREATE TABLE t1 (
c int,
@@ -676,7 +676,8 @@ PARTITION `p4` VALUES LESS THAN (40),
PARTITION `p5` VALUES LESS THAN MAXVALUE
);
insert into t1 select t2.seq, t3.seq from seq_100_to_200 t2, seq_1_to_50 t3;
## Case 2: Unordered scans with MRR
## Case 2: iterated partition scans with MRR with ASC key;
## merged scan with index_read_map with DESC key
flush status;
select * from t1 where c=113 and d between 25 and 35 order by d limit 3;
c d
@@ -1217,3 +1218,227 @@ c
11
6
DROP TABLE t1;
#
# MDEV-41366 Wrong result with Case 2 of non-merging partition index scan
#
# prefix-only PREFIX_LAST_OR_PREV, merged scan
CREATE TABLE t1 (a INT, b INT, c INT, PRIMARY KEY(a,c), KEY(b,c)) ENGINE=InnoDB
PARTITION BY RANGE(c) (PARTITION p0 VALUES LESS THAN (10),
PARTITION p1 VALUES LESS THAN (20));
INSERT INTO t1 VALUES (1,1,5),(2,0,12);
SELECT c FROM t1 FORCE INDEX(b) WHERE b=1 AND c<=15 ORDER BY c DESC;
c
5
set @js='$out';
select json_extract(@js,'$**.partitions_index_scan_method');
json_extract(@js,'$**.partitions_index_scan_method')
["merge_ordered_scans"]
DROP TABLE t1;
# same but with desc key
CREATE TABLE t1 (a INT, b INT, c INT, PRIMARY KEY(a,c), KEY(b desc,c desc)) ENGINE=InnoDB
PARTITION BY RANGE(c) (PARTITION p0 VALUES LESS THAN (10),
PARTITION p1 VALUES LESS THAN (20));
INSERT INTO t1 VALUES (1,1,5),(2,0,12);
SELECT c FROM t1 FORCE INDEX(b) WHERE b=0 AND c>=2 ORDER BY c;
c
12
set @js='$out';
select json_extract(@js,'$**.partitions_index_scan_method');
json_extract(@js,'$**.partitions_index_scan_method')
["merge_ordered_scans"]
DROP TABLE t1;
# prefix-only KEY_OR_NEXT / PREFIX_LAST_OR_PREV (opt_sum)
# merged scan
CREATE TABLE t1 (a INT, b INT, c INT NOT NULL, PRIMARY KEY(a,c), KEY k(b,c))
ENGINE=InnoDB
PARTITION BY RANGE(c) (PARTITION p0 VALUES LESS THAN (10),
PARTITION p1 VALUES LESS THAN (20),
PARTITION p2 VALUES LESS THAN (30));
# no b=1 in p0 or p2
INSERT INTO t1 VALUES (1,2,5),(2,1,15),(3,0,22);
EXPLAIN
SELECT MIN(c) FROM t1 WHERE b=1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
FLUSH STATUS;
SELECT MIN(c) FROM t1 WHERE b=1;
MIN(c)
15
# 3 (merged scan: p0, p1, p2)
SHOW STATUS LIKE 'Handler_read_key';
Variable_name Value
Handler_read_key 3
EXPLAIN
SELECT MAX(c) FROM t1 WHERE b=1;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
FLUSH STATUS;
SELECT MAX(c) FROM t1 WHERE b=1;
MAX(c)
15
# 3 (merged scan: p0, p1, p2)
SHOW STATUS LIKE 'Handler_read_key';
Variable_name Value
Handler_read_key 3
DROP TABLE t1;
# index_first with prefix does not qualify Case 2
# neither does index_read_map with AFTER_KEY
CREATE TABLE t1 (a INT, b INT, KEY(a,b)) ENGINE=InnoDB
PARTITION BY RANGE COLUMNS(b) (PARTITION p1 VALUES LESS THAN (100),
PARTITION p2 VALUES LESS THAN (200),
PARTITION p3 VALUES LESS THAN MAXVALUE);
INSERT INTO t1 SELECT seq % 3 + 1, seq FROM seq_1_to_300;
ANALYZE TABLE t1 PERSISTENT FOR ALL;
Table Op Msg_type Msg_text
test.t1 analyze status Engine-independent statistics collected
test.t1 analyze status OK
EXPLAIN SELECT a, MIN(b) FROM t1 GROUP BY a;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range NULL a 10 NULL 4 Using index for group-by
SELECT a, MIN(b) FROM t1 GROUP BY a;
a MIN(b)
1 3
2 1
3 2
set @js='$out';
select json_extract(@js,'$**.partitions_index_scan_method');
json_extract(@js,'$**.partitions_index_scan_method')
["merge_ordered_scans"]
DROP TABLE t1;
# index_first with prefix does not qualify Case 2
# neither does index_read_map with AFTER_KEY
CREATE TABLE t1 (a INT, c INT, KEY(a,c)) ENGINE=InnoDB
PARTITION BY RANGE COLUMNS(c) (PARTITION p1 VALUES LESS THAN (100),
PARTITION p2 VALUES LESS THAN (200),
PARTITION p3 VALUES LESS THAN MAXVALUE);
INSERT INTO t1 SELECT 1, seq FROM seq_1_to_99;
INSERT INTO t1 SELECT 2, seq FROM seq_100_to_199;
INSERT INTO t1 SELECT 3, seq FROM seq_1_to_99;
ANALYZE TABLE t1 PERSISTENT FOR ALL;
Table Op Msg_type Msg_text
test.t1 analyze status Engine-independent statistics collected
test.t1 analyze status OK
EXPLAIN SELECT a, MIN(c) FROM t1 GROUP BY a;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range NULL a 10 NULL 4 Using index for group-by
SELECT a, MIN(c) FROM t1 GROUP BY a;
a MIN(c)
1 1
2 100
3 1
set @js='$out';
select json_extract(@js,'$**.partitions_index_scan_method');
json_extract(@js,'$**.partitions_index_scan_method')
["merge_ordered_scans"]
DROP TABLE t1;
# AFTER_KEY / BEFORE_KEY merged scan
CREATE TABLE t1 (a INT, b INT, c INT NOT NULL, PRIMARY KEY(a,c), KEY k(b,c))
ENGINE=InnoDB
PARTITION BY RANGE(c) (PARTITION p0 VALUES LESS THAN (10),
PARTITION p1 VALUES LESS THAN (20),
PARTITION p2 VALUES LESS THAN (30));
INSERT INTO t1 VALUES
(1,1,5),(2,2,7),(3,2,10),(4,1,15),(5,2,18),(6,0,22),(7,1,25);
EXPLAIN
SELECT MIN(c) FROM t1 WHERE b=1 AND c>5;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
FLUSH STATUS;
SELECT MIN(c) FROM t1 WHERE b=1 AND c>5;
MIN(c)
15
# 3 (merged: p0, p1, p2)
SHOW STATUS LIKE 'Handler_read_key';
Variable_name Value
Handler_read_key 3
EXPLAIN
SELECT MAX(c) FROM t1 WHERE b=1 AND c<25;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Select tables optimized away
FLUSH STATUS;
SELECT MAX(c) FROM t1 WHERE b=1 AND c<25;
MAX(c)
15
# 3 (merged: p0, p1, p2)
SHOW STATUS LIKE 'Handler_read_key';
Variable_name Value
Handler_read_key 3
DROP TABLE t1;
# KEY_OR_PREV needs a reverse partition scan.
# Ordered scan (HASH)
CREATE TABLE t1 (a INT, KEY k(a)) ENGINE=InnoDB
PARTITION BY HASH(a) PARTITIONS 3;
INSERT INTO t1 SELECT seq FROM seq_1_to_9;
HANDLER t1 OPEN;
HANDLER t1 READ k <= (5);
a
5
HANDLER t1 READ k PREV;
a
4
HANDLER t1 READ k PREV;
a
3
HANDLER t1 READ k <= (0);
a
HANDLER t1 CLOSE;
DROP TABLE t1;
# KEY_OR_PREV merged scan
CREATE TABLE t1 (a INT, b INT, c INT, PRIMARY KEY(a,c), KEY k(b,c))
ENGINE=InnoDB
PARTITION BY RANGE(c) (PARTITION p0 VALUES LESS THAN (10),
PARTITION p1 VALUES LESS THAN (20));
INSERT INTO t1 VALUES (1,1,5),(2,2,7),(3,1,15);
HANDLER t1 OPEN;
HANDLER t1 READ k <= (1,17);
a b c
3 1 15
HANDLER t1 READ k PREV;
a b c
1 1 5
HANDLER t1 CLOSE;
DROP TABLE t1;
# A range spanning several prefix values:
# QUICK_SELECT_DESC reads PREFIX_LAST_OR_PREV (2,15), keypart_map=b,c
CREATE TABLE t1 (a INT, b INT, c INT, PRIMARY KEY(a,c), KEY k(b,c)) ENGINE=InnoDB
PARTITION BY RANGE(c) (PARTITION p0 VALUES LESS THAN (10),
PARTITION p1 VALUES LESS THAN (20));
INSERT INTO t1 VALUES (1,1,7),(2,2,7),(3,1,12),(4,2,12);
EXPLAIN SELECT b, c FROM t1 FORCE INDEX(k)
WHERE b BETWEEN 1 AND 2 AND c BETWEEN 5 AND 15 ORDER BY b DESC, c DESC;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t1 range k k 9 NULL 4 Using where; Using index
SELECT b, c FROM t1 FORCE INDEX(k)
WHERE b BETWEEN 1 AND 2 AND c BETWEEN 5 AND 15 ORDER BY b DESC, c DESC;
b c
2 12
2 7
1 12
1 7
set @js='$out';
select json_extract(@js,'$**.partitions_index_scan_method');
json_extract(@js,'$**.partitions_index_scan_method')
["merge_ordered_scans"]
DROP TABLE t1;
# reset m_unordered_prefix_len: single partition scan skips
# can_skip_merging_scans()
CREATE TABLE t1 (a INT, b INT, c INT, PRIMARY KEY(a,c),
KEY k(b,c), KEY kc(c,b)) ENGINE=InnoDB
PARTITION BY RANGE(c) (PARTITION p0 VALUES LESS THAN (10),
PARTITION p1 VALUES LESS THAN (20));
INSERT INTO t1 VALUES (1,1,5),(2,1,12),(3,2,13);
HANDLER t1 OPEN;
# 2 partitions, Case 2 on k(b,c): m_unordered_prefix_len = len(b)
HANDLER t1 READ k = (1);
a b c
1 1 5
# full key of kc binds c: single partition p1, reset
HANDLER t1 READ kc = (13,2);
a b c
3 2 13
# expect (2,1,12)
HANDLER t1 READ kc PREV;
a b c
2 1 12
HANDLER t1 CLOSE;
DROP TABLE t1;

View File

@@ -1,5 +1,6 @@
--source include/have_partition.inc
--source include/have_sequence.inc
--source include/have_innodb.inc
--source include/no_view_protocol.inc
--disable_ps2_protocol
--disable_cursor_protocol
@@ -251,8 +252,8 @@ select json_extract(@js,'$**.partitions_index_scan_method');
DROP TABLE t1;
--echo ## Case 2: calls to ha_partition::read_range_first and
--echo ## ha_partition::index_read_map, via loose index scan
--echo ## Case 2 loose index scan: calls to read_range_first iterates;
--echo ## index_read_map with AFTER_KEY is merged
eval
CREATE TABLE t1 (
a INT,
@@ -293,7 +294,8 @@ CREATE TABLE t1 (
insert into t1 select t2.seq, t3.seq from seq_100_to_200 t2, seq_1_to_50 t3;
--echo ## Case 2: Unordered scans with MRR
--echo ## Case 2: iterated partition scans with MRR with ASC key;
--echo ## merged scan with index_read_map with DESC key
flush status;
select * from t1 where c=113 and d between 25 and 35 order by d limit 3;
show status like 'handler_read_%';
@@ -572,5 +574,180 @@ SELECT c FROM t1 WHERE b = 1 ORDER BY c $opposite_direction;
DROP TABLE t1;
--echo #
--echo # MDEV-41366 Wrong result with Case 2 of non-merging partition index scan
--echo #
--echo # prefix-only PREFIX_LAST_OR_PREV, merged scan
CREATE TABLE t1 (a INT, b INT, c INT, PRIMARY KEY(a,c), KEY(b,c)) ENGINE=InnoDB
PARTITION BY RANGE(c) (PARTITION p0 VALUES LESS THAN (10),
PARTITION p1 VALUES LESS THAN (20));
INSERT INTO t1 VALUES (1,1,5),(2,0,12);
let $q=
SELECT c FROM t1 FORCE INDEX(b) WHERE b=1 AND c<=15 ORDER BY c DESC;
eval $q;
let $out=`analyze format=json $q`;
evalp set @js='$out';
select json_extract(@js,'$**.partitions_index_scan_method');
DROP TABLE t1;
--echo # same but with desc key
CREATE TABLE t1 (a INT, b INT, c INT, PRIMARY KEY(a,c), KEY(b desc,c desc)) ENGINE=InnoDB
PARTITION BY RANGE(c) (PARTITION p0 VALUES LESS THAN (10),
PARTITION p1 VALUES LESS THAN (20));
INSERT INTO t1 VALUES (1,1,5),(2,0,12);
let $q=
SELECT c FROM t1 FORCE INDEX(b) WHERE b=0 AND c>=2 ORDER BY c;
eval $q;
let $out=`analyze format=json $q`;
evalp set @js='$out';
select json_extract(@js,'$**.partitions_index_scan_method');
DROP TABLE t1;
--echo # prefix-only KEY_OR_NEXT / PREFIX_LAST_OR_PREV (opt_sum)
--echo # merged scan
CREATE TABLE t1 (a INT, b INT, c INT NOT NULL, PRIMARY KEY(a,c), KEY k(b,c))
ENGINE=InnoDB
PARTITION BY RANGE(c) (PARTITION p0 VALUES LESS THAN (10),
PARTITION p1 VALUES LESS THAN (20),
PARTITION p2 VALUES LESS THAN (30));
--echo # no b=1 in p0 or p2
INSERT INTO t1 VALUES (1,2,5),(2,1,15),(3,0,22);
EXPLAIN
SELECT MIN(c) FROM t1 WHERE b=1;
FLUSH STATUS;
SELECT MIN(c) FROM t1 WHERE b=1;
--echo # 3 (merged scan: p0, p1, p2)
SHOW STATUS LIKE 'Handler_read_key';
EXPLAIN
SELECT MAX(c) FROM t1 WHERE b=1;
FLUSH STATUS;
SELECT MAX(c) FROM t1 WHERE b=1;
--echo # 3 (merged scan: p0, p1, p2)
SHOW STATUS LIKE 'Handler_read_key';
DROP TABLE t1;
--echo # index_first with prefix does not qualify Case 2
--echo # neither does index_read_map with AFTER_KEY
CREATE TABLE t1 (a INT, b INT, KEY(a,b)) ENGINE=InnoDB
PARTITION BY RANGE COLUMNS(b) (PARTITION p1 VALUES LESS THAN (100),
PARTITION p2 VALUES LESS THAN (200),
PARTITION p3 VALUES LESS THAN MAXVALUE);
INSERT INTO t1 SELECT seq % 3 + 1, seq FROM seq_1_to_300;
ANALYZE TABLE t1 PERSISTENT FOR ALL;
let $q=
SELECT a, MIN(b) FROM t1 GROUP BY a;
eval EXPLAIN $q;
eval $q;
let $out=`analyze format=json $q`;
evalp set @js='$out';
select json_extract(@js,'$**.partitions_index_scan_method');
DROP TABLE t1;
--echo # index_first with prefix does not qualify Case 2
--echo # neither does index_read_map with AFTER_KEY
CREATE TABLE t1 (a INT, c INT, KEY(a,c)) ENGINE=InnoDB
PARTITION BY RANGE COLUMNS(c) (PARTITION p1 VALUES LESS THAN (100),
PARTITION p2 VALUES LESS THAN (200),
PARTITION p3 VALUES LESS THAN MAXVALUE);
INSERT INTO t1 SELECT 1, seq FROM seq_1_to_99;
INSERT INTO t1 SELECT 2, seq FROM seq_100_to_199;
INSERT INTO t1 SELECT 3, seq FROM seq_1_to_99;
ANALYZE TABLE t1 PERSISTENT FOR ALL;
let $q=
SELECT a, MIN(c) FROM t1 GROUP BY a;
eval EXPLAIN $q;
eval $q;
let $out=`analyze format=json $q`;
evalp set @js='$out';
select json_extract(@js,'$**.partitions_index_scan_method');
DROP TABLE t1;
--echo # AFTER_KEY / BEFORE_KEY merged scan
CREATE TABLE t1 (a INT, b INT, c INT NOT NULL, PRIMARY KEY(a,c), KEY k(b,c))
ENGINE=InnoDB
PARTITION BY RANGE(c) (PARTITION p0 VALUES LESS THAN (10),
PARTITION p1 VALUES LESS THAN (20),
PARTITION p2 VALUES LESS THAN (30));
INSERT INTO t1 VALUES
(1,1,5),(2,2,7),(3,2,10),(4,1,15),(5,2,18),(6,0,22),(7,1,25);
EXPLAIN
SELECT MIN(c) FROM t1 WHERE b=1 AND c>5;
FLUSH STATUS;
SELECT MIN(c) FROM t1 WHERE b=1 AND c>5;
--echo # 3 (merged: p0, p1, p2)
SHOW STATUS LIKE 'Handler_read_key';
EXPLAIN
SELECT MAX(c) FROM t1 WHERE b=1 AND c<25;
FLUSH STATUS;
SELECT MAX(c) FROM t1 WHERE b=1 AND c<25;
--echo # 3 (merged: p0, p1, p2)
SHOW STATUS LIKE 'Handler_read_key';
DROP TABLE t1;
--echo # KEY_OR_PREV needs a reverse partition scan.
--echo # Ordered scan (HASH)
CREATE TABLE t1 (a INT, KEY k(a)) ENGINE=InnoDB
PARTITION BY HASH(a) PARTITIONS 3;
INSERT INTO t1 SELECT seq FROM seq_1_to_9;
HANDLER t1 OPEN;
HANDLER t1 READ k <= (5);
HANDLER t1 READ k PREV;
HANDLER t1 READ k PREV;
HANDLER t1 READ k <= (0);
HANDLER t1 CLOSE;
DROP TABLE t1;
--echo # KEY_OR_PREV merged scan
CREATE TABLE t1 (a INT, b INT, c INT, PRIMARY KEY(a,c), KEY k(b,c))
ENGINE=InnoDB
PARTITION BY RANGE(c) (PARTITION p0 VALUES LESS THAN (10),
PARTITION p1 VALUES LESS THAN (20));
INSERT INTO t1 VALUES (1,1,5),(2,2,7),(3,1,15);
HANDLER t1 OPEN;
HANDLER t1 READ k <= (1,17);
HANDLER t1 READ k PREV;
HANDLER t1 CLOSE;
DROP TABLE t1;
--echo # A range spanning several prefix values:
--echo # QUICK_SELECT_DESC reads PREFIX_LAST_OR_PREV (2,15), keypart_map=b,c
CREATE TABLE t1 (a INT, b INT, c INT, PRIMARY KEY(a,c), KEY k(b,c)) ENGINE=InnoDB
PARTITION BY RANGE(c) (PARTITION p0 VALUES LESS THAN (10),
PARTITION p1 VALUES LESS THAN (20));
INSERT INTO t1 VALUES (1,1,7),(2,2,7),(3,1,12),(4,2,12);
let $q=
SELECT b, c FROM t1 FORCE INDEX(k)
WHERE b BETWEEN 1 AND 2 AND c BETWEEN 5 AND 15 ORDER BY b DESC, c DESC;
eval EXPLAIN $q;
eval $q;
let $out=`analyze format=json $q`;
evalp set @js='$out';
select json_extract(@js,'$**.partitions_index_scan_method');
DROP TABLE t1;
--echo # reset m_unordered_prefix_len: single partition scan skips
--echo # can_skip_merging_scans()
CREATE TABLE t1 (a INT, b INT, c INT, PRIMARY KEY(a,c),
KEY k(b,c), KEY kc(c,b)) ENGINE=InnoDB
PARTITION BY RANGE(c) (PARTITION p0 VALUES LESS THAN (10),
PARTITION p1 VALUES LESS THAN (20));
INSERT INTO t1 VALUES (1,1,5),(2,1,12),(3,2,13);
HANDLER t1 OPEN;
--echo # 2 partitions, Case 2 on k(b,c): m_unordered_prefix_len = len(b)
HANDLER t1 READ k = (1);
--echo # full key of kc binds c: single partition p1, reset
HANDLER t1 READ kc = (13,2);
--echo # expect (2,1,12)
HANDLER t1 READ kc PREV;
HANDLER t1 CLOSE;
DROP TABLE t1;
--enable_cursor_protocol
--enable_ps2_protocol

View File

@@ -5991,7 +5991,8 @@ int ha_partition::common_index_read(uchar *buf, bool have_start_key)
}
if (have_start_key &&
(m_start_key.flag == HA_READ_PREFIX_LAST ||
(m_start_key.flag == HA_READ_KEY_OR_PREV ||
m_start_key.flag == HA_READ_PREFIX_LAST ||
m_start_key.flag == HA_READ_PREFIX_LAST_OR_PREV ||
m_start_key.flag == HA_READ_BEFORE_KEY))
{
@@ -7515,8 +7516,23 @@ bool ha_partition::can_skip_merging_scans()
(key_part->key_part_flag & HA_REVERSE_SORT) ? true : false;
if (m_index_scan_type == partition_index_read)
{
return
m_start_key.key && (m_start_key.keypart_map & prefix) == prefix;
switch (m_start_key.flag) {
case HA_READ_AFTER_KEY:
case HA_READ_BEFORE_KEY:
case HA_READ_KEY_OR_NEXT:
case HA_READ_KEY_OR_PREV:
case HA_READ_PREFIX_LAST_OR_PREV:
return false;
case HA_READ_KEY_EXACT:
case HA_READ_PREFIX:
case HA_READ_PREFIX_LAST:
return m_start_key.key &&
(m_start_key.keypart_map & prefix) == prefix;
default:
/* SPATIAL keys have a single key part, so Case 2 cannot apply */
DBUG_ASSERT(0);
return false;
}
}
else if (m_index_scan_type == partition_read_range)
{
@@ -7595,6 +7611,7 @@ int ha_partition::partition_scan_set_up(uchar * buf, bool idx_read_flag)
DBUG_PRINT("info", ("index scan using the single partition %u",
(uint) m_part_spec.start_part));
m_ordered_scan_ongoing= FALSE;
m_unordered_prefix_len= 0;
}
else
{