diff --git a/planner/core/logical_plan_test.go b/planner/core/logical_plan_test.go index 0b483943a0..f9a3497fba 100644 --- a/planner/core/logical_plan_test.go +++ b/planner/core/logical_plan_test.go @@ -421,6 +421,34 @@ func (s *testPlanSuite) TestSimplifyOuterJoin(c *C) { } } +func (s *testPlanSuite) TestAntiSemiJoinConstFalse(c *C) { + defer testleak.AfterTest(c)() + tests := []struct { + sql string + best string + joinType string + }{ + { + sql: "select a from t t1 where not exists (select a from t t2 where t1.a = t2.a and t2.b = 1 and t2.b = 2)", + best: "Join{DataScan(t1)->DataScan(t2)}->Projection", + joinType: "anti semi join", + }, + } + for _, ca := range tests { + comment := Commentf("for %s", ca.sql) + stmt, err := s.ParseOneStmt(ca.sql, "", "") + c.Assert(err, IsNil, comment) + p, err := BuildLogicalPlan(s.ctx, stmt, s.is) + c.Assert(err, IsNil, comment) + p, err = logicalOptimize(flagDecorrelate|flagPredicatePushDown|flagPrunColumns, p.(LogicalPlan)) + c.Assert(err, IsNil, comment) + c.Assert(ToString(p), Equals, ca.best, comment) + join, _ := p.(LogicalPlan).Children()[0].(*LogicalJoin) + joinType := fmt.Sprintf("%s", join.JoinType.String()) + c.Assert(joinType, Equals, ca.joinType, comment) + } +} + func newPartitionInfoSchema(definitions []model.PartitionDefinition) infoschema.InfoSchema { tableInfo := *MockTable() cols := make([]*model.ColumnInfo, 0, len(tableInfo.Columns)) diff --git a/planner/core/rule_predicate_push_down.go b/planner/core/rule_predicate_push_down.go index e82c367946..bf352289da 100644 --- a/planner/core/rule_predicate_push_down.go +++ b/planner/core/rule_predicate_push_down.go @@ -159,10 +159,13 @@ func (p *LogicalJoin) PredicatePushDown(predicates []expression.Expression) (ret tempCond = append(tempCond, predicates...) tempCond = expression.ExtractFiltersFromDNFs(p.ctx, tempCond) 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 + // Return table dual when filter is constant false or null. Not applicable to AntiSemiJoin. + // TODO: For AntiSemiJoin, we can use outer plan to substitute LogicalJoin actually. + if p.JoinType != AntiSemiJoin { + dual := conds2TableDual(p, tempCond) + if dual != nil { + return ret, dual + } } equalCond, leftPushCond, rightPushCond, otherCond = extractOnCondition(tempCond, leftPlan, rightPlan, true, true) p.LeftConditions = nil