From da69877e7a77431bf47fc150b8079e79e048ed06 Mon Sep 17 00:00:00 2001 From: Han Fei Date: Thu, 22 Dec 2016 19:11:02 +0800 Subject: [PATCH] evaluator: change test (#2304) --- evaluator/builtin_time_test.go | 128 ---------- evaluator/evaluator_test.go | 248 -------------------- plan/expression_rewriter.go | 17 +- plan/expression_test.go | 414 +++++++++++++++++++++++++++++++++ 4 files changed, 430 insertions(+), 377 deletions(-) create mode 100644 plan/expression_test.go diff --git a/evaluator/builtin_time_test.go b/evaluator/builtin_time_test.go index 71b5ddb715..fcd555ad2f 100644 --- a/evaluator/builtin_time_test.go +++ b/evaluator/builtin_time_test.go @@ -19,8 +19,6 @@ import ( "time" . "github.com/pingcap/check" - "github.com/pingcap/tidb/ast" - "github.com/pingcap/tidb/model" "github.com/pingcap/tidb/util/mock" "github.com/pingcap/tidb/util/testleak" "github.com/pingcap/tidb/util/testutil" @@ -443,132 +441,6 @@ func (s *testEvaluatorSuite) TestUTCDate(c *C) { c.Assert(n.String(), GreaterEqual, last.Format(types.DateFormat)) } -func (s *testEvaluatorSuite) TestDateArith(c *C) { - defer testleak.AfterTest(c)() - ctx := mock.NewContext() - - // list all test cases - tests := []struct { - Date interface{} - Interval interface{} - Unit string - AddResult interface{} - SubResult interface{} - error bool - }{ - // basic test - {"2011-11-11", 1, "DAY", "2011-11-12", "2011-11-10", false}, - // nil test - {nil, 1, "DAY", nil, nil, false}, - {"2011-11-11", nil, "DAY", nil, nil, false}, - // tests for different units - {"2011-11-11 10:10:10", 1000, "MICROSECOND", "2011-11-11 10:10:10.001000", "2011-11-11 10:10:09.999000", false}, - {"2011-11-11 10:10:10", "10", "SECOND", "2011-11-11 10:10:20", "2011-11-11 10:10:00", false}, - {"2011-11-11 10:10:10", "10", "MINUTE", "2011-11-11 10:20:10", "2011-11-11 10:00:10", false}, - {"2011-11-11 10:10:10", "10", "HOUR", "2011-11-11 20:10:10", "2011-11-11 00:10:10", false}, - {"2011-11-11 10:10:10", "11", "DAY", "2011-11-22 10:10:10", "2011-10-31 10:10:10", false}, - {"2011-11-11 10:10:10", "2", "WEEK", "2011-11-25 10:10:10", "2011-10-28 10:10:10", false}, - {"2011-11-11 10:10:10", "2", "MONTH", "2012-01-11 10:10:10", "2011-09-11 10:10:10", false}, - {"2011-11-11 10:10:10", "4", "QUARTER", "2012-11-11 10:10:10", "2010-11-11 10:10:10", false}, - {"2011-11-11 10:10:10", "2", "YEAR", "2013-11-11 10:10:10", "2009-11-11 10:10:10", false}, - {"2011-11-11 10:10:10", "10.00100000", "SECOND_MICROSECOND", "2011-11-11 10:10:20.100000", "2011-11-11 10:09:59.900000", false}, - {"2011-11-11 10:10:10", "10.0010000000", "SECOND_MICROSECOND", "2011-11-11 10:10:30", "2011-11-11 10:09:50", false}, - {"2011-11-11 10:10:10", "10.0010000010", "SECOND_MICROSECOND", "2011-11-11 10:10:30.000010", "2011-11-11 10:09:49.999990", false}, - {"2011-11-11 10:10:10", "10:10.100", "MINUTE_MICROSECOND", "2011-11-11 10:20:20.100000", "2011-11-11 09:59:59.900000", false}, - {"2011-11-11 10:10:10", "10:10", "MINUTE_SECOND", "2011-11-11 10:20:20", "2011-11-11 10:00:00", false}, - {"2011-11-11 10:10:10", "10:10:10.100", "HOUR_MICROSECOND", "2011-11-11 20:20:20.100000", "2011-11-10 23:59:59.900000", false}, - {"2011-11-11 10:10:10", "10:10:10", "HOUR_SECOND", "2011-11-11 20:20:20", "2011-11-11 00:00:00", false}, - {"2011-11-11 10:10:10", "10:10", "HOUR_MINUTE", "2011-11-11 20:20:10", "2011-11-11 00:00:10", false}, - {"2011-11-11 10:10:10", "11 10:10:10.100", "DAY_MICROSECOND", "2011-11-22 20:20:20.100000", "2011-10-30 23:59:59.900000", false}, - {"2011-11-11 10:10:10", "11 10:10:10", "DAY_SECOND", "2011-11-22 20:20:20", "2011-10-31 00:00:00", false}, - {"2011-11-11 10:10:10", "11 10:10", "DAY_MINUTE", "2011-11-22 20:20:10", "2011-10-31 00:00:10", false}, - {"2011-11-11 10:10:10", "11 10", "DAY_HOUR", "2011-11-22 20:10:10", "2011-10-31 00:10:10", false}, - {"2011-11-11 10:10:10", "11-1", "YEAR_MONTH", "2022-12-11 10:10:10", "2000-10-11 10:10:10", false}, - {"2011-11-11 10:10:10", "11-11", "YEAR_MONTH", "2023-10-11 10:10:10", "1999-12-11 10:10:10", false}, - // tests for interval in day forms - {"2011-11-11 10:10:10", "20", "DAY", "2011-12-01 10:10:10", "2011-10-22 10:10:10", false}, - {"2011-11-11 10:10:10", 19.88, "DAY", "2011-12-01 10:10:10", "2011-10-22 10:10:10", false}, - {"2011-11-11 10:10:10", "19.88", "DAY", "2011-11-30 10:10:10", "2011-10-23 10:10:10", false}, - {"2011-11-11 10:10:10", "prefix19suffix", "DAY", "2011-11-30 10:10:10", "2011-10-23 10:10:10", false}, - {"2011-11-11 10:10:10", "20-11", "DAY", "2011-12-01 10:10:10", "2011-10-22 10:10:10", false}, - {"2011-11-11 10:10:10", "20,11", "daY", "2011-12-01 10:10:10", "2011-10-22 10:10:10", false}, - {"2011-11-11 10:10:10", "1000", "dAy", "2014-08-07 10:10:10", "2009-02-14 10:10:10", false}, - {"2011-11-11 10:10:10", "true", "Day", "2011-11-12 10:10:10", "2011-11-10 10:10:10", false}, - {"2011-11-11 10:10:10", true, "Day", "2011-11-12 10:10:10", "2011-11-10 10:10:10", false}, - // test for different return data types - {"2011-11-11", 1, "DAY", "2011-11-12", "2011-11-10", false}, - {"2011-11-11", 10, "HOUR", "2011-11-11 10:00:00", "2011-11-10 14:00:00", false}, - {"2011-11-11", 10, "MINUTE", "2011-11-11 00:10:00", "2011-11-10 23:50:00", false}, - {"2011-11-11", 10, "SECOND", "2011-11-11 00:00:10", "2011-11-10 23:59:50", false}, - {"2011-11-11", "10:10", "HOUR_MINUTE", "2011-11-11 10:10:00", "2011-11-10 13:50:00", false}, - {"2011-11-11", "10:10:10", "HOUR_SECOND", "2011-11-11 10:10:10", "2011-11-10 13:49:50", false}, - {"2011-11-11", "10:10:10.101010", "HOUR_MICROSECOND", "2011-11-11 10:10:10.101010", "2011-11-10 13:49:49.898990", false}, - {"2011-11-11", "10:10", "MINUTE_SECOND", "2011-11-11 00:10:10", "2011-11-10 23:49:50", false}, - {"2011-11-11", "10:10.101010", "MINUTE_MICROSECOND", "2011-11-11 00:10:10.101010", "2011-11-10 23:49:49.898990", false}, - {"2011-11-11", "10.101010", "SECOND_MICROSECOND", "2011-11-11 00:00:10.101010", "2011-11-10 23:59:49.898990", false}, - {"2011-11-11 00:00:00", 1, "DAY", "2011-11-12 00:00:00", "2011-11-10 00:00:00", false}, - {"2011-11-11 00:00:00", 10, "HOUR", "2011-11-11 10:00:00", "2011-11-10 14:00:00", false}, - {"2011-11-11 00:00:00", 10, "MINUTE", "2011-11-11 00:10:00", "2011-11-10 23:50:00", false}, - {"2011-11-11 00:00:00", 10, "SECOND", "2011-11-11 00:00:10", "2011-11-10 23:59:50", false}, - // tests for invalid input - {"2011-11-11", "abc1000", "MICROSECOND", nil, nil, true}, - {"20111111 10:10:10", "1", "DAY", nil, nil, true}, - {"2011-11-11", "10", "SECOND_MICROSECOND", nil, nil, true}, - {"2011-11-11", "10.0000", "MINUTE_MICROSECOND", nil, nil, true}, - {"2011-11-11", "10:10:10", "MINUTE_MICROSECOND", nil, nil, true}, - } - - // run the test cases - for _, t := range tests { - op := ast.NewValueExpr(ast.DateAdd) - dateArithInterval := ast.NewValueExpr( - ast.DateArithInterval{ - Unit: t.Unit, - Interval: ast.NewValueExpr(t.Interval), - }, - ) - date := ast.NewValueExpr(t.Date) - expr := &ast.FuncCallExpr{ - FnName: model.NewCIStr("DATE_ARITH"), - Args: []ast.ExprNode{ - op, - date, - dateArithInterval, - }, - } - ast.SetFlag(expr) - v, err := Eval(ctx, expr) - if t.error == true { - c.Assert(err, NotNil) - } else { - c.Assert(err, IsNil) - if v.IsNull() { - c.Assert(nil, Equals, t.AddResult) - } else { - c.Assert(v.Kind(), Equals, types.KindMysqlTime) - value := v.GetMysqlTime() - c.Assert(value.String(), Equals, t.AddResult) - } - } - - op = ast.NewValueExpr(ast.DateSub) - expr.Args[0] = op - v, err = Eval(ctx, expr) - if t.error == true { - c.Assert(err, NotNil) - } else { - c.Assert(err, IsNil) - if v.IsNull() { - c.Assert(nil, Equals, t.AddResult) - } else { - c.Assert(v.Kind(), Equals, types.KindMysqlTime) - value := v.GetMysqlTime() - c.Assert(value.String(), Equals, t.SubResult) - } - } - } -} - func (s *testEvaluatorSuite) TestStrToDate(c *C) { tests := []struct { Date string diff --git a/evaluator/evaluator_test.go b/evaluator/evaluator_test.go index 7c32eac07a..6496aabfe6 100644 --- a/evaluator/evaluator_test.go +++ b/evaluator/evaluator_test.go @@ -27,7 +27,6 @@ import ( "github.com/pingcap/tidb/parser" "github.com/pingcap/tidb/parser/opcode" "github.com/pingcap/tidb/sessionctx/varsutil" - "github.com/pingcap/tidb/util/charset" "github.com/pingcap/tidb/util/mock" "github.com/pingcap/tidb/util/testleak" "github.com/pingcap/tidb/util/testutil" @@ -51,37 +50,6 @@ func (s *testEvaluatorSuite) SetUpSuite(c *C) { s.ctx = mock.NewContext() } -func (s *testEvaluatorSuite) parseExpr(c *C, expr string) ast.ExprNode { - st, err := s.ParseOneStmt("select "+expr, "", "") - c.Assert(err, IsNil) - stmt := st.(*ast.SelectStmt) - return stmt.Fields.Fields[0].Expr -} - -type testCase struct { - exprStr string - resultStr string -} - -func (s *testEvaluatorSuite) runTests(c *C, cases []testCase) { - for _, ca := range cases { - expr := s.parseExpr(c, ca.exprStr) - val, err := Eval(s.ctx, expr) - c.Assert(err, IsNil) - valStr := fmt.Sprintf("%v", val.GetValue()) - c.Assert(valStr, Equals, ca.resultStr, Commentf("for %s", ca.exprStr)) - } -} - -func (s *testEvaluatorSuite) TestBetween(c *C) { - defer testleak.AfterTest(c)() - cases := []testCase{ - {exprStr: "1 between 2 and 3", resultStr: "0"}, - {exprStr: "1 not between 2 and 3", resultStr: "1"}, - } - s.runTests(c, cases) -} - func (s *testEvaluatorSuite) TestSleep(c *C) { defer testleak.AfterTest(c)() ctx := mock.NewContext() @@ -363,81 +331,6 @@ func (s *testEvaluatorSuite) TestBinopNumeric(c *C) { } } -func (s *testEvaluatorSuite) TestCaseWhen(c *C) { - defer testleak.AfterTest(c)() - cases := []testCase{ - { - exprStr: "case 1 when 1 then 'str1' when 2 then 'str2' end", - resultStr: "str1", - }, - { - exprStr: "case 2 when 1 then 'str1' when 2 then 'str2' end", - resultStr: "str2", - }, - { - exprStr: "case 3 when 1 then 'str1' when 2 then 'str2' end", - resultStr: "", - }, - { - exprStr: "case 4 when 1 then 'str1' when 2 then 'str2' else 'str3' end", - resultStr: "str3", - }, - } - s.runTests(c, cases) - - // When expression value changed, result set back to null. - valExpr := ast.NewValueExpr(1) - whenClause := &ast.WhenClause{Expr: ast.NewValueExpr(1), Result: ast.NewValueExpr(1)} - caseExpr := &ast.CaseExpr{ - Value: valExpr, - WhenClauses: []*ast.WhenClause{whenClause}, - } - v, err := Eval(s.ctx, caseExpr) - c.Assert(err, IsNil) - c.Assert(v, testutil.DatumEquals, types.NewDatum(int64(1))) - valExpr.SetValue(4) - ast.ResetEvaluatedFlag(caseExpr) - v, err = Eval(s.ctx, caseExpr) - c.Assert(err, IsNil) - c.Assert(v.Kind(), Equals, types.KindNull) -} - -func (s *testEvaluatorSuite) TestCast(c *C) { - defer testleak.AfterTest(c)() - f := types.NewFieldType(mysql.TypeLonglong) - - expr := &ast.FuncCastExpr{ - Expr: ast.NewValueExpr(1), - Tp: f, - } - ast.SetFlag(expr) - v, err := Eval(s.ctx, expr) - c.Assert(err, IsNil) - c.Assert(v, testutil.DatumEquals, types.NewDatum(int64(1))) - - f.Flag |= mysql.UnsignedFlag - v, err = Eval(s.ctx, expr) - c.Assert(err, IsNil) - c.Assert(v, testutil.DatumEquals, types.NewDatum(uint64(1))) - - f.Tp = mysql.TypeString - f.Charset = charset.CharsetBin - v, err = Eval(s.ctx, expr) - c.Assert(err, IsNil) - c.Assert(v, testutil.DatumEquals, types.NewDatum([]byte("1"))) - - f.Tp = mysql.TypeString - f.Charset = "utf8" - v, err = Eval(s.ctx, expr) - c.Assert(err, IsNil) - c.Assert(v, testutil.DatumEquals, types.NewDatum("1")) - - expr.Expr = ast.NewValueExpr(nil) - v, err = Eval(s.ctx, expr) - c.Assert(err, IsNil) - c.Assert(v.Kind(), Equals, types.KindNull) -} - func (s *testEvaluatorSuite) TestExtract(c *C) { defer testleak.AfterTest(c)() str := "2011-11-11 10:10:10.123456" @@ -480,147 +373,6 @@ func (s *testEvaluatorSuite) TestExtract(c *C) { c.Assert(v.Kind(), Equals, types.KindNull) } -func (s *testEvaluatorSuite) TestPatternIn(c *C) { - defer testleak.AfterTest(c)() - cases := []testCase{ - { - exprStr: "1 not in (1, 2, 3)", - resultStr: "0", - }, - { - exprStr: "1 in (1, 2, 3)", - resultStr: "1", - }, - { - exprStr: "1 in (2, 3)", - resultStr: "0", - }, - { - exprStr: "NULL in (2, 3)", - resultStr: "", - }, - { - exprStr: "NULL not in (2, 3)", - resultStr: "", - }, - { - exprStr: "NULL in (NULL, 3)", - resultStr: "", - }, - { - exprStr: "1 in (1, NULL)", - resultStr: "1", - }, - { - exprStr: "1 in (NULL, 1)", - resultStr: "1", - }, - { - exprStr: "2 in (1, NULL)", - resultStr: "", - }, - { - exprStr: "(-(23)++46/51*+51) in (+23)", - resultStr: "0", - }, - } - s.runTests(c, cases) -} - -func (s *testEvaluatorSuite) TestIsNull(c *C) { - defer testleak.AfterTest(c)() - cases := []testCase{ - { - exprStr: "1 IS NULL", - resultStr: "0", - }, - { - exprStr: "1 IS NOT NULL", - resultStr: "1", - }, - { - exprStr: "NULL IS NULL", - resultStr: "1", - }, - { - exprStr: "NULL IS NOT NULL", - resultStr: "0", - }, - } - s.runTests(c, cases) -} - -func (s *testEvaluatorSuite) TestIsTruth(c *C) { - defer testleak.AfterTest(c)() - cases := []testCase{ - { - exprStr: "1 IS TRUE", - resultStr: "1", - }, - { - exprStr: "2 IS TRUE", - resultStr: "1", - }, - { - exprStr: "0 IS TRUE", - resultStr: "0", - }, - { - exprStr: "NULL IS TRUE", - resultStr: "0", - }, - { - exprStr: "1 IS FALSE", - resultStr: "0", - }, - { - exprStr: "2 IS FALSE", - resultStr: "0", - }, - { - exprStr: "0 IS FALSE", - resultStr: "1", - }, - { - exprStr: "NULL IS NOT FALSE", - resultStr: "1", - }, - { - exprStr: "1 IS NOT TRUE", - resultStr: "0", - }, - { - exprStr: "2 IS NOT TRUE", - resultStr: "0", - }, - { - exprStr: "0 IS NOT TRUE", - resultStr: "1", - }, - { - exprStr: "NULL IS NOT TRUE", - resultStr: "1", - }, - { - exprStr: "1 IS NOT FALSE", - resultStr: "1", - }, - { - exprStr: "2 IS NOT FALSE", - resultStr: "1", - }, - { - exprStr: "0 IS NOT FALSE", - resultStr: "0", - }, - { - exprStr: "NULL IS NOT FALSE", - resultStr: "1", - }, - } - s.runTests(c, cases) -} - func (s *testEvaluatorSuite) TestLastInsertID(c *C) { defer testleak.AfterTest(c)() cases := []struct { diff --git a/plan/expression_rewriter.go b/plan/expression_rewriter.go index 5440fe3cf9..46f9130671 100644 --- a/plan/expression_rewriter.go +++ b/plan/expression_rewriter.go @@ -1,3 +1,16 @@ +// Copyright 2016 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + package plan import ( @@ -29,7 +42,9 @@ func EvalAstExpr(expr ast.ExprNode, ctx context.Context) (types.Datum, error) { ctx: ctx, allocator: new(idAllocator), colMapper: make(map[*ast.ColumnNameExpr]int), - is: ctx.GetSessionVars().TxnCtx.InfoSchema.(infoschema.InfoSchema), + } + if ctx.GetSessionVars().TxnCtx.InfoSchema != nil { + b.is = ctx.GetSessionVars().TxnCtx.InfoSchema.(infoschema.InfoSchema) } newExpr, _, err := b.rewrite(expr, nil, nil, true) if err != nil { diff --git a/plan/expression_test.go b/plan/expression_test.go new file mode 100644 index 0000000000..7dec83a69f --- /dev/null +++ b/plan/expression_test.go @@ -0,0 +1,414 @@ +// Copyright 2016 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +package plan + +import ( + "fmt" + + . "github.com/pingcap/check" + "github.com/pingcap/tidb/ast" + "github.com/pingcap/tidb/context" + "github.com/pingcap/tidb/model" + "github.com/pingcap/tidb/mysql" + "github.com/pingcap/tidb/parser" + "github.com/pingcap/tidb/util/charset" + "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/util/testleak" + "github.com/pingcap/tidb/util/testutil" + "github.com/pingcap/tidb/util/types" +) + +var _ = Suite(&testExpressionSuite{}) + +type testExpressionSuite struct { + *parser.Parser + ctx context.Context +} + +func (s *testExpressionSuite) SetUpSuite(c *C) { + s.Parser = parser.New() + s.ctx = mock.NewContext() +} + +func (s *testExpressionSuite) parseExpr(c *C, expr string) ast.ExprNode { + st, err := s.ParseOneStmt("select "+expr, "", "") + c.Assert(err, IsNil) + stmt := st.(*ast.SelectStmt) + return stmt.Fields.Fields[0].Expr +} + +type testCase struct { + exprStr string + resultStr string +} + +func (s *testExpressionSuite) runTests(c *C, cases []testCase) { + for _, ca := range cases { + expr := s.parseExpr(c, ca.exprStr) + val, err := EvalAstExpr(expr, s.ctx) + c.Assert(err, IsNil) + valStr := fmt.Sprintf("%v", val.GetValue()) + c.Assert(valStr, Equals, ca.resultStr, Commentf("for %s", ca.exprStr)) + } +} + +func (s *testExpressionSuite) TestBetween(c *C) { + defer testleak.AfterTest(c)() + cases := []testCase{ + {exprStr: "1 between 2 and 3", resultStr: "0"}, + {exprStr: "1 not between 2 and 3", resultStr: "1"}, + } + s.runTests(c, cases) +} + +func (s *testExpressionSuite) TestCaseWhen(c *C) { + defer testleak.AfterTest(c)() + cases := []testCase{ + { + exprStr: "case 1 when 1 then 'str1' when 2 then 'str2' end", + resultStr: "str1", + }, + { + exprStr: "case 2 when 1 then 'str1' when 2 then 'str2' end", + resultStr: "str2", + }, + { + exprStr: "case 3 when 1 then 'str1' when 2 then 'str2' end", + resultStr: "", + }, + { + exprStr: "case 4 when 1 then 'str1' when 2 then 'str2' else 'str3' end", + resultStr: "str3", + }, + } + s.runTests(c, cases) + + // When expression value changed, result set back to null. + valExpr := ast.NewValueExpr(1) + whenClause := &ast.WhenClause{Expr: ast.NewValueExpr(1), Result: ast.NewValueExpr(1)} + caseExpr := &ast.CaseExpr{ + Value: valExpr, + WhenClauses: []*ast.WhenClause{whenClause}, + } + v, err := EvalAstExpr(caseExpr, s.ctx) + c.Assert(err, IsNil) + c.Assert(v, testutil.DatumEquals, types.NewDatum(int64(1))) + valExpr.SetValue(4) + ast.ResetEvaluatedFlag(caseExpr) + v, err = EvalAstExpr(caseExpr, s.ctx) + c.Assert(err, IsNil) + c.Assert(v.Kind(), Equals, types.KindNull) +} + +func (s *testExpressionSuite) TestCast(c *C) { + defer testleak.AfterTest(c)() + f := types.NewFieldType(mysql.TypeLonglong) + + expr := &ast.FuncCastExpr{ + Expr: ast.NewValueExpr(1), + Tp: f, + } + ast.SetFlag(expr) + v, err := EvalAstExpr(expr, s.ctx) + c.Assert(err, IsNil) + c.Assert(v, testutil.DatumEquals, types.NewDatum(int64(1))) + + f.Flag |= mysql.UnsignedFlag + v, err = EvalAstExpr(expr, s.ctx) + c.Assert(err, IsNil) + c.Assert(v, testutil.DatumEquals, types.NewDatum(uint64(1))) + + f.Tp = mysql.TypeString + f.Charset = charset.CharsetBin + v, err = EvalAstExpr(expr, s.ctx) + c.Assert(err, IsNil) + c.Assert(v, testutil.DatumEquals, types.NewDatum([]byte("1"))) + + f.Tp = mysql.TypeString + f.Charset = "utf8" + v, err = EvalAstExpr(expr, s.ctx) + c.Assert(err, IsNil) + c.Assert(v, testutil.DatumEquals, types.NewDatum("1")) + + expr.Expr = ast.NewValueExpr(nil) + v, err = EvalAstExpr(expr, s.ctx) + c.Assert(err, IsNil) + c.Assert(v.Kind(), Equals, types.KindNull) +} + +func (s *testExpressionSuite) TestPatternIn(c *C) { + defer testleak.AfterTest(c)() + cases := []testCase{ + { + exprStr: "1 not in (1, 2, 3)", + resultStr: "0", + }, + { + exprStr: "1 in (1, 2, 3)", + resultStr: "1", + }, + { + exprStr: "1 in (2, 3)", + resultStr: "0", + }, + { + exprStr: "NULL in (2, 3)", + resultStr: "", + }, + { + exprStr: "NULL not in (2, 3)", + resultStr: "", + }, + { + exprStr: "NULL in (NULL, 3)", + resultStr: "", + }, + { + exprStr: "1 in (1, NULL)", + resultStr: "1", + }, + { + exprStr: "1 in (NULL, 1)", + resultStr: "1", + }, + { + exprStr: "2 in (1, NULL)", + resultStr: "", + }, + { + exprStr: "(-(23)++46/51*+51) in (+23)", + resultStr: "0", + }, + } + s.runTests(c, cases) +} + +func (s *testExpressionSuite) TestIsNull(c *C) { + defer testleak.AfterTest(c)() + cases := []testCase{ + { + exprStr: "1 IS NULL", + resultStr: "0", + }, + { + exprStr: "1 IS NOT NULL", + resultStr: "1", + }, + { + exprStr: "NULL IS NULL", + resultStr: "1", + }, + { + exprStr: "NULL IS NOT NULL", + resultStr: "0", + }, + } + s.runTests(c, cases) +} + +func (s *testExpressionSuite) TestIsTruth(c *C) { + defer testleak.AfterTest(c)() + cases := []testCase{ + { + exprStr: "1 IS TRUE", + resultStr: "1", + }, + { + exprStr: "2 IS TRUE", + resultStr: "1", + }, + { + exprStr: "0 IS TRUE", + resultStr: "0", + }, + { + exprStr: "NULL IS TRUE", + resultStr: "0", + }, + { + exprStr: "1 IS FALSE", + resultStr: "0", + }, + { + exprStr: "2 IS FALSE", + resultStr: "0", + }, + { + exprStr: "0 IS FALSE", + resultStr: "1", + }, + { + exprStr: "NULL IS NOT FALSE", + resultStr: "1", + }, + { + exprStr: "1 IS NOT TRUE", + resultStr: "0", + }, + { + exprStr: "2 IS NOT TRUE", + resultStr: "0", + }, + { + exprStr: "0 IS NOT TRUE", + resultStr: "1", + }, + { + exprStr: "NULL IS NOT TRUE", + resultStr: "1", + }, + { + exprStr: "1 IS NOT FALSE", + resultStr: "1", + }, + { + exprStr: "2 IS NOT FALSE", + resultStr: "1", + }, + { + exprStr: "0 IS NOT FALSE", + resultStr: "0", + }, + { + exprStr: "NULL IS NOT FALSE", + resultStr: "1", + }, + } + s.runTests(c, cases) +} + +func (s *testExpressionSuite) TestDateArith(c *C) { + defer testleak.AfterTest(c)() + + // list all test cases + tests := []struct { + Date interface{} + Interval interface{} + Unit string + AddResult interface{} + SubResult interface{} + error bool + }{ + // basic test + {"2011-11-11", 1, "DAY", "2011-11-12", "2011-11-10", false}, + // nil test + {nil, 1, "DAY", nil, nil, false}, + {"2011-11-11", nil, "DAY", nil, nil, false}, + // tests for different units + {"2011-11-11 10:10:10", 1000, "MICROSECOND", "2011-11-11 10:10:10.001000", "2011-11-11 10:10:09.999000", false}, + {"2011-11-11 10:10:10", "10", "SECOND", "2011-11-11 10:10:20", "2011-11-11 10:10:00", false}, + {"2011-11-11 10:10:10", "10", "MINUTE", "2011-11-11 10:20:10", "2011-11-11 10:00:10", false}, + {"2011-11-11 10:10:10", "10", "HOUR", "2011-11-11 20:10:10", "2011-11-11 00:10:10", false}, + {"2011-11-11 10:10:10", "11", "DAY", "2011-11-22 10:10:10", "2011-10-31 10:10:10", false}, + {"2011-11-11 10:10:10", "2", "WEEK", "2011-11-25 10:10:10", "2011-10-28 10:10:10", false}, + {"2011-11-11 10:10:10", "2", "MONTH", "2012-01-11 10:10:10", "2011-09-11 10:10:10", false}, + {"2011-11-11 10:10:10", "4", "QUARTER", "2012-11-11 10:10:10", "2010-11-11 10:10:10", false}, + {"2011-11-11 10:10:10", "2", "YEAR", "2013-11-11 10:10:10", "2009-11-11 10:10:10", false}, + {"2011-11-11 10:10:10", "10.00100000", "SECOND_MICROSECOND", "2011-11-11 10:10:20.100000", "2011-11-11 10:09:59.900000", false}, + {"2011-11-11 10:10:10", "10.0010000000", "SECOND_MICROSECOND", "2011-11-11 10:10:30", "2011-11-11 10:09:50", false}, + {"2011-11-11 10:10:10", "10.0010000010", "SECOND_MICROSECOND", "2011-11-11 10:10:30.000010", "2011-11-11 10:09:49.999990", false}, + {"2011-11-11 10:10:10", "10:10.100", "MINUTE_MICROSECOND", "2011-11-11 10:20:20.100000", "2011-11-11 09:59:59.900000", false}, + {"2011-11-11 10:10:10", "10:10", "MINUTE_SECOND", "2011-11-11 10:20:20", "2011-11-11 10:00:00", false}, + {"2011-11-11 10:10:10", "10:10:10.100", "HOUR_MICROSECOND", "2011-11-11 20:20:20.100000", "2011-11-10 23:59:59.900000", false}, + {"2011-11-11 10:10:10", "10:10:10", "HOUR_SECOND", "2011-11-11 20:20:20", "2011-11-11 00:00:00", false}, + {"2011-11-11 10:10:10", "10:10", "HOUR_MINUTE", "2011-11-11 20:20:10", "2011-11-11 00:00:10", false}, + {"2011-11-11 10:10:10", "11 10:10:10.100", "DAY_MICROSECOND", "2011-11-22 20:20:20.100000", "2011-10-30 23:59:59.900000", false}, + {"2011-11-11 10:10:10", "11 10:10:10", "DAY_SECOND", "2011-11-22 20:20:20", "2011-10-31 00:00:00", false}, + {"2011-11-11 10:10:10", "11 10:10", "DAY_MINUTE", "2011-11-22 20:20:10", "2011-10-31 00:00:10", false}, + {"2011-11-11 10:10:10", "11 10", "DAY_HOUR", "2011-11-22 20:10:10", "2011-10-31 00:10:10", false}, + {"2011-11-11 10:10:10", "11-1", "YEAR_MONTH", "2022-12-11 10:10:10", "2000-10-11 10:10:10", false}, + {"2011-11-11 10:10:10", "11-11", "YEAR_MONTH", "2023-10-11 10:10:10", "1999-12-11 10:10:10", false}, + // tests for interval in day forms + {"2011-11-11 10:10:10", "20", "DAY", "2011-12-01 10:10:10", "2011-10-22 10:10:10", false}, + {"2011-11-11 10:10:10", 19.88, "DAY", "2011-12-01 10:10:10", "2011-10-22 10:10:10", false}, + {"2011-11-11 10:10:10", "19.88", "DAY", "2011-11-30 10:10:10", "2011-10-23 10:10:10", false}, + {"2011-11-11 10:10:10", "prefix19suffix", "DAY", "2011-11-30 10:10:10", "2011-10-23 10:10:10", false}, + {"2011-11-11 10:10:10", "20-11", "DAY", "2011-12-01 10:10:10", "2011-10-22 10:10:10", false}, + {"2011-11-11 10:10:10", "20,11", "daY", "2011-12-01 10:10:10", "2011-10-22 10:10:10", false}, + {"2011-11-11 10:10:10", "1000", "dAy", "2014-08-07 10:10:10", "2009-02-14 10:10:10", false}, + {"2011-11-11 10:10:10", "true", "Day", "2011-11-12 10:10:10", "2011-11-10 10:10:10", false}, + {"2011-11-11 10:10:10", true, "Day", "2011-11-12 10:10:10", "2011-11-10 10:10:10", false}, + // test for different return data types + {"2011-11-11", 1, "DAY", "2011-11-12", "2011-11-10", false}, + {"2011-11-11", 10, "HOUR", "2011-11-11 10:00:00", "2011-11-10 14:00:00", false}, + {"2011-11-11", 10, "MINUTE", "2011-11-11 00:10:00", "2011-11-10 23:50:00", false}, + {"2011-11-11", 10, "SECOND", "2011-11-11 00:00:10", "2011-11-10 23:59:50", false}, + {"2011-11-11", "10:10", "HOUR_MINUTE", "2011-11-11 10:10:00", "2011-11-10 13:50:00", false}, + {"2011-11-11", "10:10:10", "HOUR_SECOND", "2011-11-11 10:10:10", "2011-11-10 13:49:50", false}, + {"2011-11-11", "10:10:10.101010", "HOUR_MICROSECOND", "2011-11-11 10:10:10.101010", "2011-11-10 13:49:49.898990", false}, + {"2011-11-11", "10:10", "MINUTE_SECOND", "2011-11-11 00:10:10", "2011-11-10 23:49:50", false}, + {"2011-11-11", "10:10.101010", "MINUTE_MICROSECOND", "2011-11-11 00:10:10.101010", "2011-11-10 23:49:49.898990", false}, + {"2011-11-11", "10.101010", "SECOND_MICROSECOND", "2011-11-11 00:00:10.101010", "2011-11-10 23:59:49.898990", false}, + {"2011-11-11 00:00:00", 1, "DAY", "2011-11-12 00:00:00", "2011-11-10 00:00:00", false}, + {"2011-11-11 00:00:00", 10, "HOUR", "2011-11-11 10:00:00", "2011-11-10 14:00:00", false}, + {"2011-11-11 00:00:00", 10, "MINUTE", "2011-11-11 00:10:00", "2011-11-10 23:50:00", false}, + {"2011-11-11 00:00:00", 10, "SECOND", "2011-11-11 00:00:10", "2011-11-10 23:59:50", false}, + // tests for invalid input + {"2011-11-11", "abc1000", "MICROSECOND", nil, nil, true}, + {"20111111 10:10:10", "1", "DAY", nil, nil, true}, + {"2011-11-11", "10", "SECOND_MICROSECOND", nil, nil, true}, + {"2011-11-11", "10.0000", "MINUTE_MICROSECOND", nil, nil, true}, + {"2011-11-11", "10:10:10", "MINUTE_MICROSECOND", nil, nil, true}, + } + + // run the test cases + for _, t := range tests { + op := ast.NewValueExpr(ast.DateAdd) + dateArithInterval := ast.NewValueExpr( + ast.DateArithInterval{ + Unit: t.Unit, + Interval: ast.NewValueExpr(t.Interval), + }, + ) + date := ast.NewValueExpr(t.Date) + expr := &ast.FuncCallExpr{ + FnName: model.NewCIStr("DATE_ARITH"), + Args: []ast.ExprNode{ + op, + date, + dateArithInterval, + }, + } + ast.SetFlag(expr) + v, err := EvalAstExpr(expr, s.ctx) + if t.error == true { + c.Assert(err, NotNil) + } else { + c.Assert(err, IsNil) + if v.IsNull() { + c.Assert(nil, Equals, t.AddResult) + } else { + c.Assert(v.Kind(), Equals, types.KindMysqlTime) + value := v.GetMysqlTime() + c.Assert(value.String(), Equals, t.AddResult) + } + } + + op = ast.NewValueExpr(ast.DateSub) + expr.Args[0] = op + v, err = EvalAstExpr(expr, s.ctx) + if t.error == true { + c.Assert(err, NotNil) + } else { + c.Assert(err, IsNil) + if v.IsNull() { + c.Assert(nil, Equals, t.AddResult) + } else { + c.Assert(v.Kind(), Equals, types.KindMysqlTime) + value := v.GetMysqlTime() + c.Assert(value.String(), Equals, t.SubResult) + } + } + } +}