diff --git a/plan/logical_plan_test.go b/plan/logical_plan_test.go index 78966a2073185..65036e753075f 100644 --- a/plan/logical_plan_test.go +++ b/plan/logical_plan_test.go @@ -426,6 +426,11 @@ func (s *testPlanSuite) TestPredicatePushDown(c *C) { sql: "select t1.a, t2.a from t as t1 left join t as t2 on t1.a = t2.a where t1.a < 1.0", best: "Join{DataScan(t1)->DataScan(t2)}(t1.a,t2.a)->Projection", }, + // issue #7728 + { + sql: "select * from t t1 join t t2 on t1.a = t2.a where t2.a = null", + best: "Dual->Projection", + }, } for _, ca := range tests { comment := Commentf("for %s", ca.sql) diff --git a/plan/rule_predicate_push_down.go b/plan/rule_predicate_push_down.go index 5327bf7bf9409..75753f8133644 100644 --- a/plan/rule_predicate_push_down.go +++ b/plan/rule_predicate_push_down.go @@ -34,6 +34,12 @@ func addSelection(p LogicalPlan, child LogicalPlan, conditions []expression.Expr return } conditions = expression.PropagateConstant(p.context(), conditions) + // Return table dual when filter is constant false or null. + dual := conds2TableDual(child, conditions) + if dual != nil { + p.Children()[chIdx] = dual + return + } selection := LogicalSelection{Conditions: conditions}.init(p.context()) selection.SetChildren(child) p.Children()[chIdx] = selection @@ -55,6 +61,11 @@ func (p *LogicalSelection) PredicatePushDown(predicates []expression.Expression) retConditions, child := p.children[0].PredicatePushDown(append(p.Conditions, predicates...)) if len(retConditions) > 0 { p.Conditions = expression.PropagateConstant(p.ctx, retConditions) + // Return table dual when filter is constant false or null. + dual := conds2TableDual(p, p.Conditions) + if dual != nil { + return nil, dual + } return nil, p } return nil, child @@ -129,7 +140,13 @@ func (p *LogicalJoin) PredicatePushDown(predicates []expression.Expression) (ret tempCond = append(tempCond, p.OtherConditions...) tempCond = append(tempCond, predicates...) tempCond = expression.ExtractFiltersFromDNFs(p.ctx, tempCond) - equalCond, leftPushCond, rightPushCond, otherCond = extractOnCondition(expression.PropagateConstant(p.ctx, tempCond), leftPlan, rightPlan, true, true) + tempCond = expression.PropagateConstant(p.ctx, tempCond) + // Return table dual when filter is constant false or null. + dual := conds2TableDual(p, tempCond) + if dual != nil { + return ret, dual + } + equalCond, leftPushCond, rightPushCond, otherCond = extractOnCondition(tempCond, leftPlan, rightPlan, true, true) p.LeftConditions = nil p.RightConditions = nil p.EqualConditions = equalCond @@ -378,3 +395,21 @@ func deriveOtherConditions(p *LogicalJoin, deriveLeft bool, deriveRight bool) (l } return } + +// conds2TableDual builds a LogicalTableDual if cond is constant false or null. +func conds2TableDual(p LogicalPlan, conds []expression.Expression) LogicalPlan { + if len(conds) != 1 { + return nil + } + con, ok := conds[0].(*expression.Constant) + if !ok { + return nil + } + sc := p.context().GetSessionVars().StmtCtx + if isTrue, err := con.Value.ToBool(sc); (err == nil && isTrue == 0) || con.Value.IsNull() { + dual := LogicalTableDual{}.init(p.context()) + dual.SetSchema(p.Schema()) + return dual + } + return nil +}