Skip to content

Commit

Permalink
util/ranger: don't merge consecutive ranges when building ranges for …
Browse files Browse the repository at this point in the history
…cnf item (#41661) (#44356)

close #41572
  • Loading branch information
ti-chi-bot authored Jun 2, 2023
1 parent ede0681 commit f9277d8
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 6 deletions.
26 changes: 20 additions & 6 deletions util/ranger/detacher.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,14 @@ func extractIndexPointRangesForCNF(sctx sessionctx.Context, conds []expression.E
if colSets.Len() == 0 {
continue
}
res, err := DetachCondAndBuildRangeForIndex(sctx, tmpConds, cols, lengths, rangeMaxSize)
// When we build ranges for the CNF item, we choose not to merge consecutive ranges because we hope to get point
// ranges here. See https://github.com/pingcap/tidb/issues/41572 for more details.
//
// Here is an example. Assume that the index is `idx(a,b,c)` and the condition is `((a,b) in ((1,1),(1,2)) and c = 1`.
// We build ranges for `(a,b) in ((1,1),(1,2))` and get `[1 1, 1 1] [1 2, 1 2]`, which are point ranges and we can
// append `c = 1` to the point ranges. However, if we choose to merge consecutive ranges here, we get `[1 1, 1 2]`,
// which are not point ranges, and we cannot append `c = 1` anymore.
res, err := detachCondAndBuildRangeWithoutMerging(sctx, tmpConds, cols, lengths, rangeMaxSize)
if err != nil {
return nil, -1, nil, err
}
Expand Down Expand Up @@ -821,11 +828,9 @@ func DetachCondAndBuildRangeForIndex(sctx sessionctx.Context, conditions []expre
return d.detachCondAndBuildRangeForCols()
}

// DetachCondAndBuildRangeForPartition will detach the index filters from table filters.
// rangeMaxSize is the max memory limit for ranges. O indicates no memory limit. If you ask that all conditions must be used
// for building ranges, set rangeMemQuota to 0 to avoid range fallback.
// The returned values are encapsulated into a struct DetachRangeResult, see its comments for explanation.
func DetachCondAndBuildRangeForPartition(sctx sessionctx.Context, conditions []expression.Expression, cols []*expression.Column,
// detachCondAndBuildRangeWithoutMerging detaches the index filters from table filters and uses them to build ranges.
// When building ranges, it doesn't merge consecutive ranges.
func detachCondAndBuildRangeWithoutMerging(sctx sessionctx.Context, conditions []expression.Expression, cols []*expression.Column,
lengths []int, rangeMaxSize int64) (*DetachRangeResult, error) {
d := &rangeDetacher{
sctx: sctx,
Expand All @@ -838,6 +843,15 @@ func DetachCondAndBuildRangeForPartition(sctx sessionctx.Context, conditions []e
return d.detachCondAndBuildRangeForCols()
}

// DetachCondAndBuildRangeForPartition will detach the index filters from table filters.
// rangeMaxSize is the max memory limit for ranges. O indicates no memory limit. If you ask that all conditions must be used
// for building ranges, set rangeMemQuota to 0 to avoid range fallback.
// The returned values are encapsulated into a struct DetachRangeResult, see its comments for explanation.
func DetachCondAndBuildRangeForPartition(sctx sessionctx.Context, conditions []expression.Expression, cols []*expression.Column,
lengths []int, rangeMaxSize int64) (*DetachRangeResult, error) {
return detachCondAndBuildRangeWithoutMerging(sctx, conditions, cols, lengths, rangeMaxSize)
}

type rangeDetacher struct {
sctx sessionctx.Context
allConds []expression.Expression
Expand Down
27 changes: 27 additions & 0 deletions util/ranger/ranger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1000,6 +1000,33 @@ func TestCompIndexMultiColDNF2(t *testing.T) {
}
}

func TestIssue41572(t *testing.T) {
store := testkit.CreateMockStore(t)

testKit := testkit.NewTestKit(t, store)
testKit.MustExec("use test")
testKit.MustExec("drop table if exists t")
testKit.MustExec("create table t(a varchar(100), b int, c int, d int, index idx(a, b, c))")
testKit.MustExec("insert into t values ('t',1,1,1),('t',1,3,3),('t',2,1,3),('t',2,3,1),('w',0,3,3),('z',0,1,1)")

var input []string
var output []struct {
SQL string
Plan []string
Result []string
}
rangerSuiteData.LoadTestCases(t, &input, &output)
for i, tt := range input {
testdata.OnRecord(func() {
output[i].SQL = tt
output[i].Plan = testdata.ConvertRowsToStrings(testKit.MustQuery("explain " + tt).Rows())
output[i].Result = testdata.ConvertRowsToStrings(testKit.MustQuery(tt).Rows())
})
testKit.MustQuery("explain " + tt).Check(testkit.Rows(output[i].Plan...))
testKit.MustQuery(tt).Sort().Check(testkit.Rows(output[i].Result...))
}
}

func TestPrefixIndexMultiColDNF(t *testing.T) {
store := testkit.CreateMockStore(t)

Expand Down
7 changes: 7 additions & 0 deletions util/ranger/testdata/ranger_suite_in.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@
"select * from t where ((a = 1 and b = 1) or (a = 2 and b = 2)) and c > 2;"
]
},
{
"name": "TestIssue41572",
"cases": [
"select * from t use index (idx) where ((a = 't' and b = 1) or (a = 't' and b = 2) or (a = 'w' and b = 0)) and c > 2",
"select * from t use index (idx) where ((a = 't' and b = 1) or (a = 't' and b = 2) or (a = 'w' and b = 0)) and d > 2"
]
},
{
"name": "TestPrefixIndexMultiColDNF",
"cases": [
Expand Down
32 changes: 32 additions & 0 deletions util/ranger/testdata/ranger_suite_out.json
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,38 @@
}
]
},
{
"Name": "TestIssue41572",
"Cases": [
{
"SQL": "select * from t use index (idx) where ((a = 't' and b = 1) or (a = 't' and b = 2) or (a = 'w' and b = 0)) and c > 2",
"Plan": [
"IndexLookUp_7 1.00 root ",
"├─IndexRangeScan_5(Build) 1.00 cop[tikv] table:t, index:idx(a, b, c) range:(\"t\" 1 2,\"t\" 1 +inf], (\"t\" 2 2,\"t\" 2 +inf], (\"w\" 0 2,\"w\" 0 +inf], keep order:false, stats:pseudo",
"└─TableRowIDScan_6(Probe) 1.00 cop[tikv] table:t keep order:false, stats:pseudo"
],
"Result": [
"t 1 3 3",
"t 2 3 1",
"w 0 3 3"
]
},
{
"SQL": "select * from t use index (idx) where ((a = 't' and b = 1) or (a = 't' and b = 2) or (a = 'w' and b = 0)) and d > 2",
"Plan": [
"IndexLookUp_8 0.10 root ",
"├─IndexRangeScan_5(Build) 0.30 cop[tikv] table:t, index:idx(a, b, c) range:[\"t\" 1,\"t\" 1], [\"t\" 2,\"t\" 2], [\"w\" 0,\"w\" 0], keep order:false, stats:pseudo",
"└─Selection_7(Probe) 0.10 cop[tikv] gt(test.t.d, 2)",
" └─TableRowIDScan_6 0.30 cop[tikv] table:t keep order:false, stats:pseudo"
],
"Result": [
"t 1 3 3",
"t 2 1 3",
"w 0 3 3"
]
}
]
},
{
"Name": "TestPrefixIndexMultiColDNF",
"Cases": [
Expand Down

0 comments on commit f9277d8

Please sign in to comment.