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/core: fix range info for the inner child of index join in explain (#8058) #9528

Closed
wants to merge 4 commits into from
Closed
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
4 changes: 2 additions & 2 deletions cmd/explaintest/r/explain_complex.result
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ Projection_13 1.00 root gad.id, test.dd.id, gad.aid, gad.cm, test.dd.dic, test.d
└─HashAgg_19 1.00 root group by:gad.aid, test.dd.dic, funcs:firstrow(gad.id), firstrow(gad.aid), firstrow(gad.cm), firstrow(gad.p1), firstrow(gad.p2), firstrow(gad.p3), firstrow(gad.p4), firstrow(gad.p5), firstrow(gad.p6_md5), firstrow(gad.p7_md5), firstrow(gad.ext), firstrow(gad.t), firstrow(test.dd.id), firstrow(test.dd.dic), firstrow(test.dd.ip), firstrow(test.dd.t)
└─IndexJoin_24 0.00 root inner join, inner:IndexLookUp_23, outer key:gad.aid, inner key:test.dd.aid, other cond:eq(test.dd.ip, gad.ip), gt(test.dd.t, gad.t)
├─IndexLookUp_23 0.00 root
│ ├─IndexScan_20 10.00 cop table:dd, index:aid, dic, range: decided by [gad.aid gad.ip], keep order:false, stats:pseudo
│ ├─IndexScan_20 10.00 cop table:dd, index:aid, dic, range: decided by [gad.aid], keep order:false, stats:pseudo
│ └─Selection_22 0.00 cop eq(test.dd.bm, 0), eq(test.dd.pt, "android"), gt(test.dd.t, 1478143908), not(isnull(test.dd.ip)), not(isnull(test.dd.t))
│ └─TableScan_21 10.00 cop table:dd, keep order:false, stats:pseudo
└─IndexLookUp_33 3.33 root
Expand All @@ -137,7 +137,7 @@ Projection_10 0.00 root gad.id, sdk.id, gad.aid, gad.cm, sdk.dic, sdk.ip, sdk.t,
│ └─Selection_26 0.00 cop eq(gad.bm, 0), eq(gad.dit, "mac"), eq(gad.pt, "ios"), not(isnull(gad.dic))
│ └─TableScan_25 3333.33 cop table:st, keep order:false, stats:pseudo
└─IndexLookUp_17 0.00 root
├─IndexScan_14 10.00 cop table:sdk, index:aid, dic, range: decided by [gad.aid gad.dic], keep order:false, stats:pseudo
├─IndexScan_14 10.00 cop table:sdk, index:aid, dic, range: decided by [gad.aid], keep order:false, stats:pseudo
└─Selection_16 0.00 cop eq(sdk.bm, 0), eq(sdk.pt, "ios"), gt(sdk.t, 1477971479), not(isnull(sdk.mac)), not(isnull(sdk.t))
└─TableScan_15 10.00 cop table:dd, keep order:false, stats:pseudo
explain SELECT cm, p1, p2, p3, p4, p5, p6_md5, p7_md5, count(1) as click_pv, count(DISTINCT ip) as click_ip FROM st WHERE (t between 1478188800 and 1478275200) and aid='cn.sbkcq' and pt='android' GROUP BY cm, p1, p2, p3, p4, p5, p6_md5, p7_md5;
Expand Down
4 changes: 2 additions & 2 deletions cmd/explaintest/r/explain_complex_stats.result
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ Projection_13 424.00 root gad.id, test.dd.id, gad.aid, gad.cm, test.dd.dic, test
│ └─Selection_28 424.00 cop eq(gad.bm, 0), eq(gad.pt, "android"), gt(gad.t, 1478143908), not(isnull(gad.ip))
│ └─TableScan_27 1999.00 cop table:gad, range:[0,+inf], keep order:false
└─IndexLookUp_23 455.80 root
├─IndexScan_20 1.00 cop table:dd, index:aid, dic, range: decided by [gad.aid gad.ip], keep order:false
├─IndexScan_20 1.00 cop table:dd, index:aid, dic, range: decided by [gad.aid], keep order:false
└─Selection_22 455.80 cop eq(test.dd.bm, 0), eq(test.dd.pt, "android"), gt(test.dd.t, 1478143908), not(isnull(test.dd.ip)), not(isnull(test.dd.t))
└─TableScan_21 1.00 cop table:dd, keep order:false
explain select gad.id as gid,sdk.id as sid,gad.aid as aid,gad.cm as cm,sdk.dic as dic,sdk.ip as ip, sdk.t as t, gad.p1 as p1, gad.p2 as p2, gad.p3 as p3, gad.p4 as p4, gad.p5 as p5, gad.p6_md5 as p6, gad.p7_md5 as p7, gad.ext as ext from st gad join dd sdk on gad.aid = sdk.aid and gad.dic = sdk.mac and gad.t < sdk.t where gad.t > 1477971479 and gad.bm = 0 and gad.pt = 'ios' and gad.dit = 'mac' and sdk.t > 1477971479 and sdk.bm = 0 and sdk.pt = 'ios' limit 3000;
Expand All @@ -145,7 +145,7 @@ Projection_10 170.34 root gad.id, sdk.id, gad.aid, gad.cm, sdk.dic, sdk.ip, sdk.
│ └─Selection_22 170.34 cop eq(gad.bm, 0), eq(gad.dit, "mac"), eq(gad.pt, "ios"), gt(gad.t, 1477971479), not(isnull(gad.dic))
│ └─TableScan_21 1999.00 cop table:gad, range:[0,+inf], keep order:false
└─IndexLookUp_17 509.04 root
├─IndexScan_14 1.00 cop table:sdk, index:aid, dic, range: decided by [gad.aid gad.dic], keep order:false
├─IndexScan_14 1.00 cop table:sdk, index:aid, dic, range: decided by [gad.aid], keep order:false
└─Selection_16 509.04 cop eq(sdk.bm, 0), eq(sdk.pt, "ios"), gt(sdk.t, 1477971479), not(isnull(sdk.mac)), not(isnull(sdk.t))
└─TableScan_15 1.00 cop table:dd, keep order:false
explain SELECT cm, p1, p2, p3, p4, p5, p6_md5, p7_md5, count(1) as click_pv, count(DISTINCT ip) as click_ip FROM st WHERE (t between 1478188800 and 1478275200) and aid='cn.sbkcq' and pt='android' GROUP BY cm, p1, p2, p3, p4, p5, p6_md5, p7_md5;
Expand Down
2 changes: 1 addition & 1 deletion cmd/explaintest/r/tpch.result
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,7 @@ Sort_25 2406.00 root profit.nation:asc, profit.o_year:desc
│ └─TableReader_40 1.00 root data:TableScan_39
│ └─TableScan_39 1.00 cop table:orders, range: decided by [tpch.lineitem.l_orderkey], keep order:false
└─IndexLookUp_34 1.00 root
├─IndexScan_32 1.00 cop table:partsupp, index:PS_PARTKEY, PS_SUPPKEY, range: decided by [tpch.lineitem.l_suppkey tpch.lineitem.l_partkey], keep order:false
├─IndexScan_32 1.00 cop table:partsupp, index:PS_PARTKEY, PS_SUPPKEY, range: decided by [tpch.lineitem.l_partkey tpch.lineitem.l_suppkey], keep order:false
└─TableScan_33 1.00 cop table:partsupp, keep order:false
/*
Q10 Returned Item Reporting Query
Expand Down
57 changes: 57 additions & 0 deletions planner/core/cbo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,63 @@ func (s *testAnalyzeSuite) TestCBOWithoutAnalyze(c *C) {
))
}

func (s *testAnalyzeSuite) TestIssue8058(c *C) {
defer testleak.AfterTest(c)()
store, dom, err := newStoreWithBootstrap()
c.Assert(err, IsNil)
defer func() {
dom.Close()
store.Close()
}()

testKit := testkit.NewTestKit(c, store)
testKit.MustExec(`use test`)
testKit.MustExec(`create table n (pid int(11) not null, nid int(11) not null, primary key(pid, nid), key i_nid(nid))`)
testKit.MustExec(`create table w (nid int(11) not null, day date not null, hour smallint(6) not null, cid bigint(20) not null, cnt int (11) not null, primary key(nid, day, hour, cid))`)
testKit.MustQuery(`explain select count(1) from w join n on w.nid = n.nid where n.pid = 863 and cid in(73151457924606867) and day = '2018-10-23'`).Check(testkit.Rows(
"StreamAgg_13 1.00 root funcs:count(1)",
"└─IndexJoin_17 0.01 root inner join, inner:IndexReader_16, outer key:test.w.nid, inner key:test.n.nid",
" ├─TableReader_31 0.01 root data:Selection_30",
" │ └─Selection_30 0.01 cop eq(test.w.day, 2018-10-23 00:00:00.000000), in(test.w.cid, 73151457924606867)",
" │ └─TableScan_29 10000.00 cop table:w, range:[-inf,+inf], keep order:false, stats:pseudo",
" └─IndexReader_16 10.00 root index:IndexScan_15",
" └─IndexScan_15 10.00 cop table:n, index:pid, nid, range: decided by [863 test.w.nid,863 test.w.nid], keep order:false, stats:pseudo",
))
testKit.MustQuery(`explain select count(1) from w join n on w.nid = n.nid where n.pid in (863, 534) and cid in(73151457924606867) and day = '2018-10-23'`).Check(testkit.Rows(
"StreamAgg_13 1.00 root funcs:count(1)",
"└─IndexJoin_17 0.01 root inner join, inner:IndexReader_16, outer key:test.w.nid, inner key:test.n.nid",
" ├─TableReader_35 0.01 root data:Selection_34",
" │ └─Selection_34 0.01 cop eq(test.w.day, 2018-10-23 00:00:00.000000), in(test.w.cid, 73151457924606867)",
" │ └─TableScan_33 10000.00 cop table:w, range:[-inf,+inf], keep order:false, stats:pseudo",
" └─IndexReader_16 10.00 root index:IndexScan_15",
" └─IndexScan_15 10.00 cop table:n, index:pid, nid, range: decided by [534 test.w.nid,534 test.w.nid], [863 test.w.nid,863 test.w.nid], keep order:false, stats:pseudo",
))

testKit.MustExec(`create table t1(a int not null, b int not null, c int not null, d int not null, index idx_ab(a, b, c, d))`)
testKit.MustExec(`create table t2(a int not null, b int not null, c int not null, d int not null)`)
testKit.MustQuery(`explain select /*+ TIDB_INLJ(t1, t2) */ count(1) from t1 join t2 on t1.d = t2.d and t1.a = t2.a where t1.b = 2`).Check(testkit.Rows(
"StreamAgg_12 1.00 root funcs:count(1)",
"└─IndexJoin_20 12.50 root inner join, inner:IndexReader_19, outer key:test.t2.a, inner key:test.t1.a, other cond:eq(test.t1.d, test.t2.d)",
" ├─IndexReader_19 10.00 root index:IndexScan_18",
" │ └─IndexScan_18 10.00 cop table:t1, index:a, b, c, d, range: decided by [test.t2.a 2,test.t2.a 2], keep order:false, stats:pseudo",
" └─TableReader_22 10000.00 root data:TableScan_21",
" └─TableScan_21 10000.00 cop table:t2, range:[-inf,+inf], keep order:false, stats:pseudo",
))

testKit.MustExec(`drop table if exists t1, t2`)
testKit.MustExec(`create table t1(a int not null, b int not null, index idx_ab(a, b))`)
testKit.MustExec(`create table t2(a int not null, b int not null, c int not null)`)
testKit.MustQuery(`explain select /*+ TIDB_INLJ(t1, t2) */ count(1) from t1 join t2 on t1.b = t2.b and t1.a = t2.a where t2.c = 1000`).Check(testkit.Rows(
"StreamAgg_12 1.00 root funcs:count(1)",
"└─IndexJoin_21 12.50 root inner join, inner:IndexReader_20, outer key:test.t2.b, test.t2.a, inner key:test.t1.b, test.t1.a",
" ├─IndexReader_20 10.00 root index:IndexScan_19",
" │ └─IndexScan_19 10.00 cop table:t1, index:a, b, range: decided by [test.t2.a test.t2.b], keep order:false, stats:pseudo",
" └─TableReader_24 10.00 root data:Selection_23",
" └─Selection_23 10.00 cop eq(test.t2.c, 1000)",
" └─TableScan_22 10000.00 cop table:t2, range:[-inf,+inf], keep order:false, stats:pseudo",
))
}

func (s *testAnalyzeSuite) TestStraightJoin(c *C) {
defer testleak.AfterTest(c)()
store, dom, err := newStoreWithBootstrap()
Expand Down
9 changes: 6 additions & 3 deletions planner/core/exhaust_physical_plans.go
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ func (p *LogicalJoin) getIndexJoinByOuterIdx(prop *property.PhysicalProperty, ou
}
}
if bestIndexInfo != nil {
innerPlan := p.constructInnerIndexScan(ds, bestIndexInfo, remainedOfBest, outerJoinKeys, us)
innerPlan := p.constructInnerIndexScan(ds, bestIndexInfo, rangesOfBest, remainedOfBest, outerJoinKeys, us, keyOff2IdxOff)
return p.constructIndexJoin(prop, innerJoinKeys, outerJoinKeys, outerIdx, innerPlan, rangesOfBest, keyOff2IdxOff)
}
return nil
Expand Down Expand Up @@ -480,7 +480,7 @@ func (p *LogicalJoin) constructInnerUnionScan(us *LogicalUnionScan, reader Physi
}

// constructInnerIndexScan is specially used to construct the inner plan for PhysicalIndexJoin.
func (p *LogicalJoin) constructInnerIndexScan(ds *DataSource, idx *model.IndexInfo, remainedConds []expression.Expression, outerJoinKeys []*expression.Column, us *LogicalUnionScan) PhysicalPlan {
func (p *LogicalJoin) constructInnerIndexScan(ds *DataSource, idx *model.IndexInfo, ranges []*ranger.Range, remainedConds []expression.Expression, outerJoinKeys []*expression.Column, us *LogicalUnionScan, keyOff2IdxOff []int) PhysicalPlan {
is := PhysicalIndexScan{
Table: ds.tableInfo,
TableAsName: ds.TableAsName,
Expand All @@ -490,7 +490,10 @@ func (p *LogicalJoin) constructInnerIndexScan(ds *DataSource, idx *model.IndexIn
dataSourceSchema: ds.schema,
KeepOrder: false,
Ranges: ranger.FullRange(),
rangeDecidedBy: outerJoinKeys,

keyOff2IdxOff: keyOff2IdxOff,
rangesWithFakeConds: ranges,
rangeDecidedBy: outerJoinKeys,
}.Init(ds.ctx)
is.filterCondition = remainedConds

Expand Down
8 changes: 7 additions & 1 deletion planner/core/explain.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,13 @@ func (p *PhysicalIndexScan) ExplainInfo() string {
}
}
if len(p.rangeDecidedBy) > 0 {
fmt.Fprintf(buffer, ", range: decided by %v", p.rangeDecidedBy)
fmt.Fprintf(buffer, ", range: decided by ")
for i, idxRange := range p.rangesWithFakeConds {
fmt.Fprintf(buffer, idxRange.StringWithJoinKeys(p.rangeDecidedBy, p.keyOff2IdxOff))
if i+1 < len(p.rangesWithFakeConds) {
fmt.Fprint(buffer, ", ")
}
}
} else if haveCorCol {
fmt.Fprintf(buffer, ", range: decided by %v", p.AccessCondition)
} else if len(p.Ranges) > 0 {
Expand Down
3 changes: 3 additions & 0 deletions planner/core/physical_plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ package core_test

import (
"context"
"log"

. "github.com/pingcap/check"
"github.com/pingcap/parser"
Expand Down Expand Up @@ -785,6 +786,8 @@ func (s *testPlanSuite) TestDAGPlanBuilderUnionScan(c *C) {
}

func (s *testPlanSuite) TestDAGPlanBuilderAgg(c *C) {
log.Println("TestDAGPlanBuilderAgg start")

defer testleak.AfterTest(c)()
store, dom, err := newStoreWithBootstrap()
c.Assert(err, IsNil)
Expand Down
4 changes: 3 additions & 1 deletion planner/core/physical_plans.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@ type PhysicalIndexScan struct {
// It is used for query feedback.
Hist *statistics.Histogram

rangeDecidedBy []*expression.Column
rangeDecidedBy []*expression.Column
rangesWithFakeConds []*ranger.Range
keyOff2IdxOff []int

// The index scan may be on a partition.
isPartition bool
Expand Down
76 changes: 76 additions & 0 deletions util/ranger/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"strings"

"github.com/pingcap/errors"
"github.com/pingcap/tidb/expression"
"github.com/pingcap/tidb/kv"
"github.com/pingcap/tidb/sessionctx/stmtctx"
"github.com/pingcap/tidb/types"
Expand Down Expand Up @@ -77,6 +78,81 @@ func (ran *Range) IsPoint(sc *stmtctx.StatementContext) bool {
return !ran.LowExclude && !ran.HighExclude
}

// StringWithJoinKeys formats the range with join keys and keyOff2IdxOff for index join in explain command
func (ran *Range) StringWithJoinKeys(keys []*expression.Column, keyOff2IdxOff []int) string {
maxIdxOff := -1
for _, idxOff := range keyOff2IdxOff {
if maxIdxOff < idxOff {
maxIdxOff = idxOff
}
}

idxOff2Key := make([]*expression.Column, maxIdxOff+1)
for keyOff, idxOff := range keyOff2IdxOff {
if idxOff >= 0 {
idxOff2Key[idxOff] = keys[keyOff]
}
}

// resolve used keys and arrange keys by idxOff
var usedKeys []*expression.Column
for i := 0; i < len(idxOff2Key); i++ {
if i >= len(ran.LowVal) {
break
}

key := idxOff2Key[i]
if key != nil {
usedKeys = append(usedKeys, key)
}
}

if len(usedKeys) == len(ran.LowVal) {
return fmt.Sprintf("%v", usedKeys)
}

lowStrs := make([]string, 0, len(ran.LowVal))
for i, d := range ran.LowVal {
var key *expression.Column
if i < len(idxOff2Key) {
key = idxOff2Key[i]
} else {
key = nil
}

if key == nil {
lowStrs = append(lowStrs, formatDatum(d, true))
} else {
lowStrs = append(lowStrs, key.String())
}
}

highStrs := make([]string, 0, len(ran.HighVal))
for i, d := range ran.HighVal {
var key *expression.Column
if i < len(idxOff2Key) {
key = idxOff2Key[i]
} else {
key = nil
}

if key == nil {
highStrs = append(highStrs, formatDatum(d, false))
} else {
highStrs = append(highStrs, key.String())
}
}

l, r := "[", "]"
if ran.LowExclude {
l = "("
}
if ran.HighExclude {
r = ")"
}
return l + strings.Join(lowStrs, " ") + "," + strings.Join(highStrs, " ") + r
}

// String implements the Stringer interface.
func (ran *Range) String() string {
lowStrs := make([]string, 0, len(ran.LowVal))
Expand Down