Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

util/ranger: don't merge consecutive ranges when building ranges for cnf item (#41661) #44356

Merged
Merged
Show file tree
Hide file tree
Changes from all 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
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