forbid where condition to be pushed through outer join.
fix bug #1164 (#1167)
This commit is contained in:
@ -1047,6 +1047,38 @@ func (s *testSuite) TestJoin(c *C) {
|
||||
result := tk.MustQuery(ca.sql)
|
||||
result.Check(ca.result)
|
||||
}
|
||||
|
||||
tk.MustExec("drop table if exists t")
|
||||
tk.MustExec("drop table if exists t1")
|
||||
tk.MustExec("create table t(c1 int, c2 int)")
|
||||
tk.MustExec("create table t1(c1 int, c2 int)")
|
||||
tk.MustExec("insert into t values(1,1),(2,2)")
|
||||
tk.MustExec("insert into t1 values(2,3),(4,4)")
|
||||
result := tk.MustQuery("select * from t left outer join t1 on t.c1 = t1.c1 where t.c1 = 1 or t1.c2 > 20")
|
||||
result.Check(testkit.Rows("1 1 <nil> <nil>"))
|
||||
result = tk.MustQuery("select * from t1 right outer join t on t.c1 = t1.c1 where t.c1 = 1 or t1.c2 > 20")
|
||||
result.Check(testkit.Rows("<nil> <nil> 1 1"))
|
||||
result = tk.MustQuery("select * from t right outer join t1 on t.c1 = t1.c1 where t.c1 = 1 or t1.c2 > 20")
|
||||
result.Check(testkit.Rows())
|
||||
result = tk.MustQuery("select * from t left outer join t1 on t.c1 = t1.c1 where t1.c1 = 3 or false")
|
||||
result.Check(testkit.Rows())
|
||||
result = tk.MustQuery("select * from t left outer join t1 on t.c1 = t1.c1 and t.c1 != 1")
|
||||
result.Check(testkit.Rows("1 1 <nil> <nil>", "2 2 2 3"))
|
||||
|
||||
tk.MustExec("drop table if exists t1")
|
||||
tk.MustExec("drop table if exists t2")
|
||||
tk.MustExec("drop table if exists t3")
|
||||
|
||||
tk.MustExec("create table t1 (c1 int, c2 int)")
|
||||
tk.MustExec("create table t2 (c1 int, c2 int)")
|
||||
tk.MustExec("create table t3 (c1 int, c2 int)")
|
||||
|
||||
tk.MustExec("insert into t1 values (1,1), (2,2), (3,3)")
|
||||
tk.MustExec("insert into t2 values (1,1), (3,3), (5,5)")
|
||||
tk.MustExec("insert into t3 values (1,1), (5,5), (9,9)")
|
||||
|
||||
result = tk.MustQuery("select * from t1 left join t2 on t1.c1 = t2.c1 right join t3 on t2.c1 = t3.c1 order by t1.c1, t1.c2, t2.c1, t2.c2, t3.c1, t3.c2;")
|
||||
result.Check(testkit.Rows("<nil> <nil> <nil> <nil> 5 5", "<nil> <nil> <nil> <nil> 9 9", "1 1 1 1 1 1"))
|
||||
}
|
||||
|
||||
func (s *testSuite) TestIndexScan(c *C) {
|
||||
|
||||
@ -588,7 +588,7 @@ func (s *testPlanSuite) TestJoinPath(c *C) {
|
||||
},
|
||||
{
|
||||
"select * from t1 left join t2 on 1 where t2.c1 != 0 or t1.c1 != 0",
|
||||
"OuterJoin{Table(t1)->Table(t2)}->Fields",
|
||||
"OuterJoin{Table(t1)->Table(t2)}->Filter->Fields",
|
||||
},
|
||||
{
|
||||
"select * from t1 left join t2 on t1.i1 = t2.i1 where t1.i1 = 1",
|
||||
|
||||
@ -113,8 +113,9 @@ func newOuterJoinPath(isRightJoin bool, leftPath, rightPath *joinPath, on *ast.O
|
||||
conditions := splitWhere(on.Expr)
|
||||
availablePaths := []*joinPath{outerJoin.outer}
|
||||
for _, con := range conditions {
|
||||
if !outerJoin.inner.attachCondition(con, availablePaths) {
|
||||
if !outerJoin.inner.attachCondition(con, availablePaths, true) {
|
||||
log.Errorf("Inner failed to attach ON condition")
|
||||
outerJoin.conditions = append(outerJoin.conditions, con)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -146,7 +147,7 @@ func newInnerJoinPath(leftPath, rightPath *joinPath, on *ast.OnCondition) *joinP
|
||||
if on != nil {
|
||||
conditions := splitWhere(on.Expr)
|
||||
for _, con := range conditions {
|
||||
if !innerJoin.attachCondition(con, nil) {
|
||||
if !innerJoin.attachCondition(con, nil, true) {
|
||||
innerJoin.conditions = append(innerJoin.conditions, con)
|
||||
}
|
||||
}
|
||||
@ -173,7 +174,8 @@ func (p *joinPath) resultFields() []*ast.ResultField {
|
||||
|
||||
// attachCondition tries to attach a condition as deep as possible.
|
||||
// availablePaths are paths join before this path.
|
||||
func (p *joinPath) attachCondition(condition ast.ExprNode, availablePaths []*joinPath) (attached bool) {
|
||||
// onCond represents whether the conditions is from current join's on condition. The on condition from other joins is treated as where condition.
|
||||
func (p *joinPath) attachCondition(condition ast.ExprNode, availablePaths []*joinPath, onCond bool) (attached bool) {
|
||||
filterRate := guesstimateFilterRate(condition)
|
||||
// table
|
||||
if p.table != nil || p.subquery != nil {
|
||||
@ -189,7 +191,7 @@ func (p *joinPath) attachCondition(condition ast.ExprNode, availablePaths []*joi
|
||||
// inner join
|
||||
if len(p.inners) > 0 {
|
||||
for _, in := range p.inners {
|
||||
if in.attachCondition(condition, availablePaths) {
|
||||
if in.attachCondition(condition, availablePaths, false) {
|
||||
p.filterRate *= filterRate
|
||||
return true
|
||||
}
|
||||
@ -205,11 +207,13 @@ func (p *joinPath) attachCondition(condition ast.ExprNode, availablePaths []*joi
|
||||
}
|
||||
|
||||
// outer join
|
||||
if p.outer.attachCondition(condition, availablePaths) {
|
||||
if p.outer.attachCondition(condition, availablePaths, false) {
|
||||
p.filterRate *= filterRate
|
||||
return true
|
||||
}
|
||||
if p.inner.attachCondition(condition, append(availablePaths, p.outer)) {
|
||||
// can't attach any where condition
|
||||
if onCond && p.inner.attachCondition(condition, availablePaths, false) {
|
||||
log.Infof("condition %v", condition)
|
||||
p.filterRate *= filterRate
|
||||
return true
|
||||
}
|
||||
@ -474,7 +478,7 @@ func (p *joinPath) reattach(pathMap map[*joinPath]bool, availablePaths []*joinPa
|
||||
for _, con := range p.conditions {
|
||||
var attached bool
|
||||
for path := range pathMap {
|
||||
if path.attachCondition(con, availablePaths) {
|
||||
if path.attachCondition(con, availablePaths, true) {
|
||||
attached = true
|
||||
break
|
||||
}
|
||||
@ -602,12 +606,11 @@ func (b *planBuilder) buildJoin(sel *ast.SelectStmt) Plan {
|
||||
path := b.buildBasicJoinPath(sel.From.TableRefs, nrfinder.nullRejectTables)
|
||||
rfs := path.resultFields()
|
||||
|
||||
var filterConditions []ast.ExprNode
|
||||
whereConditions := splitWhere(sel.Where)
|
||||
for _, whereCond := range whereConditions {
|
||||
if !path.attachCondition(whereCond, nil) {
|
||||
// TODO: Find a better way to handle this condition.
|
||||
path.conditions = append(path.conditions, whereCond)
|
||||
log.Warnf("Failed to attach where condtion in %s", sel.Text())
|
||||
if !path.attachCondition(whereCond, nil, false) {
|
||||
filterConditions = append(filterConditions, whereCond)
|
||||
}
|
||||
}
|
||||
path.extractEqualConditon()
|
||||
@ -615,6 +618,12 @@ func (b *planBuilder) buildJoin(sel *ast.SelectStmt) Plan {
|
||||
path.optimizeJoinOrder(nil)
|
||||
p := b.buildPlanFromJoinPath(path)
|
||||
p.SetFields(rfs)
|
||||
if filterConditions != nil {
|
||||
filterPlan := &Filter{Conditions: filterConditions}
|
||||
filterPlan.SetSrc(p)
|
||||
filterPlan.SetFields(p.Fields())
|
||||
return filterPlan
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
|
||||
@ -57,6 +57,8 @@ func (e *stringer) Leave(in Plan) (Plan, bool) {
|
||||
str = "Lock"
|
||||
case *ShowDDL:
|
||||
str = "ShowDDL"
|
||||
case *Filter:
|
||||
str = "Filter"
|
||||
case *Sort:
|
||||
str = "Sort"
|
||||
if x.ExecLimit != nil {
|
||||
|
||||
Reference in New Issue
Block a user