Skip to content

Commit

Permalink
planner: fix the issue that the optimizer cannot convert OUTER JOIN t…
Browse files Browse the repository at this point in the history
…o INNER JOIN with nested AND/OR in some cases (#49625) (#49639)

close #49616
  • Loading branch information
ti-chi-bot authored Feb 27, 2024
1 parent acc9abc commit aaa6521
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 1 deletion.
11 changes: 10 additions & 1 deletion planner/core/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ func TestAggPushDownEngine(t *testing.T) {
" └─TableFullScan 10000.00 cop[tikv] table:t keep order:false, stats:pseudo"))
}

func TestIssue15110(t *testing.T) {
func TestIssue15110And49616(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
Expand Down Expand Up @@ -436,6 +436,15 @@ func TestIssue15110(t *testing.T) {

tk.MustExec("set @@session.tidb_isolation_read_engines = 'tiflash'")
tk.MustExec("explain format = 'brief' SELECT count(*) FROM crm_rd_150m dataset_48 WHERE (CASE WHEN (month(dataset_48.customer_first_date)) <= 30 THEN '新客' ELSE NULL END) IS NOT NULL;")

// for #49616
tk.MustExec(`use test`)
tk.MustExec("set @@session.tidb_isolation_read_engines = 'tikv'")
tk.MustExec(`create table t1 (k int, a int)`)
tk.MustExec(`create table t2 (k int, b int, key(k))`)
require.True(t, tk.HasPlan(`select /*+ tidb_inlj(t2, t1) */ *
from t2 left join t1 on t1.k=t2.k
where a>0 or (a=0 and b>0)`, `IndexJoin`))
}

func TestIssue40910(t *testing.T) {
Expand Down
45 changes: 45 additions & 0 deletions planner/core/rule_predicate_push_down.go
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,10 @@ func isNullRejected(ctx sessionctx.Context, schema *expression.Schema, expr expr
sc.InNullRejectCheck = false
}()
for _, cond := range expression.SplitCNFItems(expr) {
if isNullRejectedSpecially(ctx, schema, expr) {
return true
}

result := expression.EvaluateExprWithNull(ctx, schema, cond)
x, ok := result.(*expression.Constant)
if !ok {
Expand All @@ -453,6 +457,47 @@ func isNullRejected(ctx sessionctx.Context, schema *expression.Schema, expr expr
return false
}

// isNullRejectedSpecially handles some null-rejected cases specially, since the current in
// EvaluateExprWithNull is too strict for some cases, e.g. #49616.
func isNullRejectedSpecially(ctx sessionctx.Context, schema *expression.Schema, expr expression.Expression) bool {
return specialNullRejectedCase1(ctx, schema, expr) // only 1 case now
}

// specialNullRejectedCase1 is mainly for #49616.
// Case1 specially handles `null-rejected OR (null-rejected AND {others})`, then no matter what the result
// of `{others}` is (True, False or Null), the result of this predicate is null, so this predicate is null-rejected.
func specialNullRejectedCase1(ctx sessionctx.Context, schema *expression.Schema, expr expression.Expression) bool {
isFunc := func(e expression.Expression, lowerFuncName string) *expression.ScalarFunction {
f, ok := e.(*expression.ScalarFunction)
if !ok {
return nil
}
if f.FuncName.L == lowerFuncName {
return f
}
return nil
}
orFunc := isFunc(expr, ast.LogicOr)
if orFunc == nil {
return false
}
for i := 0; i < 2; i++ {
andFunc := isFunc(orFunc.GetArgs()[i], ast.LogicAnd)
if andFunc == nil {
continue
}
if !isNullRejected(ctx, schema, orFunc.GetArgs()[1-i]) {
continue // the other side should be null-rejected: null-rejected OR (... AND ...)
}
for _, andItem := range expression.SplitCNFItems(andFunc) {
if isNullRejected(ctx, schema, andItem) {
return true // hit the case in the comment: null-rejected OR (null-rejected AND ...)
}
}
}
return false
}

// PredicatePushDown implements LogicalPlan PredicatePushDown interface.
func (p *LogicalProjection) PredicatePushDown(predicates []expression.Expression, opt *logicalOptimizeOp) (ret []expression.Expression, retPlan LogicalPlan) {
canBePushed := make([]expression.Expression, 0, len(predicates))
Expand Down

0 comments on commit aaa6521

Please sign in to comment.