From a671b3c5e5c36dbb0b697b6d722e541d0ca51130 Mon Sep 17 00:00:00 2001 From: Yiding Cui Date: Thu, 13 Apr 2017 14:25:24 +0800 Subject: [PATCH] plan: fix failed case when do index nested loop join (#3034) --- plan/physical_plan_builder.go | 43 +++++++++++++++++++++++++++-------- plan/physical_plan_test.go | 12 ++++++++++ plan/physical_plans.go | 20 +++++++++++----- 3 files changed, 59 insertions(+), 16 deletions(-) diff --git a/plan/physical_plan_builder.go b/plan/physical_plan_builder.go index 21779397c8..36ed9db87c 100644 --- a/plan/physical_plan_builder.go +++ b/plan/physical_plan_builder.go @@ -546,6 +546,7 @@ func (p *LogicalJoin) convert2PhysicalPlanRight(prop *requiredProperty, innerJoi } // buildSelectionWithConds will build a selection use the conditions of join and convert one side's column to correlated column. +// If the inner side is one selection then one data source, the inner child should be the data source other than the selection. // This is called when build nested loop join. func (p *LogicalJoin) buildSelectionWithConds(leftAsOuter bool) (*Selection, []*expression.CorrelatedColumn) { var ( @@ -562,6 +563,10 @@ func (p *LogicalJoin) buildSelectionWithConds(leftAsOuter bool) (*Selection, []* innerConditions = p.LeftConditions innerChild = p.children[0] } + if sel, ok := innerChild.(*Selection); ok { + innerConditions = append(innerConditions, sel.Conditions...) + innerChild = sel.children[0] + } corCols := make([]*expression.CorrelatedColumn, 0, outerSchema.Len()) for _, col := range outerSchema.Columns { corCol := &expression.CorrelatedColumn{Column: *col, Data: new(types.Datum)} @@ -572,26 +577,38 @@ func (p *LogicalJoin) buildSelectionWithConds(leftAsOuter bool) (*Selection, []* selection.SetSchema(innerChild.Schema().Clone()) selection.SetChildren(innerChild) conds := make([]expression.Expression, 0, len(p.EqualConditions)+len(innerConditions)+len(p.OtherConditions)) - for _, cond := range innerConditions { - conds = append(conds, cond) - } for _, cond := range p.EqualConditions { newCond := expression.ConvertCol2CorCol(cond, corCols, outerSchema) conds = append(conds, newCond) } + selection.Conditions = conds + // Currently only eq conds will be considered when we call checkScanController, and innerConds from the below sel may contain correlated column, + // which will have side effect when we do check. So we do check before append other conditions into selection. + selection.controllerStatus = selection.checkScanController() + if selection.controllerStatus == notController { + return nil, nil + } + for _, cond := range innerConditions { + conds = append(conds, cond) + } for _, cond := range p.OtherConditions { newCond := expression.ConvertCol2CorCol(cond, corCols, outerSchema) newCond.ResolveIndices(innerChild.Schema()) conds = append(conds, newCond) } selection.Conditions = conds - selection.controllerStatus = selection.checkScanController() return selection, corCols } func (p *LogicalJoin) convert2IndexNestedLoopJoinLeft(prop *requiredProperty, innerJoin bool) (*physicalPlanInfo, error) { lChild := p.children[0].(LogicalPlan) - if _, ok := p.children[1].(*DataSource); !ok { + switch x := p.children[1].(type) { + case *DataSource: + case *Selection: + if _, ok := x.children[0].(*DataSource); !ok { + return nil, nil + } + default: return nil, nil } allLeft := true @@ -620,7 +637,7 @@ func (p *LogicalJoin) convert2IndexNestedLoopJoinLeft(prop *requiredProperty, in return nil, nil } selection, corCols := p.buildSelectionWithConds(true) - if selection.controllerStatus == notController { + if selection == nil { return nil, nil } rInfo := selection.makeScanController() @@ -656,7 +673,13 @@ func (p *LogicalJoin) convert2IndexNestedLoopJoinLeft(prop *requiredProperty, in func (p *LogicalJoin) convert2IndexNestedLoopJoinRight(prop *requiredProperty, innerJoin bool) (*physicalPlanInfo, error) { rChild := p.children[1].(LogicalPlan) - if _, ok := p.children[0].(*DataSource); !ok { + switch x := p.children[0].(type) { + case *DataSource: + case *Selection: + if _, ok := x.children[0].(*DataSource); !ok { + return nil, nil + } + default: return nil, nil } allRight := true @@ -685,7 +708,7 @@ func (p *LogicalJoin) convert2IndexNestedLoopJoinRight(prop *requiredProperty, i return nil, nil } selection, corCols := p.buildSelectionWithConds(false) - if selection.controllerStatus == notController { + if selection == nil { return nil, nil } lInfo := selection.makeScanController() @@ -960,7 +983,7 @@ func (p *LogicalJoin) convert2PhysicalPlan(prop *requiredProperty) (*physicalPla break } if p.preferINLJ > 0 && p.hasEqualConds() { - if _, ok := p.children[1].(*DataSource); ok && (p.preferINLJ&preferLeftAsOuter) > 0 { + if (p.preferINLJ & preferLeftAsOuter) > 0 { lNLJInfo, err := p.convert2IndexNestedLoopJoinLeft(prop, true) if err != nil { return nil, errors.Trace(err) @@ -969,7 +992,7 @@ func (p *LogicalJoin) convert2PhysicalPlan(prop *requiredProperty) (*physicalPla info = lNLJInfo } } - if _, ok := p.children[0].(*DataSource); ok && (p.preferINLJ&preferRightAsOuter) > 0 { + if (p.preferINLJ & preferRightAsOuter) > 0 { rNLJInfo, err := p.convert2IndexNestedLoopJoinRight(prop, true) if err != nil { return nil, errors.Trace(err) diff --git a/plan/physical_plan_test.go b/plan/physical_plan_test.go index 803bf17461..1d56b02ed1 100644 --- a/plan/physical_plan_test.go +++ b/plan/physical_plan_test.go @@ -1112,6 +1112,18 @@ func (s *testPlanSuite) TestJoinAlgorithm(c *C) { sql: "select /*+ tidb_inlj(t2) */ * from t t1 join t t2 on t1.c=t2.c and t1.d=t2.d and t1.e > t2.e", ans: "Apply{Index(t.c_d_e)[]->Selection->Table(t)}", }, + { + sql: "select /*+ TIDB_INLJ(t1) */ * from t join t t1 where t.a=t1.a and t.b > 100 and t1.b>10", + ans: "Apply{Table(t)->Selection->Table(t)}", + }, + { + sql: "select /*+ TIDB_INLJ(tt, t1) */ * from (select * from t where t.b > 100) tt left join t t1 on tt.a=t1.a and t1.b>10", + ans: "Apply{Table(t)->Table(t)->Selection}", + }, + { + sql: "select /*+ TIDB_INLJ(t, t1) */ * from t left join (select * from t where t.b > 10) t1 on t.a=t1.a and t.b > 100", + ans: "LeftHashJoin{Table(t)->Table(t)}(test.t.a,t1.a)", + }, } for _, ca := range cases { comment := Commentf("for %s", ca.sql) diff --git a/plan/physical_plans.go b/plan/physical_plans.go index d3695b5ef5..15a80f8d91 100644 --- a/plan/physical_plans.go +++ b/plan/physical_plans.go @@ -696,16 +696,24 @@ func (p *PhysicalApply) MarshalJSON() ([]byte, error) { return nil, errors.Trace(err) } buffer := bytes.NewBufferString("{") - if p.PhysicalJoin.(*PhysicalHashJoin).SmallTable == 1 { + switch x := p.PhysicalJoin.(type) { + case *PhysicalHashJoin: + if x.SmallTable == 1 { + buffer.WriteString(fmt.Sprintf( + "\"innerPlan\": \"%s\",\n "+ + "\"outerPlan\": \"%s\",\n "+ + "\"join\": %s\n}", p.children[1].ID(), p.children[0].ID(), join)) + } else { + buffer.WriteString(fmt.Sprintf( + "\"innerPlan\": \"%s\",\n "+ + "\"outerPlan\": \"%s\",\n "+ + "\"join\": %s\n}", p.children[0].ID(), p.children[1].ID(), join)) + } + case *PhysicalHashSemiJoin: buffer.WriteString(fmt.Sprintf( "\"innerPlan\": \"%s\",\n "+ "\"outerPlan\": \"%s\",\n "+ "\"join\": %s\n}", p.children[1].ID(), p.children[0].ID(), join)) - } else { - buffer.WriteString(fmt.Sprintf( - "\"innerPlan\": \"%s\",\n "+ - "\"outerPlan\": \"%s\",\n "+ - "\"join\": %s\n}", p.children[0].ID(), p.children[1].ID(), join)) } return buffer.Bytes(), nil }