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

planner: refine index join range display for clustered index #38259

Merged
merged 8 commits into from
Sep 30, 2022
Merged
23 changes: 18 additions & 5 deletions planner/core/exhaust_physical_plans.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"bytes"
"fmt"
"math"
"strings"
"unsafe"

"github.com/pingcap/errors"
Expand Down Expand Up @@ -804,13 +805,14 @@ func (p *LogicalJoin) buildIndexJoinInner2TableScan(
if helper == nil {
return nil
}
innerTask = p.constructInnerTableScanTask(ds, helper.chosenRanges.Range(), outerJoinKeys, us, false, false, avgInnerRowCnt)
rangeInfo := helper.buildRangeDecidedByInformation(helper.chosenPath.IdxCols, outerJoinKeys)
innerTask = p.constructInnerTableScanTask(ds, helper.chosenRanges.Range(), outerJoinKeys, us, rangeInfo, false, false, avgInnerRowCnt)
// The index merge join's inner plan is different from index join, so we
// should construct another inner plan for it.
// Because we can't keep order for union scan, if there is a union scan in inner task,
// we can't construct index merge join.
if us == nil {
innerTask2 = p.constructInnerTableScanTask(ds, helper.chosenRanges.Range(), outerJoinKeys, us, true, !prop.IsSortItemEmpty() && prop.SortItems[0].Desc, avgInnerRowCnt)
innerTask2 = p.constructInnerTableScanTask(ds, helper.chosenRanges.Range(), outerJoinKeys, us, rangeInfo, true, !prop.IsSortItemEmpty() && prop.SortItems[0].Desc, avgInnerRowCnt)
}
ranges = helper.chosenRanges
} else {
Expand All @@ -834,13 +836,23 @@ func (p *LogicalJoin) buildIndexJoinInner2TableScan(
return nil
}
ranges := ranger.FullIntRange(mysql.HasUnsignedFlag(pkCol.RetType.GetFlag()))
innerTask = p.constructInnerTableScanTask(ds, ranges, outerJoinKeys, us, false, false, avgInnerRowCnt)
var buffer strings.Builder
buffer.WriteString("[")
for i, key := range outerJoinKeys {
if i != 0 {
buffer.WriteString(" ")
}
buffer.WriteString(key.String())
}
buffer.WriteString("]")
rangeInfo := buffer.String()
innerTask = p.constructInnerTableScanTask(ds, ranges, outerJoinKeys, us, rangeInfo, false, false, avgInnerRowCnt)
// The index merge join's inner plan is different from index join, so we
// should construct another inner plan for it.
// Because we can't keep order for union scan, if there is a union scan in inner task,
// we can't construct index merge join.
if us == nil {
innerTask2 = p.constructInnerTableScanTask(ds, ranges, outerJoinKeys, us, true, !prop.IsSortItemEmpty() && prop.SortItems[0].Desc, avgInnerRowCnt)
innerTask2 = p.constructInnerTableScanTask(ds, ranges, outerJoinKeys, us, rangeInfo, true, !prop.IsSortItemEmpty() && prop.SortItems[0].Desc, avgInnerRowCnt)
}
}
var (
Expand Down Expand Up @@ -960,6 +972,7 @@ func (p *LogicalJoin) constructInnerTableScanTask(
ranges ranger.Ranges,
outerJoinKeys []*expression.Column,
us *LogicalUnionScan,
rangeInfo string,
keepOrder bool,
desc bool,
rowCount float64,
Expand All @@ -977,7 +990,7 @@ func (p *LogicalJoin) constructInnerTableScanTask(
DBName: ds.DBName,
filterCondition: ds.pushedDownConds,
Ranges: ranges,
rangeDecidedBy: outerJoinKeys,
rangeInfo: rangeInfo,
KeepOrder: keepOrder,
Desc: desc,
physicalTableID: ds.physicalTableID,
Expand Down
16 changes: 6 additions & 10 deletions planner/core/explain.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,15 +173,11 @@ func (p *PhysicalTableScan) ExplainNormalizedInfo() string {
// OperatorInfo implements dataAccesser interface.
func (p *PhysicalTableScan) OperatorInfo(normalized bool) string {
var buffer strings.Builder
if len(p.rangeDecidedBy) > 0 {
buffer.WriteString("range: decided by [")
for i, rangeDecidedBy := range p.rangeDecidedBy {
if i != 0 {
buffer.WriteString(" ")
}
buffer.WriteString(rangeDecidedBy.String())
}
buffer.WriteString("], ")
if len(p.rangeInfo) > 0 {
// TODO: deal with normalized case
buffer.WriteString("range: decided by ")
buffer.WriteString(p.rangeInfo)
buffer.WriteString(", ")
} else if p.haveCorCol() {
if normalized {
buffer.WriteString("range: decided by ")
Expand Down Expand Up @@ -232,7 +228,7 @@ func (p *PhysicalTableScan) haveCorCol() bool {
}

func (p *PhysicalTableScan) isFullScan() bool {
if len(p.rangeDecidedBy) > 0 || p.haveCorCol() {
if len(p.rangeInfo) > 0 || p.haveCorCol() {
return false
}
var unsignedIntHandle bool
Expand Down
4 changes: 3 additions & 1 deletion planner/core/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1721,7 +1721,7 @@ func TestInvisibleIndex(t *testing.T) {
tk.MustExec("admin check index t i_a")
}

// for issue #14822
// for issue #14822 and #38258
func TestIndexJoinTableRange(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
Expand All @@ -1730,6 +1730,8 @@ func TestIndexJoinTableRange(t *testing.T) {
tk.MustExec("drop table if exists t1, t2")
tk.MustExec("create table t1(a int, b int, primary key (a), key idx_t1_b (b))")
tk.MustExec("create table t2(a int, b int, primary key (a), key idx_t1_b (b))")
tk.MustExec("create table t3(a int, b int, c int)")
tk.MustExec("create table t4(a int, b int, c int, primary key (a, b) clustered)")

var input []string
var output []struct {
Expand Down
8 changes: 3 additions & 5 deletions planner/core/physical_plans.go
Original file line number Diff line number Diff line change
Expand Up @@ -788,7 +788,7 @@ type PhysicalTableScan struct {

physicalTableID int64

rangeDecidedBy []*expression.Column
rangeInfo string

// HandleIdx is the index of handle, which is only used for admin check table.
HandleIdx []int
Expand Down Expand Up @@ -839,7 +839,7 @@ func (ts *PhysicalTableScan) Clone() (PhysicalPlan, error) {
if ts.Hist != nil {
clonedScan.Hist = ts.Hist.Copy()
}
clonedScan.rangeDecidedBy = cloneCols(ts.rangeDecidedBy)
clonedScan.rangeInfo = ts.rangeInfo
return clonedScan, nil
}

Expand Down Expand Up @@ -961,9 +961,7 @@ func (ts *PhysicalTableScan) MemoryUsage() (sum int64) {
for _, rang := range ts.Ranges {
sum += rang.MemUsage()
}
for _, col := range ts.rangeDecidedBy {
sum += col.MemoryUsage()
}
sum += int64(len(ts.rangeInfo))
for _, col := range ts.tblCols {
sum += col.MemoryUsage()
}
Expand Down
4 changes: 3 additions & 1 deletion planner/core/testdata/integration_suite_in.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,9 @@
"name": "TestIndexJoinTableRange",
"cases": [
"desc format = 'brief' select /*+ TIDB_INLJ(t2)*/ * from t1, t2 where t1.a = t2.a and t1.b = t2.b",
"desc format = 'brief' select /*+ TIDB_INLJ(t2)*/ * from t1, t2 where t1.a = t2.a and t1.b = t2.a and t1.b = t2.b"
"desc format = 'brief' select /*+ TIDB_INLJ(t2)*/ * from t1, t2 where t1.a = t2.a and t1.b = t2.a and t1.b = t2.b",
"desc format = 'brief' select /*+ INL_JOIN(t4) */ * from t3 join t4 on t3.a = t4.a where t4.b = 1",
"desc format = 'brief' select /*+ INL_JOIN(t4) */ * from t3 join t4 on t3.b = t4.b where t4.a = 1"
]
},
{
Expand Down
24 changes: 24 additions & 0 deletions planner/core/testdata/integration_suite_out.json
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,30 @@
" └─Selection 1.00 cop[tikv] not(isnull(test.t2.b))",
" └─TableRangeScan 1.00 cop[tikv] table:t2 range: decided by [test.t1.a test.t1.b], keep order:false, stats:pseudo"
]
},
{
"SQL": "desc format = 'brief' select /*+ INL_JOIN(t4) */ * from t3 join t4 on t3.a = t4.a where t4.b = 1",
"Plan": [
"IndexJoin 12.50 root inner join, inner:TableReader, outer key:test.t3.a, inner key:test.t4.a, equal cond:eq(test.t3.a, test.t4.a)",
"├─TableReader(Build) 9990.00 root data:Selection",
"│ └─Selection 9990.00 cop[tikv] not(isnull(test.t3.a))",
"│ └─TableFullScan 10000.00 cop[tikv] table:t3 keep order:false, stats:pseudo",
"└─TableReader(Probe) 0.00 root data:Selection",
" └─Selection 0.00 cop[tikv] eq(test.t4.b, 1)",
" └─TableRangeScan 1.00 cop[tikv] table:t4 range: decided by [eq(test.t4.a, test.t3.a) eq(test.t4.b, 1)], keep order:false, stats:pseudo"
]
},
{
"SQL": "desc format = 'brief' select /*+ INL_JOIN(t4) */ * from t3 join t4 on t3.b = t4.b where t4.a = 1",
"Plan": [
"IndexJoin 12.50 root inner join, inner:TableReader, outer key:test.t3.b, inner key:test.t4.b, equal cond:eq(test.t3.b, test.t4.b)",
"├─TableReader(Build) 9990.00 root data:Selection",
"│ └─Selection 9990.00 cop[tikv] not(isnull(test.t3.b))",
"│ └─TableFullScan 10000.00 cop[tikv] table:t3 keep order:false, stats:pseudo",
"└─TableReader(Probe) 0.00 root data:Selection",
" └─Selection 0.00 cop[tikv] eq(test.t4.a, 1)",
" └─TableRangeScan 1.00 cop[tikv] table:t4 range: decided by [eq(test.t4.b, test.t3.b) eq(test.t4.a, 1)], keep order:false, stats:pseudo"
]
}
]
},
Expand Down