forbid where condition to be pushed through outer join.

fix bug #1164 (#1167)
This commit is contained in:
Han Fei
2016-04-27 16:55:38 +08:00
parent 14f8c4fe91
commit 42e4dfd428
4 changed files with 55 additions and 12 deletions

View File

@ -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) {

View File

@ -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",

View File

@ -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
}

View File

@ -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 {