Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions pkg/util/ranger/ranger.go
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,8 @@ type sortRange struct {
// For two intervals [a, b], [c, d], we have guaranteed that a <= c. If b >= c. Then two intervals are overlapped.
// And this two can be merged as [a, max(b, d)].
// Otherwise they aren't overlapped.
//
// The function does not modify the caller's ranges (see issue #69779).
func UnionRanges(sctx *rangerctx.RangerContext, ranges Ranges, mergeConsecutive bool) (Ranges, error) {
if len(ranges) == 0 {
return nil, nil
Expand Down Expand Up @@ -682,9 +684,14 @@ func UnionRanges(sctx *rangerctx.RangerContext, ranges Ranges, mergeConsecutive
})
ranges = ranges[:0]
lastRange := objects[0]
detached := false
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) {
if !detached {
lastRange.originalValue = lastRange.originalValue.Clone()
detached = true
}
if bytes.Compare(lastRange.encodedEnd, objects[i].encodedEnd) < 0 {
lastRange.encodedEnd = objects[i].encodedEnd
lastRange.originalValue.HighVal = objects[i].originalValue.HighVal
Expand All @@ -693,6 +700,7 @@ func UnionRanges(sctx *rangerctx.RangerContext, ranges Ranges, mergeConsecutive
} else {
ranges = append(ranges, lastRange.originalValue)
lastRange = objects[i]
detached = false
}
}
ranges = append(ranges, lastRange.originalValue)
Expand Down
76 changes: 76 additions & 0 deletions pkg/util/ranger/ranger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2633,6 +2633,82 @@ func TestMinAccessCondsForDNFCond(t *testing.T) {
}
}

func TestUnionRangesPreservesCallerRanges(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
sctx := tk.Session()
rctx := sctx.GetRangerCtx()

// Build 3 ranges where r1 and r2 overlap so they trigger the merge path.
// r1 = [1, 3], r2 = [2, 4], r3 = [5, 7].
binaryCollator := collate.GetBinaryCollator()
r1 := &ranger.Range{
LowVal: []types.Datum{types.NewIntDatum(1)},
HighVal: []types.Datum{types.NewIntDatum(3)},
LowExclude: false,
HighExclude: false,
Collators: []collate.Collator{binaryCollator},
}
r2 := &ranger.Range{
LowVal: []types.Datum{types.NewIntDatum(2)},
HighVal: []types.Datum{types.NewIntDatum(4)},
LowExclude: false,
HighExclude: false,
Collators: []collate.Collator{binaryCollator},
}
r3 := &ranger.Range{
LowVal: []types.Datum{types.NewIntDatum(5)},
HighVal: []types.Datum{types.NewIntDatum(7)},
LowExclude: false,
HighExclude: false,
Collators: []collate.Collator{binaryCollator},
}
input := ranger.Ranges{r1, r2, r3}

// r1 and r2 overlap -> merged to [1, 4]. r3 is disjoint -> kept as [5, 7].
// Expected result: [[1, 4], [5, 7]].
result, err := ranger.UnionRanges(rctx, input, false /* mergeConsecutive */)
require.NoError(t, err)
require.Equal(t, 2, len(result))
require.Equal(t, int64(1), result[0].LowVal[0].GetInt64())
require.Equal(t, int64(4), result[0].HighVal[0].GetInt64())
require.Equal(t, int64(5), result[1].LowVal[0].GetInt64())
require.Equal(t, int64(7), result[1].HighVal[0].GetInt64())

// The original range objects must not be modified.
require.Equal(t, int64(1), r1.LowVal[0].GetInt64())
require.Equal(t, int64(3), r1.HighVal[0].GetInt64())
require.False(t, r1.HighExclude)
require.Equal(t, int64(2), r2.LowVal[0].GetInt64())
require.Equal(t, int64(4), r2.HighVal[0].GetInt64())
require.False(t, r2.HighExclude)
require.Equal(t, int64(5), r3.LowVal[0].GetInt64())
require.Equal(t, int64(7), r3.HighVal[0].GetInt64())
require.False(t, r3.HighExclude)

// [1, 4] and [5, 7] are consecutive after encoding (PrefixNext makes
// the encoded end of [1, 4] touch the encoded start of [5, 7]),
// so all three ranges merge into one: [1, 7].
input = ranger.Ranges{r1, r2, r3}
result2, err := ranger.UnionRanges(rctx, input, true /* mergeConsecutive */)
require.NoError(t, err)
require.Equal(t, 1, len(result2))
require.Equal(t, int64(1), result2[0].LowVal[0].GetInt64())
require.Equal(t, int64(7), result2[0].HighVal[0].GetInt64())

// The original range objects still must not be modified.
require.Equal(t, int64(1), r1.LowVal[0].GetInt64())
require.Equal(t, int64(3), r1.HighVal[0].GetInt64())
require.False(t, r1.HighExclude)
require.Equal(t, int64(2), r2.LowVal[0].GetInt64())
require.Equal(t, int64(4), r2.HighVal[0].GetInt64())
require.False(t, r2.HighExclude)
require.Equal(t, int64(5), r3.LowVal[0].GetInt64())
require.Equal(t, int64(7), r3.HighVal[0].GetInt64())
require.False(t, r3.HighExclude)
}

func TestBinCollationRangeForIndex(t *testing.T) {
store, dom := testkit.CreateMockStoreAndDomain(t)
tk := testkit.NewTestKit(t, store)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,27 @@ 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);
explain format='brief' select * from t3 where a >= 0;
id estRows task access object operator info
TableReader 3333.33 root data:Selection
└─Selection 3333.33 cop[tikv] ge(planner__core__casetest__index__partialindex.t3.a, 0)
└─TableFullScan 10000.00 cop[tikv] table:t3 keep order:false, stats:pseudo
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;
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,14 @@ 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);
explain format='brief' select * from t3 where a >= 0;
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;