Skip to content

Commit

Permalink
expression, planner: skip isNullRejected check when contain outer n…
Browse files Browse the repository at this point in the history
…ot after `PushDownNot` (#31210)

close #20510
  • Loading branch information
rebelice authored Dec 31, 2021
1 parent 74ef32a commit 8c880d2
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 1 deletion.
35 changes: 35 additions & 0 deletions expression/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,41 @@ func PushDownNot(ctx sessionctx.Context, expr Expression) Expression {
return newExpr
}

// ContainOuterNot checks if there is an outer `not`.
func ContainOuterNot(expr Expression) bool {
return containOuterNot(expr, false)
}

// containOuterNot checks if there is an outer `not`.
// Input `not` means whether there is `not` outside `expr`
//
// eg.
// not(0+(t.a == 1 and t.b == 2)) returns true
// not(t.a) and not(t.b) returns false
func containOuterNot(expr Expression, not bool) bool {
if f, ok := expr.(*ScalarFunction); ok {
switch f.FuncName.L {
case ast.UnaryNot:
return containOuterNot(f.GetArgs()[0], true)
case ast.IsTruthWithNull, ast.IsNull:
return containOuterNot(f.GetArgs()[0], not)
default:
if not {
return true
}
hasNot := false
for _, expr := range f.GetArgs() {
hasNot = hasNot || containOuterNot(expr, not)
if hasNot {
return hasNot
}
}
return hasNot
}
}
return false
}

// Contains tests if `exprs` contains `e`.
func Contains(exprs []Expression, e Expression) bool {
for _, expr := range exprs {
Expand Down
26 changes: 26 additions & 0 deletions planner/core/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5150,3 +5150,29 @@ func (s *testIntegrationSuite) TestIndexMergeWithCorrelatedColumns(c *C) {
}

}

func (s *testIntegrationSuite) TestIssue20510(c *C) {
tk := testkit.NewTestKit(c, s.store)
tk.MustExec("use test")

tk.MustExec("drop table if exists t1, t2")
tk.MustExec("CREATE TABLE t1 (a int PRIMARY KEY, b int)")
tk.MustExec("CREATE TABLE t2 (a int PRIMARY KEY, b int)")
tk.MustExec("INSERT INTO t1 VALUES (1,1), (2,1), (3,1), (4,2)")
tk.MustExec("INSERT INTO t2 VALUES (1,2), (2,2)")

tk.MustQuery("explain format=brief SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.a WHERE not(0+(t1.a=30 and t2.b=1));").Check(testkit.Rows(
"Selection 8000.00 root not(plus(0, and(eq(test.t1.a, 30), eq(test.t2.b, 1))))",
"└─MergeJoin 10000.00 root left outer join, left key:test.t1.a, right key:test.t2.a",
" ├─TableReader(Build) 8000.00 root data:Selection",
" │ └─Selection 8000.00 cop[tikv] not(istrue_with_null(plus(0, and(eq(test.t2.a, 30), eq(test.t2.b, 1)))))",
" │ └─TableFullScan 10000.00 cop[tikv] table:t2 keep order:true, stats:pseudo",
" └─TableReader(Probe) 10000.00 root data:TableFullScan",
" └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:true, stats:pseudo"))
tk.MustQuery("SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.a WHERE not(0+(t1.a=30 and t2.b=1));").Check(testkit.Rows(
"1 1 1 2",
"2 1 2 2",
"3 1 <nil> <nil>",
"4 2 <nil> <nil>",
))
}
3 changes: 3 additions & 0 deletions planner/core/rule_predicate_push_down.go
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,9 @@ func simplifyOuterJoin(p *LogicalJoin, predicates []expression.Expression) {
// If it is a disjunction of null-rejected conditions.
func isNullRejected(ctx sessionctx.Context, schema *expression.Schema, expr expression.Expression) bool {
expr = expression.PushDownNot(ctx, expr)
if expression.ContainOuterNot(expr) {
return false
}
sc := ctx.GetSessionVars().StmtCtx
sc.InNullRejectCheck = true
result := expression.EvaluateExprWithNull(ctx, schema, expr)
Expand Down
4 changes: 3 additions & 1 deletion planner/core/testdata/plan_suite_unexported_in.json
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,9 @@
"select * from t t1 left join t t2 on t1.b = t2.b where not (t1.c > 1 or t2.c > 1);",
"select * from t t1 left join t t2 on t1.b = t2.b where not (t1.c > 1 and t2.c > 1);",
"select * from t t1 left join t t2 on t1.b > 1 where t1.c = t2.c;",
"select * from t t1 left join t t2 on true where t1.b <=> t2.b;"
"select * from t t1 left join t t2 on true where t1.b <=> t2.b;",
"select * from t t1 left join t t2 on t1.b = t2.b where not(0+(t1.c=1 and t2.c=2));",
"select * from t t1 left join t t2 on t1.b = t2.b where not(t1.c) and not(t2.c)"
]
},
{
Expand Down
8 changes: 8 additions & 0 deletions planner/core/testdata/plan_suite_unexported_out.json
Original file line number Diff line number Diff line change
Expand Up @@ -991,6 +991,14 @@
{
"Best": "Join{DataScan(t1)->DataScan(t2)}->Sel([nulleq(test.t.b, test.t.b)])->Projection",
"JoinType": "left outer join"
},
{
"Best": "Join{DataScan(t1)->DataScan(t2)}(test.t.b,test.t.b)->Sel([not(plus(0, and(eq(test.t.c, 1), eq(test.t.c, 2))))])->Projection",
"JoinType": "left outer join"
},
{
"Best": "Join{DataScan(t1)->DataScan(t2)}(test.t.b,test.t.b)->Projection",
"JoinType": "inner join"
}
]
},
Expand Down

0 comments on commit 8c880d2

Please sign in to comment.