From 0f2dbcdca49be49f18126b716b80ceaa6f4eac8d Mon Sep 17 00:00:00 2001 From: Yiding Cui Date: Wed, 22 Apr 2020 15:32:42 +0800 Subject: [PATCH] =?UTF-8?q?expression:=20constant=20propagation=20for=20ou?= =?UTF-8?q?ter=20join=20don't=20prop=20con=E2=80=A6=20(#15855)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- expression/constant_propagation.go | 17 ++++++++++++----- expression/testdata/expression_suite_in.json | 4 +++- expression/testdata/expression_suite_out.json | 12 ++++++++++++ 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/expression/constant_propagation.go b/expression/constant_propagation.go index 802d1eed09..9ee6f3bdfe 100644 --- a/expression/constant_propagation.go +++ b/expression/constant_propagation.go @@ -119,7 +119,7 @@ func validEqualCond(ctx sessionctx.Context, cond Expression) (*Column, *Constant // for 'a, b, a < 3', it returns 'true, false, b < 3' // for 'a, b, sin(a) + cos(a) = 5', it returns 'true, false, returns sin(b) + cos(b) = 5' // for 'a, b, cast(a) < rand()', it returns 'false, true, cast(a) < rand()' -func tryToReplaceCond(ctx sessionctx.Context, src *Column, tgt *Column, cond Expression) (bool, bool, Expression) { +func tryToReplaceCond(ctx sessionctx.Context, src *Column, tgt *Column, cond Expression, rejectControl bool) (bool, bool, Expression) { sf, ok := cond.(*ScalarFunction) if !ok { return false, false, cond @@ -132,6 +132,13 @@ func tryToReplaceCond(ctx sessionctx.Context, src *Column, tgt *Column, cond Exp if _, ok := inequalFunctions[sf.FuncName.L]; ok { return false, true, cond } + // See https://github.com/pingcap/tidb/issues/15782. The control function's result may rely on the original nullable + // information of the outer side column. Its args cannot be replaced easily. + // A more strict check is that after we replace the arg. We check the nullability of the new expression. + // But we haven't maintained it yet, so don't replace the arg of the control function currently. + if rejectControl && (sf.FuncName.L == ast.Ifnull || sf.FuncName.L == ast.If || sf.FuncName.L == ast.Case) { + return false, false, cond + } for idx, expr := range sf.GetArgs() { if src.Equal(nil, expr) { _, coll, _ := cond.CharsetAndCollation(ctx) @@ -145,7 +152,7 @@ func tryToReplaceCond(ctx sessionctx.Context, src *Column, tgt *Column, cond Exp } args[idx] = tgt } else { - subReplaced, isNonDeterministic, subExpr := tryToReplaceCond(ctx, src, tgt, expr) + subReplaced, isNonDeterministic, subExpr := tryToReplaceCond(ctx, src, tgt, expr, rejectControl) if isNonDeterministic { return false, true, cond } else if subReplaced { @@ -244,11 +251,11 @@ func (s *propConstSolver) propagateColumnEQ() { continue } cond := s.conditions[k] - replaced, _, newExpr := tryToReplaceCond(s.ctx, coli, colj, cond) + replaced, _, newExpr := tryToReplaceCond(s.ctx, coli, colj, cond, false) if replaced { s.conditions = append(s.conditions, newExpr) } - replaced, _, newExpr = tryToReplaceCond(s.ctx, colj, coli, cond) + replaced, _, newExpr = tryToReplaceCond(s.ctx, colj, coli, cond, false) if replaced { s.conditions = append(s.conditions, newExpr) } @@ -498,7 +505,7 @@ func (s *propOuterJoinConstSolver) deriveConds(outerCol, innerCol *Column, schem visited[k+offset] = true continue } - replaced, _, newExpr := tryToReplaceCond(s.ctx, outerCol, innerCol, cond) + replaced, _, newExpr := tryToReplaceCond(s.ctx, outerCol, innerCol, cond, true) if replaced { s.joinConds = append(s.joinConds, newExpr) } diff --git a/expression/testdata/expression_suite_in.json b/expression/testdata/expression_suite_in.json index 9b94b9cc3c..e67f1efc58 100644 --- a/expression/testdata/expression_suite_in.json +++ b/expression/testdata/expression_suite_in.json @@ -33,7 +33,9 @@ "explain select * from t1 left join t2 on t1.a = 1 or (t1.a = 2 and t1.a = 3)", "explain select * from t1 left join t2 on true where t1.a = 1 or (t1.a = 2 and t1.a = 3)", // Constant propagation over left outer semi join, filter with aux column should not be derived. - "explain select * from t1 where t1.b > 1 or t1.b in (select b from t2)" + "explain select * from t1 where t1.b > 1 or t1.b in (select b from t2)", + // Don't propagate for the control function. + "explain select * from t1 left join t2 on t1.a = t2.a where ifnull(t2.b, t1.a) = 1" ] } ] diff --git a/expression/testdata/expression_suite_out.json b/expression/testdata/expression_suite_out.json index 1d0e6fb636..ddbd8c6678 100644 --- a/expression/testdata/expression_suite_out.json +++ b/expression/testdata/expression_suite_out.json @@ -274,6 +274,18 @@ " └─TableReader_11(Probe) 10000.00 root data:TableFullScan_10", " └─TableFullScan_10 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo" ] + }, + { + "SQL": "explain select * from t1 left join t2 on t1.a = t2.a where ifnull(t2.b, t1.a) = 1", + "Result": [ + "Selection_7 9990.00 root eq(ifnull(test.t2.b, test.t1.a), 1)", + "└─HashJoin_8 12487.50 root left outer join, equal:[eq(test.t1.a, test.t2.a)]", + " ├─TableReader_14(Build) 9990.00 root data:Selection_13", + " │ └─Selection_13 9990.00 cop[tikv] not(isnull(test.t2.a))", + " │ └─TableFullScan_12 10000.00 cop[tikv] table:t2 keep order:false, stats:pseudo", + " └─TableReader_11(Probe) 10000.00 root data:TableFullScan_10", + " └─TableFullScan_10 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo" + ] } ] }