diff --git a/executor/reload_expr_pushdown_blacklist.go b/executor/reload_expr_pushdown_blacklist.go index 23c10079b58eb..6542265e1d084 100644 --- a/executor/reload_expr_pushdown_blacklist.go +++ b/executor/reload_expr_pushdown_blacklist.go @@ -87,7 +87,7 @@ var funcName2Alias = map[string]string{ "case": ast.Case, "regexp": ast.Regexp, "is null": ast.IsNull, - "is true": ast.IsTruth, + "is true": ast.IsTruthWithoutNull, "is false": ast.IsFalsity, "values": ast.Values, "bit_count": ast.BitCount, diff --git a/expression/builtin.go b/expression/builtin.go index 618dccae2466a..77c5bc5c4198c 100644 --- a/expression/builtin.go +++ b/expression/builtin.go @@ -519,41 +519,42 @@ var funcs = map[string]functionClass{ ast.GetLock: &lockFunctionClass{baseFunctionClass{ast.GetLock, 2, 2}}, ast.ReleaseLock: &releaseLockFunctionClass{baseFunctionClass{ast.ReleaseLock, 1, 1}}, - ast.LogicAnd: &logicAndFunctionClass{baseFunctionClass{ast.LogicAnd, 2, 2}}, - ast.LogicOr: &logicOrFunctionClass{baseFunctionClass{ast.LogicOr, 2, 2}}, - ast.LogicXor: &logicXorFunctionClass{baseFunctionClass{ast.LogicXor, 2, 2}}, - ast.GE: &compareFunctionClass{baseFunctionClass{ast.GE, 2, 2}, opcode.GE}, - ast.LE: &compareFunctionClass{baseFunctionClass{ast.LE, 2, 2}, opcode.LE}, - ast.EQ: &compareFunctionClass{baseFunctionClass{ast.EQ, 2, 2}, opcode.EQ}, - ast.NE: &compareFunctionClass{baseFunctionClass{ast.NE, 2, 2}, opcode.NE}, - ast.LT: &compareFunctionClass{baseFunctionClass{ast.LT, 2, 2}, opcode.LT}, - ast.GT: &compareFunctionClass{baseFunctionClass{ast.GT, 2, 2}, opcode.GT}, - ast.NullEQ: &compareFunctionClass{baseFunctionClass{ast.NullEQ, 2, 2}, opcode.NullEQ}, - ast.Plus: &arithmeticPlusFunctionClass{baseFunctionClass{ast.Plus, 2, 2}}, - ast.Minus: &arithmeticMinusFunctionClass{baseFunctionClass{ast.Minus, 2, 2}}, - ast.Mod: &arithmeticModFunctionClass{baseFunctionClass{ast.Mod, 2, 2}}, - ast.Div: &arithmeticDivideFunctionClass{baseFunctionClass{ast.Div, 2, 2}}, - ast.Mul: &arithmeticMultiplyFunctionClass{baseFunctionClass{ast.Mul, 2, 2}}, - ast.IntDiv: &arithmeticIntDivideFunctionClass{baseFunctionClass{ast.IntDiv, 2, 2}}, - ast.BitNeg: &bitNegFunctionClass{baseFunctionClass{ast.BitNeg, 1, 1}}, - ast.And: &bitAndFunctionClass{baseFunctionClass{ast.And, 2, 2}}, - ast.LeftShift: &leftShiftFunctionClass{baseFunctionClass{ast.LeftShift, 2, 2}}, - ast.RightShift: &rightShiftFunctionClass{baseFunctionClass{ast.RightShift, 2, 2}}, - ast.UnaryNot: &unaryNotFunctionClass{baseFunctionClass{ast.UnaryNot, 1, 1}}, - ast.Or: &bitOrFunctionClass{baseFunctionClass{ast.Or, 2, 2}}, - ast.Xor: &bitXorFunctionClass{baseFunctionClass{ast.Xor, 2, 2}}, - ast.UnaryMinus: &unaryMinusFunctionClass{baseFunctionClass{ast.UnaryMinus, 1, 1}}, - ast.In: &inFunctionClass{baseFunctionClass{ast.In, 2, -1}}, - ast.IsTruth: &isTrueOrFalseFunctionClass{baseFunctionClass{ast.IsTruth, 1, 1}, opcode.IsTruth, false}, - ast.IsFalsity: &isTrueOrFalseFunctionClass{baseFunctionClass{ast.IsFalsity, 1, 1}, opcode.IsFalsity, false}, - ast.Like: &likeFunctionClass{baseFunctionClass{ast.Like, 3, 3}}, - ast.Regexp: ®expFunctionClass{baseFunctionClass{ast.Regexp, 2, 2}}, - ast.Case: &caseWhenFunctionClass{baseFunctionClass{ast.Case, 1, -1}}, - ast.RowFunc: &rowFunctionClass{baseFunctionClass{ast.RowFunc, 2, -1}}, - ast.SetVar: &setVarFunctionClass{baseFunctionClass{ast.SetVar, 2, 2}}, - ast.GetVar: &getVarFunctionClass{baseFunctionClass{ast.GetVar, 1, 1}}, - ast.BitCount: &bitCountFunctionClass{baseFunctionClass{ast.BitCount, 1, 1}}, - ast.GetParam: &getParamFunctionClass{baseFunctionClass{ast.GetParam, 1, 1}}, + ast.LogicAnd: &logicAndFunctionClass{baseFunctionClass{ast.LogicAnd, 2, 2}}, + ast.LogicOr: &logicOrFunctionClass{baseFunctionClass{ast.LogicOr, 2, 2}}, + ast.LogicXor: &logicXorFunctionClass{baseFunctionClass{ast.LogicXor, 2, 2}}, + ast.GE: &compareFunctionClass{baseFunctionClass{ast.GE, 2, 2}, opcode.GE}, + ast.LE: &compareFunctionClass{baseFunctionClass{ast.LE, 2, 2}, opcode.LE}, + ast.EQ: &compareFunctionClass{baseFunctionClass{ast.EQ, 2, 2}, opcode.EQ}, + ast.NE: &compareFunctionClass{baseFunctionClass{ast.NE, 2, 2}, opcode.NE}, + ast.LT: &compareFunctionClass{baseFunctionClass{ast.LT, 2, 2}, opcode.LT}, + ast.GT: &compareFunctionClass{baseFunctionClass{ast.GT, 2, 2}, opcode.GT}, + ast.NullEQ: &compareFunctionClass{baseFunctionClass{ast.NullEQ, 2, 2}, opcode.NullEQ}, + ast.Plus: &arithmeticPlusFunctionClass{baseFunctionClass{ast.Plus, 2, 2}}, + ast.Minus: &arithmeticMinusFunctionClass{baseFunctionClass{ast.Minus, 2, 2}}, + ast.Mod: &arithmeticModFunctionClass{baseFunctionClass{ast.Mod, 2, 2}}, + ast.Div: &arithmeticDivideFunctionClass{baseFunctionClass{ast.Div, 2, 2}}, + ast.Mul: &arithmeticMultiplyFunctionClass{baseFunctionClass{ast.Mul, 2, 2}}, + ast.IntDiv: &arithmeticIntDivideFunctionClass{baseFunctionClass{ast.IntDiv, 2, 2}}, + ast.BitNeg: &bitNegFunctionClass{baseFunctionClass{ast.BitNeg, 1, 1}}, + ast.And: &bitAndFunctionClass{baseFunctionClass{ast.And, 2, 2}}, + ast.LeftShift: &leftShiftFunctionClass{baseFunctionClass{ast.LeftShift, 2, 2}}, + ast.RightShift: &rightShiftFunctionClass{baseFunctionClass{ast.RightShift, 2, 2}}, + ast.UnaryNot: &unaryNotFunctionClass{baseFunctionClass{ast.UnaryNot, 1, 1}}, + ast.Or: &bitOrFunctionClass{baseFunctionClass{ast.Or, 2, 2}}, + ast.Xor: &bitXorFunctionClass{baseFunctionClass{ast.Xor, 2, 2}}, + ast.UnaryMinus: &unaryMinusFunctionClass{baseFunctionClass{ast.UnaryMinus, 1, 1}}, + ast.In: &inFunctionClass{baseFunctionClass{ast.In, 2, -1}}, + ast.IsTruthWithoutNull: &isTrueOrFalseFunctionClass{baseFunctionClass{ast.IsTruthWithoutNull, 1, 1}, opcode.IsTruth, false}, + ast.IsTruthWithNull: &isTrueOrFalseFunctionClass{baseFunctionClass{ast.IsTruthWithNull, 1, 1}, opcode.IsTruth, true}, + ast.IsFalsity: &isTrueOrFalseFunctionClass{baseFunctionClass{ast.IsFalsity, 1, 1}, opcode.IsFalsity, false}, + ast.Like: &likeFunctionClass{baseFunctionClass{ast.Like, 3, 3}}, + ast.Regexp: ®expFunctionClass{baseFunctionClass{ast.Regexp, 2, 2}}, + ast.Case: &caseWhenFunctionClass{baseFunctionClass{ast.Case, 1, -1}}, + ast.RowFunc: &rowFunctionClass{baseFunctionClass{ast.RowFunc, 2, -1}}, + ast.SetVar: &setVarFunctionClass{baseFunctionClass{ast.SetVar, 2, 2}}, + ast.GetVar: &getVarFunctionClass{baseFunctionClass{ast.GetVar, 1, 1}}, + ast.BitCount: &bitCountFunctionClass{baseFunctionClass{ast.BitCount, 1, 1}}, + ast.GetParam: &getParamFunctionClass{baseFunctionClass{ast.GetParam, 1, 1}}, // encryption and compression functions ast.AesDecrypt: &aesDecryptFunctionClass{baseFunctionClass{ast.AesDecrypt, 2, 3}}, diff --git a/expression/builtin_op_test.go b/expression/builtin_op_test.go index 3b74faca29252..41065dff34417 100644 --- a/expression/builtin_op_test.go +++ b/expression/builtin_op_test.go @@ -565,7 +565,7 @@ func (s *testEvaluatorSuite) TestIsTrueOrFalse(c *C) { } for _, tc := range testCases { - isTrueSig, err := funcs[ast.IsTruth].getFunction(s.ctx, s.datumsToConstants(types.MakeDatums(tc.args...))) + isTrueSig, err := funcs[ast.IsTruthWithoutNull].getFunction(s.ctx, s.datumsToConstants(types.MakeDatums(tc.args...))) c.Assert(err, IsNil) c.Assert(isTrueSig, NotNil) diff --git a/expression/expr_to_pb.go b/expression/expr_to_pb.go index a863f28ba47b6..5f0fce00cbb52 100644 --- a/expression/expr_to_pb.go +++ b/expression/expr_to_pb.go @@ -289,7 +289,8 @@ func (pc PbConverter) canFuncBePushed(sf *ScalarFunction) bool { ast.In, ast.IsNull, ast.Like, - ast.IsTruth, + ast.IsTruthWithoutNull, + ast.IsTruthWithNull, ast.IsFalsity, // arithmetical functions. diff --git a/expression/expression.go b/expression/expression.go index f8686f5d57031..171b02073de93 100644 --- a/expression/expression.go +++ b/expression/expression.go @@ -396,15 +396,23 @@ func wrapWithIsTrue(ctx sessionctx.Context, keepNull bool, arg Expression) (Expr if arg.GetType().EvalType() == types.ETInt { return arg, nil } - fc := &isTrueOrFalseFunctionClass{baseFunctionClass{ast.IsTruth, 1, 1}, opcode.IsTruth, keepNull} + var fc *isTrueOrFalseFunctionClass + if keepNull { + fc = &isTrueOrFalseFunctionClass{baseFunctionClass{ast.IsTruthWithNull, 1, 1}, opcode.IsTruth, keepNull} + } else { + fc = &isTrueOrFalseFunctionClass{baseFunctionClass{ast.IsTruthWithoutNull, 1, 1}, opcode.IsTruth, keepNull} + } f, err := fc.getFunction(ctx, []Expression{arg}) if err != nil { return nil, err } sf := &ScalarFunction{ - FuncName: model.NewCIStr(ast.IsTruth), + FuncName: model.NewCIStr(ast.IsTruthWithoutNull), Function: f, RetType: f.getRetTp(), } + if keepNull { + sf.FuncName = model.NewCIStr(ast.IsTruthWithNull) + } return FoldConstant(sf), nil } diff --git a/expression/integration_test.go b/expression/integration_test.go index 8408192f7b8df..528b63d73bc58 100644 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -5162,3 +5162,26 @@ func (s *testIntegrationSuite) TestIssue19804(c *C) { _, err = tk.Exec("alter table t change a a set('a', 'b', 'c', 'e', 'f');") c.Assert(err, NotNil) } + +func (s *testIntegrationSuite) TestIssue17476(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("DROP TABLE IF EXISTS `table_float`;") + tk.MustExec("DROP TABLE IF EXISTS `table_int_float_varchar`;") + tk.MustExec("CREATE TABLE `table_float` (`id_1` int(16) NOT NULL AUTO_INCREMENT,`col_float_1` float DEFAULT NULL,PRIMARY KEY (`id_1`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=97635;") + tk.MustExec("CREATE TABLE `table_int_float_varchar` " + + "(`id_6` int(16) NOT NULL AUTO_INCREMENT," + + "`col_int_6` int(16) DEFAULT NULL,`col_float_6` float DEFAULT NULL," + + "`col_varchar_6` varchar(511) DEFAULT NULL,PRIMARY KEY (`id_6`)," + + "KEY `vhyen` (`id_6`,`col_int_6`,`col_float_6`,`col_varchar_6`(1))," + + "KEY `zzylq` (`id_6`,`col_int_6`,`col_float_6`,`col_varchar_6`(1))) " + + "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=90818;") + + tk.MustExec("INSERT INTO `table_float` VALUES (1,NULL),(2,0.1),(3,0),(4,-0.1),(5,-0.1),(6,NULL),(7,0.5),(8,0),(9,0),(10,NULL),(11,1),(12,1.5),(13,NULL),(14,NULL);") + tk.MustExec("INSERT INTO `table_int_float_varchar` VALUES (1,0,0.1,'true'),(2,-1,1.5,'2020-02-02 02:02:00'),(3,NULL,1.5,NULL),(4,65535,0.1,'true'),(5,NULL,0.1,'1'),(6,-1,1.5,'2020-02-02 02:02:00'),(7,-1,NULL,''),(8,NULL,-0.1,NULL),(9,NULL,-0.1,'1'),(10,-1,NULL,''),(11,NULL,1.5,'false'),(12,-1,0,NULL),(13,0,-0.1,NULL),(14,-1,NULL,'-0'),(15,65535,-1,'1'),(16,NULL,0.5,NULL),(17,-1,NULL,NULL);") + tk.MustQuery(`select count(*) from table_float + JOIN table_int_float_varchar AS tmp3 ON (tmp3.col_varchar_6 AND NULL) + IS NULL WHERE col_int_6=0;`).Check(testkit.Rows("14")) + tk.MustQuery(`SELECT count(*) FROM (table_float JOIN table_int_float_varchar AS tmp3 ON (tmp3.col_varchar_6 AND NULL) IS NULL);`).Check(testkit.Rows("154")) + tk.MustQuery(`SELECT * FROM (table_int_float_varchar AS tmp3) WHERE (col_varchar_6 AND NULL) IS NULL AND col_int_6=0;`).Check(testkit.Rows("13 0 -0.1 ")) +} diff --git a/expression/simple_rewriter.go b/expression/simple_rewriter.go index 1f4b1e5d31f09..fa39ebfa3ed1e 100644 --- a/expression/simple_rewriter.go +++ b/expression/simple_rewriter.go @@ -456,7 +456,7 @@ func (sr *simpleRewriter) notToExpression(hasNot bool, op string, tp *types.Fiel func (sr *simpleRewriter) isTrueToScalarFunc(v *ast.IsTruthExpr) { arg := sr.pop() - op := ast.IsTruth + op := ast.IsTruthWithoutNull if v.True == 0 { op = ast.IsFalsity } diff --git a/expression/util.go b/expression/util.go index 25af2d431d231..482333ca1fa49 100644 --- a/expression/util.go +++ b/expression/util.go @@ -270,21 +270,21 @@ func timeZone2Duration(tz string) time.Duration { } var logicalOps = map[string]struct{}{ - ast.LT: {}, - ast.GE: {}, - ast.GT: {}, - ast.LE: {}, - ast.EQ: {}, - ast.NE: {}, - ast.UnaryNot: {}, - ast.LogicAnd: {}, - ast.LogicOr: {}, - ast.LogicXor: {}, - ast.In: {}, - ast.IsNull: {}, - ast.IsTruth: {}, - ast.IsFalsity: {}, - ast.Like: {}, + ast.LT: {}, + ast.GE: {}, + ast.GT: {}, + ast.LE: {}, + ast.EQ: {}, + ast.NE: {}, + ast.UnaryNot: {}, + ast.LogicAnd: {}, + ast.LogicOr: {}, + ast.LogicXor: {}, + ast.In: {}, + ast.IsNull: {}, + ast.IsTruthWithoutNull: {}, + ast.IsFalsity: {}, + ast.Like: {}, } var oppositeOp = map[string]string{ diff --git a/go.mod b/go.mod index 3745e28278da2..6a90e2c0b0375 100644 --- a/go.mod +++ b/go.mod @@ -38,7 +38,7 @@ require ( github.com/pingcap/goleveldb v0.0.0-20171020122428-b9ff6c35079e github.com/pingcap/kvproto v0.0.0-20200311073257-e53d835099b0 github.com/pingcap/log v0.0.0-20190715063458-479153f07ebd - github.com/pingcap/parser v3.0.17-0.20200901060850-21ac2654ca11+incompatible + github.com/pingcap/parser v3.0.17-0.20200917074335-8b16316f8a6e+incompatible github.com/pingcap/pd v1.1.0-beta.0.20191223090411-ea2b748f6ee2 github.com/pingcap/tidb-tools v3.0.6-0.20191119150227-ff0a3c6e5763+incompatible github.com/pingcap/tipb v0.0.0-20200426072559-d2c068e96eb3 diff --git a/go.sum b/go.sum index ab384590b7782..6e325e6fa3ace 100644 --- a/go.sum +++ b/go.sum @@ -158,8 +158,8 @@ github.com/pingcap/kvproto v0.0.0-20200311073257-e53d835099b0 h1:dXXNHvDwAEN1YNg github.com/pingcap/kvproto v0.0.0-20200311073257-e53d835099b0/go.mod h1:QMdbTAXCHzzygQzqcG9uVUgU2fKeSN1GmfMiykdSzzY= github.com/pingcap/log v0.0.0-20190715063458-479153f07ebd h1:hWDol43WY5PGhsh3+8794bFHY1bPrmu6bTalpssCrGg= github.com/pingcap/log v0.0.0-20190715063458-479153f07ebd/go.mod h1:WpHUKhNZ18v116SvGrmjkA9CBhYmuUTKL+p8JC9ANEw= -github.com/pingcap/parser v3.0.17-0.20200901060850-21ac2654ca11+incompatible h1:pymLtwPxqonEYzYNIZ5CXUKi9gx1AdwrdTlJTU2N5KE= -github.com/pingcap/parser v3.0.17-0.20200901060850-21ac2654ca11+incompatible/go.mod h1:1FNvfp9+J0wvc4kl8eGNh7Rqrxveg15jJoWo/a0uHwA= +github.com/pingcap/parser v3.0.17-0.20200917074335-8b16316f8a6e+incompatible h1:A82fSnaJr+jRR9X0DquF+JhHdygw1I+YV5rrqWf95DY= +github.com/pingcap/parser v3.0.17-0.20200917074335-8b16316f8a6e+incompatible/go.mod h1:1FNvfp9+J0wvc4kl8eGNh7Rqrxveg15jJoWo/a0uHwA= github.com/pingcap/pd v1.1.0-beta.0.20191223090411-ea2b748f6ee2 h1:NL23b8tsg6M1QpSQedK14/Jx++QeyKL2rGiBvXAQVfA= github.com/pingcap/pd v1.1.0-beta.0.20191223090411-ea2b748f6ee2/go.mod h1:b4gaAPSxaVVtaB+EHamV4Nsv8JmTdjlw0cTKmp4+dRQ= github.com/pingcap/tidb-tools v3.0.6-0.20191119150227-ff0a3c6e5763+incompatible h1:I8HirWsu1MZp6t9G/g8yKCEjJJxtHooKakEgccvdJ4M= diff --git a/planner/core/expression_rewriter.go b/planner/core/expression_rewriter.go index 997505b510d1f..8da3d6f080f73 100644 --- a/planner/core/expression_rewriter.go +++ b/planner/core/expression_rewriter.go @@ -1104,7 +1104,7 @@ func (er *expressionRewriter) positionToScalarFunc(v *ast.PositionExpr) { func (er *expressionRewriter) isTrueToScalarFunc(v *ast.IsTruthExpr) { stkLen := len(er.ctxStack) - op := ast.IsTruth + op := ast.IsTruthWithoutNull if v.True == 0 { op = ast.IsFalsity } diff --git a/util/ranger/checker.go b/util/ranger/checker.go index 403c8e71a9219..1632253724708 100644 --- a/util/ranger/checker.go +++ b/util/ranger/checker.go @@ -59,7 +59,7 @@ func (c *conditionChecker) checkScalarFunction(scalar *expression.ScalarFunction } case ast.IsNull: return c.checkColumn(scalar.GetArgs()[0]) - case ast.IsTruth, ast.IsFalsity: + case ast.IsTruthWithoutNull, ast.IsTruthWithNull, ast.IsFalsity: if s, ok := scalar.GetArgs()[0].(*expression.Column); ok { if s.RetType.EvalType() == types.ETString { return false diff --git a/util/ranger/points.go b/util/ranger/points.go index cc31b5d02b0d1..7a6c2ccc8daed 100644 --- a/util/ranger/points.go +++ b/util/ranger/points.go @@ -26,7 +26,6 @@ import ( "github.com/pingcap/tidb/sessionctx/stmtctx" "github.com/pingcap/tidb/types" "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tipb/go-tipb" ) // Error instances. @@ -475,9 +474,10 @@ func (r *builder) newBuildFromPatternLike(expr *expression.ScalarFunction) []poi func (r *builder) buildFromNot(expr *expression.ScalarFunction) []point { switch n := expr.FuncName.L; n { - case ast.IsTruth: - keepNull := r.isTrueKeepNull(expr) - return r.buildFromIsTrue(expr, 1, keepNull) + case ast.IsTruthWithoutNull: + return r.buildFromIsTrue(expr, 1, false) + case ast.IsTruthWithNull: + return r.buildFromIsTrue(expr, 1, true) case ast.IsFalsity: return r.buildFromIsFalse(expr, 1) case ast.In: @@ -532,9 +532,10 @@ func (r *builder) buildFromScalarFunc(expr *expression.ScalarFunction) []point { return r.intersection(r.build(expr.GetArgs()[0]), r.build(expr.GetArgs()[1])) case ast.LogicOr: return r.union(r.build(expr.GetArgs()[0]), r.build(expr.GetArgs()[1])) - case ast.IsTruth: - keepNull := r.isTrueKeepNull(expr) - return r.buildFromIsTrue(expr, 0, keepNull) + case ast.IsTruthWithoutNull: + return r.buildFromIsTrue(expr, 0, false) + case ast.IsTruthWithNull: + return r.buildFromIsTrue(expr, 0, true) case ast.IsFalsity: return r.buildFromIsFalse(expr, 0) case ast.In: @@ -595,11 +596,3 @@ func (r *builder) merge(a, b []point, union bool) []point { } return merged } - -func (r *builder) isTrueKeepNull(expr *expression.ScalarFunction) bool { - switch expr.Function.PbCode() { - case tipb.ScalarFuncSig_DecimalIsTrueWithNull, tipb.ScalarFuncSig_RealIsTrueWithNull, tipb.ScalarFuncSig_IntIsTrueWithNull: - return true - } - return false -}