Skip to content

Commit

Permalink
planner: revise isnullRejected check for And and OR (#38702)
Browse files Browse the repository at this point in the history
close #38654
  • Loading branch information
Yisaer authored Oct 28, 2022
1 parent 95d177a commit 7806839
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 24 deletions.
49 changes: 25 additions & 24 deletions expression/expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -858,45 +858,46 @@ func evaluateExprWithNullInNullRejectCheck(ctx sessionctx.Context, schema *Schem
for i, arg := range x.GetArgs() {
args[i], nullFromSets[i] = evaluateExprWithNullInNullRejectCheck(ctx, schema, arg)
}

// allNullFromSet indicates whether all arguments are Null Constant and the Null Constant is affected by the column of the schema.
allNullFromSet := true
allArgsNullFromSet := true
for i := range args {
if cons, ok := args[i].(*Constant); ok && cons.Value.IsNull() && !nullFromSets[i] {
allNullFromSet = false
allArgsNullFromSet = false
break
}
}

// allArgsNullFromSet indicates whether all Null Constant are affected by the column of the schema
allArgsNullFromSet := true
for i := range args {
if cons, ok := args[i].(*Constant); ok && cons.Value.IsNull() && nullFromSets[i] {
continue
}
allArgsNullFromSet = false
}

// If all the args are Null Constant and affected by the column schema, then we should keep it.
// If one of the args of `AND` and `OR` are Null Constant from the column schema, and the another argument is Constant, then we should keep it.
// Otherwise, we shouldn't let Null Constant which affected by the column schema participate in computing in `And` and `OR`
// due to the result of `AND` and `OR are uncertain if one of the arguments is NULL.
if !allArgsNullFromSet {
for i := range args {
if cons, ok := args[i].(*Constant); ok && cons.Value.IsNull() && nullFromSets[i] {
if x.FuncName.L == ast.LogicAnd {
args[i] = NewOne()
}
if x.FuncName.L == ast.LogicOr {
args[i] = NewZero()
// due to the result of `AND` and `OR` are uncertain if one of the arguments is NULL.
if x.FuncName.L == ast.LogicAnd || x.FuncName.L == ast.LogicOr {
hasNonConstantArg := false
for _, arg := range args {
if _, ok := arg.(*Constant); !ok {
hasNonConstantArg = true
break
}
}
if hasNonConstantArg {
for i := range args {
if cons, ok := args[i].(*Constant); ok && cons.Value.IsNull() && nullFromSets[i] {
if x.FuncName.L == ast.LogicAnd {
args[i] = NewOne()
break
}
if x.FuncName.L == ast.LogicOr {
args[i] = NewZero()
break
}
}
}
}
}

c := NewFunctionInternal(ctx, x.FuncName.L, x.RetType.Clone(), args...)
cons, ok := c.(*Constant)
// If the return expr is Null Constant, and all the Null Constant arguments are affected by column schema,
// then we think the result Null Constant is also affected by the column schema
return c, ok && cons.Value.IsNull() && allNullFromSet
return c, ok && cons.Value.IsNull() && allArgsNullFromSet
case *Column:
if !schema.Contains(x) {
return x, false
Expand Down
7 changes: 7 additions & 0 deletions planner/core/plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1078,6 +1078,7 @@ func TestNullEQConditionPlan(t *testing.T) {
}

// https://github.com/pingcap/tidb/issues/38304
// https://github.com/pingcap/tidb/issues/38654
func TestOuterJoinOnNull(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
Expand All @@ -1091,4 +1092,10 @@ func TestOuterJoinOnNull(t *testing.T) {
tk.MustQuery("SELECT * FROM t0 LEFT JOIN t1 ON NULL WHERE NOT '2' =(t1.c0 AND t0.c1 IS NULL); ").Check(testkit.Rows("> 1 <nil>"))
tk.MustQuery("SELECT * FROM t0 LEFT JOIN t1 ON NULL WHERE t1.c0 or true; ").Check(testkit.Rows("> 1 <nil>"))
tk.MustQuery("SELECT * FROM t0 LEFT JOIN t1 ON NULL WHERE not(t1.c0 and false); ").Check(testkit.Rows("> 1 <nil>"))

tk.MustExec("CREATE TABLE t2(c0 INT);")
tk.MustExec("CREATE TABLE t3(c0 INT);")
tk.MustExec("INSERT INTO t3 VALUES (1);")
tk.MustQuery("SELECT ((NOT ('i'))AND(t2.c0)) IS NULL FROM t2 RIGHT JOIN t3 ON t3.c0;").Check(testkit.Rows("1"))
tk.MustQuery("SELECT * FROM t2 RIGHT JOIN t3 ON t2.c0 WHERE ((NOT ('i'))AND(t2.c0)) IS NULL;").Check(testkit.Rows("<nil> 1"))
}

0 comments on commit 7806839

Please sign in to comment.