// Copyright 2017 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 expression import ( "time" . "github.com/pingcap/check" "github.com/pingcap/errors" "github.com/pingcap/parser/ast" "github.com/pingcap/parser/model" "github.com/pingcap/parser/mysql" "github.com/pingcap/tidb/types" "github.com/pingcap/tidb/types/json" "github.com/pingcap/tidb/util/chunk" ) func (s *testEvaluatorSuite) TestCompareFunctionWithRefine(c *C) { tblInfo := newTestTableBuilder("").add("a", mysql.TypeLong).build() tests := []struct { exprStr string result string }{ {"a < '1.0'", "lt(a, 1)"}, {"a <= '1.0'", "le(a, 1)"}, {"a > '1'", "gt(a, 1)"}, {"a >= '1'", "ge(a, 1)"}, {"a = '1'", "eq(a, 1)"}, {"a <=> '1'", "nulleq(a, 1)"}, {"a != '1'", "ne(a, 1)"}, {"a < '1.1'", "lt(a, 2)"}, {"a <= '1.1'", "le(a, 1)"}, {"a > 1.1", "gt(a, 1)"}, {"a >= '1.1'", "ge(a, 2)"}, {"a = '1.1'", "0"}, {"a <=> '1.1'", "0"}, {"a != '1.1'", "ne(cast(a, double BINARY), 1.1)"}, {"'1' < a", "lt(1, a)"}, {"'1' <= a", "le(1, a)"}, {"'1' > a", "gt(1, a)"}, {"'1' >= a", "ge(1, a)"}, {"'1' = a", "eq(1, a)"}, {"'1' <=> a", "nulleq(1, a)"}, {"'1' != a", "ne(1, a)"}, {"'1.1' < a", "lt(1, a)"}, {"'1.1' <= a", "le(2, a)"}, {"'1.1' > a", "gt(2, a)"}, {"'1.1' >= a", "ge(1, a)"}, {"'1.1' = a", "0"}, {"'1.1' <=> a", "0"}, {"'1.1' != a", "ne(1.1, cast(a, double BINARY))"}, {"'123456789123456711111189' = a", "0"}, {"123456789123456789.12345 = a", "0"}, {"123456789123456789123456789.12345 > a", "1"}, {"-123456789123456789123456789.12345 > a", "0"}, {"123456789123456789123456789.12345 < a", "0"}, {"-123456789123456789123456789.12345 < a", "1"}, {"'aaaa'=a", "eq(0, a)"}, } cols, names := ColumnInfos2ColumnsAndNames(s.ctx, model.NewCIStr(""), tblInfo.Name, tblInfo.Columns, tblInfo) schema := NewSchema(cols...) for _, t := range tests { f, err := ParseSimpleExprsWithNames(s.ctx, t.exprStr, schema, names) c.Assert(err, IsNil) c.Assert(f[0].String(), Equals, t.result) } } func (s *testEvaluatorSuite) TestCompare(c *C) { intVal, uintVal, realVal, stringVal, decimalVal := 1, uint64(1), 1.1, "123", types.NewDecFromFloatForTest(123.123) timeVal := types.NewTime(types.FromGoTime(time.Now()), mysql.TypeDatetime, 6) durationVal := types.Duration{Duration: time.Duration(12*time.Hour + 1*time.Minute + 1*time.Second)} jsonVal := json.CreateBinary("123") // test cases for generating function signatures. tests := []struct { arg0 interface{} arg1 interface{} funcName string tp byte expected int64 }{ {intVal, intVal, ast.LT, mysql.TypeLonglong, 0}, {stringVal, stringVal, ast.LT, mysql.TypeVarString, 0}, {intVal, decimalVal, ast.LT, mysql.TypeNewDecimal, 1}, {realVal, decimalVal, ast.LT, mysql.TypeDouble, 1}, {durationVal, durationVal, ast.LT, mysql.TypeDuration, 0}, {realVal, realVal, ast.LT, mysql.TypeDouble, 0}, {intVal, intVal, ast.NullEQ, mysql.TypeLonglong, 1}, {decimalVal, decimalVal, ast.LE, mysql.TypeNewDecimal, 1}, {decimalVal, decimalVal, ast.GT, mysql.TypeNewDecimal, 0}, {decimalVal, decimalVal, ast.GE, mysql.TypeNewDecimal, 1}, {decimalVal, decimalVal, ast.NE, mysql.TypeNewDecimal, 0}, {decimalVal, decimalVal, ast.EQ, mysql.TypeNewDecimal, 1}, {decimalVal, decimalVal, ast.NullEQ, mysql.TypeNewDecimal, 1}, {durationVal, durationVal, ast.LE, mysql.TypeDuration, 1}, {durationVal, durationVal, ast.GT, mysql.TypeDuration, 0}, {durationVal, durationVal, ast.GE, mysql.TypeDuration, 1}, {durationVal, durationVal, ast.EQ, mysql.TypeDuration, 1}, {durationVal, durationVal, ast.NE, mysql.TypeDuration, 0}, {durationVal, durationVal, ast.NullEQ, mysql.TypeDuration, 1}, {nil, nil, ast.NullEQ, mysql.TypeNull, 1}, {nil, intVal, ast.NullEQ, mysql.TypeDouble, 0}, {uintVal, intVal, ast.NullEQ, mysql.TypeLonglong, 1}, {uintVal, intVal, ast.EQ, mysql.TypeLonglong, 1}, {intVal, uintVal, ast.NullEQ, mysql.TypeLonglong, 1}, {intVal, uintVal, ast.EQ, mysql.TypeLonglong, 1}, {timeVal, timeVal, ast.LT, mysql.TypeDatetime, 0}, {timeVal, timeVal, ast.LE, mysql.TypeDatetime, 1}, {timeVal, timeVal, ast.GT, mysql.TypeDatetime, 0}, {timeVal, timeVal, ast.GE, mysql.TypeDatetime, 1}, {timeVal, timeVal, ast.EQ, mysql.TypeDatetime, 1}, {timeVal, timeVal, ast.NE, mysql.TypeDatetime, 0}, {timeVal, timeVal, ast.NullEQ, mysql.TypeDatetime, 1}, {jsonVal, jsonVal, ast.LT, mysql.TypeJSON, 0}, {jsonVal, jsonVal, ast.LE, mysql.TypeJSON, 1}, {jsonVal, jsonVal, ast.GT, mysql.TypeJSON, 0}, {jsonVal, jsonVal, ast.GE, mysql.TypeJSON, 1}, {jsonVal, jsonVal, ast.NE, mysql.TypeJSON, 0}, {jsonVal, jsonVal, ast.EQ, mysql.TypeJSON, 1}, {jsonVal, jsonVal, ast.NullEQ, mysql.TypeJSON, 1}, } for _, t := range tests { bf, err := funcs[t.funcName].getFunction(s.ctx, s.primitiveValsToConstants([]interface{}{t.arg0, t.arg1})) c.Assert(err, IsNil) args := bf.getArgs() c.Assert(args[0].GetType().Tp, Equals, t.tp) c.Assert(args[1].GetType().Tp, Equals, t.tp) res, isNil, err := bf.evalInt(chunk.Row{}) c.Assert(err, IsNil) c.Assert(isNil, IsFalse) c.Assert(res, Equals, t.expected) } // test decimalCol, stringCon := &Column{RetType: types.NewFieldType(mysql.TypeNewDecimal)}, &Constant{RetType: types.NewFieldType(mysql.TypeVarchar)} bf, err := funcs[ast.LT].getFunction(s.ctx, []Expression{decimalCol, stringCon}) c.Assert(err, IsNil) args := bf.getArgs() c.Assert(args[0].GetType().Tp, Equals, mysql.TypeNewDecimal) c.Assert(args[1].GetType().Tp, Equals, mysql.TypeNewDecimal) // test