From 9980287a0bdd12fd3c0987d61490ec3ccf7e7d2e Mon Sep 17 00:00:00 2001 From: yy <736262857@qq.com> Date: Thu, 23 Jul 2026 09:46:40 +0000 Subject: [PATCH 1/2] planner, util/ranger: fix partial index pruning due to shared Range pointer UnionRanges builds sortRange objects holding pointers to the original Range. When merging adjacent or overlapping ranges, it mutates the pointed Range directly, corrupting the caller's input because the pointer is shared across all sortRange items. This causes CheckPartialIndexes to incorrectly determine that partial index conditions are implied by query conditions, leading to wrong index pruning. Ref #69779 --- pkg/util/ranger/ranger.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pkg/util/ranger/ranger.go b/pkg/util/ranger/ranger.go index 5f3ced201ee7b..897b34ffa667b 100644 --- a/pkg/util/ranger/ranger.go +++ b/pkg/util/ranger/ranger.go @@ -681,7 +681,11 @@ func UnionRanges(sctx *rangerctx.RangerContext, ranges Ranges, mergeConsecutive return bytes.Compare(i.encodedStart, j.encodedStart) }) ranges = ranges[:0] - lastRange := objects[0] + lastRange := &sortRange{ + originalValue: objects[0].originalValue.Clone(), + encodedStart: objects[0].encodedStart, + encodedEnd: objects[0].encodedEnd, + } for i := 1; i < len(objects); i++ { if (mergeConsecutive && bytes.Compare(lastRange.encodedEnd, objects[i].encodedStart) >= 0) || (!mergeConsecutive && bytes.Compare(lastRange.encodedEnd, objects[i].encodedStart) > 0) { @@ -692,7 +696,9 @@ func UnionRanges(sctx *rangerctx.RangerContext, ranges Ranges, mergeConsecutive } } else { ranges = append(ranges, lastRange.originalValue) - lastRange = objects[i] + lastRange.encodedStart = objects[i].encodedStart + lastRange.encodedEnd = objects[i].encodedEnd + lastRange.originalValue = objects[i].originalValue.Clone() } } ranges = append(ranges, lastRange.originalValue) From c1c14edaff1a504e52068642e0db7c175f73d4d4 Mon Sep 17 00:00:00 2001 From: yy <736262857@qq.com> Date: Thu, 23 Jul 2026 14:37:09 +0000 Subject: [PATCH 2/2] planner: add integration test for partial index pruning (issue #69779) Verify that a >= 0 does NOT imply a < 3 for partial index pruning, which is the regression case from the shared pointer bug in UnionRanges. --- .../core/casetest/index/partialindex.result | 18 ++++++++++++++++++ .../core/casetest/index/partialindex.test | 9 +++++++++ 2 files changed, 27 insertions(+) diff --git a/tests/integrationtest/r/planner/core/casetest/index/partialindex.result b/tests/integrationtest/r/planner/core/casetest/index/partialindex.result index 0d8d7d469cb64..10dbd51d1a803 100644 --- a/tests/integrationtest/r/planner/core/casetest/index/partialindex.result +++ b/tests/integrationtest/r/planner/core/casetest/index/partialindex.result @@ -107,4 +107,22 @@ IndexMerge 5555.56 root type: union ├─IndexRangeScan(Build) 3333.33 cop[tikv] table:tt, index:idx1(a) range:(10,+inf], keep order:false, stats:pseudo ├─IndexRangeScan(Build) 3333.33 cop[tikv] table:tt, index:idx2(b) range:(10,+inf], keep order:false, stats:pseudo └─TableRowIDScan(Probe) 5555.56 cop[tikv] table:tt keep order:false, stats:pseudo +drop table if exists t3; +create table t3(id int primary key, a int not null, b int not null, index pi(b) where a < 3); +insert into t3 values (1, 0, 1), (2, 1, 2), (3, 2, 3), (4, 3, 4), (5, 10, 5); +select id, a, b from t3 where a >= 0 order by b limit 5; +id a b +1 0 1 +2 1 2 +3 2 3 +4 3 4 +5 10 5 +select id, a, b from t3 ignore index(pi) where a >= 0 order by b limit 5; +id a b +1 0 1 +2 1 2 +3 2 3 +4 3 4 +5 10 5 +drop table t3; drop table t, tt; diff --git a/tests/integrationtest/t/planner/core/casetest/index/partialindex.test b/tests/integrationtest/t/planner/core/casetest/index/partialindex.test index 652e12eb90b47..0216ea3c32586 100644 --- a/tests/integrationtest/t/planner/core/casetest/index/partialindex.test +++ b/tests/integrationtest/t/planner/core/casetest/index/partialindex.test @@ -36,4 +36,13 @@ explain format='brief' select /*+ inl_join(t2) */ * from tt t1, tt t2 where t1.b explain format='brief' select /*+ use_index_merge(t, idx1, idx2) */ * from t where (a > 10) or (b > 10); explain format='brief' select /*+ use_index_merge(tt, idx1, idx2) */ * from tt where (a > 10) or (b > 10); +# TestPartialIndexImplicationCheck +# a >= 0 does NOT imply a < 3, partial index pi should NOT be used. +drop table if exists t3; +create table t3(id int primary key, a int not null, b int not null, index pi(b) where a < 3); +insert into t3 values (1, 0, 1), (2, 1, 2), (3, 2, 3), (4, 3, 4), (5, 10, 5); +select id, a, b from t3 where a >= 0 order by b limit 5; +select id, a, b from t3 ignore index(pi) where a >= 0 order by b limit 5; +drop table t3; + drop table t, tt;