func builtinPow(args []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { x, err := types.ToFloat64(args[0]) if err != nil { return nil, errors.Trace(err) } y, err := types.ToFloat64(args[1]) if err != nil { return nil, errors.Trace(err) } return math.Pow(x, y), nil }
func builtinAbs(args []interface{}, ctx map[interface{}]interface{}) (v interface{}, err error) { switch x := args[0].(type) { case nil: return nil, nil case uint, uint8, uint16, uint32, uint64: return x, nil case int, int8, int16, int32, int64: // we don't need to handle error here, it must be success v, _ := types.ToInt64(args[0]) if v >= 0 { return x, nil } // TODO: handle overflow if x is MinInt64 return -v, nil case *types.DataItem: args[0] = x.Data return builtinAbs(args, ctx) default: // we will try to convert other types to float // TODO: if time has no precision, it will be a integer f, err := types.ToFloat64(args[0]) return math.Abs(f), errors.Trace(err) } }
func calculateSum(sum interface{}, v interface{}) (interface{}, error) { // for avg and sum calculation // avg and sum use decimal for integer and decimal type, use float for others // see https://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html var ( data interface{} err error ) switch y := v.(type) { case int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64: data, err = mysql.ConvertToDecimal(v) case mysql.Decimal: data = y default: data, err = types.ToFloat64(v) } if err != nil { return nil, err } switch x := sum.(type) { case nil: return data, nil case float64: return x + data.(float64), nil case mysql.Decimal: return x.Add(data.(mysql.Decimal)), nil default: return nil, errors.Errorf("invalid value %v(%T) for aggregate", x, x) } }
func (o *BinaryOperation) evalDiv(a interface{}, b interface{}) (interface{}, error) { // MySQL support integer divison Div and division operator / // we use opcode.Div for division operator and will use another for integer division later. // for division operator, we will use float64 for calculation. switch x := a.(type) { case float64: y, err := types.ToFloat64(b) if err != nil { return nil, err } if y == 0 { return nil, nil } return x / y, nil default: // the scale of the result is the scale of the first operand plus // the value of the div_precision_increment system variable (which is 4 by default) // we will use 4 here xa, err := types.ToDecimal(a) if err != nil { return nil, o.traceErr(err) } xb, err := types.ToDecimal(b) if err != nil { return nil, o.traceErr(err) } if f, _ := xb.Float64(); f == 0 { // division by zero return null return nil, nil } return xa.Div(xb), nil } }
func (s *testEvaluatorSuite) TestBinopNumeric(c *C) { ctx := mock.NewContext() tbl := []struct { lhs interface{} op opcode.Op rhs interface{} ret interface{} }{ // plus {1, opcode.Plus, 1, 2}, {1, opcode.Plus, uint64(1), 2}, {1, opcode.Plus, "1", 2}, {1, opcode.Plus, mysql.NewDecimalFromInt(1, 0), 2}, {uint64(1), opcode.Plus, 1, 2}, {uint64(1), opcode.Plus, uint64(1), 2}, {1, opcode.Plus, []byte("1"), 2}, {1, opcode.Plus, mysql.Hex{Value: 1}, 2}, {1, opcode.Plus, mysql.Bit{Value: 1, Width: 1}, 2}, {1, opcode.Plus, mysql.Enum{Name: "a", Value: 1}, 2}, {1, opcode.Plus, mysql.Set{Name: "a", Value: 1}, 2}, // minus {1, opcode.Minus, 1, 0}, {1, opcode.Minus, uint64(1), 0}, {1, opcode.Minus, float64(1), 0}, {1, opcode.Minus, mysql.NewDecimalFromInt(1, 0), 0}, {uint64(1), opcode.Minus, 1, 0}, {uint64(1), opcode.Minus, uint64(1), 0}, {mysql.NewDecimalFromInt(1, 0), opcode.Minus, 1, 0}, {"1", opcode.Minus, []byte("1"), 0}, // mul {1, opcode.Mul, 1, 1}, {1, opcode.Mul, uint64(1), 1}, {1, opcode.Mul, float64(1), 1}, {1, opcode.Mul, mysql.NewDecimalFromInt(1, 0), 1}, {uint64(1), opcode.Mul, 1, 1}, {uint64(1), opcode.Mul, uint64(1), 1}, {mysql.Time{}, opcode.Mul, 0, 0}, {mysql.ZeroDuration, opcode.Mul, 0, 0}, {mysql.Time{Time: time.Now(), Fsp: 0, Type: mysql.TypeDatetime}, opcode.Mul, 0, 0}, {mysql.Time{Time: time.Now(), Fsp: 6, Type: mysql.TypeDatetime}, opcode.Mul, 0, 0}, {mysql.Duration{Duration: 100000000, Fsp: 6}, opcode.Mul, 0, 0}, // div {1, opcode.Div, float64(1), 1}, {1, opcode.Div, float64(0), nil}, {1, opcode.Div, 2, 0.5}, {1, opcode.Div, 0, nil}, // int div {1, opcode.IntDiv, 2, 0}, {1, opcode.IntDiv, uint64(2), 0}, {1, opcode.IntDiv, 0, nil}, {1, opcode.IntDiv, uint64(0), nil}, {uint64(1), opcode.IntDiv, 2, 0}, {uint64(1), opcode.IntDiv, uint64(2), 0}, {uint64(1), opcode.IntDiv, 0, nil}, {uint64(1), opcode.IntDiv, uint64(0), nil}, {1.0, opcode.IntDiv, 2.0, 0}, {1.0, opcode.IntDiv, 0, nil}, // mod {10, opcode.Mod, 2, 0}, {10, opcode.Mod, uint64(2), 0}, {10, opcode.Mod, 0, nil}, {10, opcode.Mod, uint64(0), nil}, {-10, opcode.Mod, uint64(2), 0}, {uint64(10), opcode.Mod, 2, 0}, {uint64(10), opcode.Mod, uint64(2), 0}, {uint64(10), opcode.Mod, 0, nil}, {uint64(10), opcode.Mod, uint64(0), nil}, {uint64(10), opcode.Mod, -2, 0}, {float64(10), opcode.Mod, 2, 0}, {float64(10), opcode.Mod, 0, nil}, {mysql.NewDecimalFromInt(10, 0), opcode.Mod, 2, 0}, {mysql.NewDecimalFromInt(10, 0), opcode.Mod, 0, nil}, } for _, t := range tbl { expr := &ast.BinaryOperationExpr{Op: t.op, L: ast.NewValueExpr(t.lhs), R: ast.NewValueExpr(t.rhs)} v, err := Eval(ctx, expr) c.Assert(err, IsNil) switch v.(type) { case nil: c.Assert(t.ret, IsNil) default: // we use float64 as the result type check for all. f, err := types.ToFloat64(v) c.Assert(err, IsNil) r, err := types.ToFloat64(t.ret) c.Assert(err, IsNil) c.Assert(r, Equals, f) } } }
func (s *testBinOpSuite) TestNumericOp(c *C) { tbl := []struct { lhs interface{} op opcode.Op rhs interface{} ret interface{} }{ // plus {1, opcode.Plus, 1, 2}, {1, opcode.Plus, uint64(1), 2}, {1, opcode.Plus, "1", 2}, {1, opcode.Plus, mysql.NewDecimalFromInt(1, 0), 2}, {uint64(1), opcode.Plus, 1, 2}, {uint64(1), opcode.Plus, uint64(1), 2}, // minus {1, opcode.Minus, 1, 0}, {1, opcode.Minus, uint64(1), 0}, {1, opcode.Minus, float64(1), 0}, {1, opcode.Minus, mysql.NewDecimalFromInt(1, 0), 0}, {uint64(1), opcode.Minus, 1, 0}, {uint64(1), opcode.Minus, uint64(1), 0}, {mysql.NewDecimalFromInt(1, 0), opcode.Minus, 1, 0}, // mul {1, opcode.Mul, 1, 1}, {1, opcode.Mul, uint64(1), 1}, {1, opcode.Mul, float64(1), 1}, {1, opcode.Mul, mysql.NewDecimalFromInt(1, 0), 1}, {uint64(1), opcode.Mul, 1, 1}, {uint64(1), opcode.Mul, uint64(1), 1}, {mysql.Time{}, opcode.Mul, 0, 0}, {mysql.ZeroDuration, opcode.Mul, 0, 0}, // div {1, opcode.Div, float64(1), 1}, {1, opcode.Div, float64(0), nil}, {1, opcode.Div, 2, 0.5}, {1, opcode.Div, 0, nil}, // int div {1, opcode.IntDiv, 2, 0}, {1, opcode.IntDiv, uint64(2), 0}, {1, opcode.IntDiv, 0, nil}, {1, opcode.IntDiv, uint64(0), nil}, {uint64(1), opcode.IntDiv, 2, 0}, {uint64(1), opcode.IntDiv, uint64(2), 0}, {uint64(1), opcode.IntDiv, 0, nil}, {uint64(1), opcode.IntDiv, uint64(0), nil}, {1.0, opcode.IntDiv, 2.0, 0}, {1.0, opcode.IntDiv, 0, nil}, // mod {10, opcode.Mod, 2, 0}, {10, opcode.Mod, uint64(2), 0}, {10, opcode.Mod, 0, nil}, {10, opcode.Mod, uint64(0), nil}, {-10, opcode.Mod, uint64(2), 0}, {uint64(10), opcode.Mod, 2, 0}, {uint64(10), opcode.Mod, uint64(2), 0}, {uint64(10), opcode.Mod, 0, nil}, {uint64(10), opcode.Mod, uint64(0), nil}, {uint64(10), opcode.Mod, -2, 0}, {float64(10), opcode.Mod, 2, 0}, {float64(10), opcode.Mod, 0, nil}, {mysql.NewDecimalFromInt(10, 0), opcode.Mod, 2, 0}, {mysql.NewDecimalFromInt(10, 0), opcode.Mod, 0, nil}, } for _, t := range tbl { expr := NewBinaryOperation(t.op, Value{t.lhs}, Value{t.rhs}) v, err := expr.Eval(nil, nil) c.Assert(err, IsNil) switch v.(type) { case nil: c.Assert(t.ret, IsNil) default: // we use float64 as the result type check for all. f, err := types.ToFloat64(v) c.Assert(err, IsNil) r, err := types.ToFloat64(t.ret) c.Assert(err, IsNil) c.Assert(r, Equals, f) } } // test error expr := &BinaryOperation{} _, err := expr.evalPlus(1, 1) c.Assert(err, NotNil) _, err = expr.evalMinus(1, 1) c.Assert(err, NotNil) _, err = expr.evalMul(1, 1) c.Assert(err, NotNil) _, err = expr.evalDiv("abc", 1) c.Assert(err, NotNil) _, err = expr.evalDiv(float64(1), "abc") c.Assert(err, NotNil) _, err = expr.evalDiv(1, "abc") c.Assert(err, NotNil) _, err = expr.evalIntDiv("abc", 1) c.Assert(err, NotNil) _, err = expr.evalIntDiv(1, "abc") c.Assert(err, NotNil) _, err = expr.evalMod("abc", 1) c.Assert(err, NotNil) expr.L = Value{1} expr.R = Value{1} _, err = expr.evalArithmeticOp(nil, nil) c.Assert(err, NotNil) expr.L = mockExpr{err: errors.New("must error")} _, err = expr.evalArithmeticOp(nil, nil) c.Assert(err, NotNil) expr.L = Value{"abc"} expr.R = Value{1} _, err = expr.evalArithmeticOp(nil, nil) c.Assert(err, NotNil) expr.L = Value{1} expr.R = Value{"abc"} _, err = expr.evalArithmeticOp(nil, nil) c.Assert(err, NotNil) expr.Op = 0 _, err = expr.Eval(nil, nil) c.Assert(err, NotNil) }
func (s *testBuiltinSuite) TestGroupBy(c *C) { tbl := []struct { F string // args for every Eval round RoundArgs []interface{} Distinct bool Ret interface{} }{ {"avg", []interface{}{1, 1}, false, 1}, {"avg", []interface{}{1, 2}, true, 1.5}, {"avg", []interface{}{1.0, 2.0}, true, 1.5}, {"avg", []interface{}{1, 1}, true, 1}, {"avg", []interface{}{nil, nil}, true, nil}, {"count", []interface{}{1, 1}, false, 2}, {"count", []interface{}{1, 1}, true, 1}, {"count", []interface{}{nil, nil}, true, 0}, {"count", []interface{}{nil, nil}, false, 0}, {"count", []interface{}{nil, 1}, true, 1}, {"max", []interface{}{1, 2, 3}, false, 3}, {"max", []interface{}{nil, 2}, false, 2}, {"max", []interface{}{nil, nil}, false, nil}, {"min", []interface{}{1, 2, 3}, false, 1}, {"min", []interface{}{nil, 1}, false, 1}, {"min", []interface{}{nil, nil}, false, nil}, {"min", []interface{}{3, 2, 1}, false, 1}, {"sum", []interface{}{1, 1, 1, 1, 1}, false, 5}, {"sum", []interface{}{float64(1.0), float64(1.0)}, false, 2.0}, {"sum", []interface{}{1, 1, 1, 1, 1}, true, 1}, {"sum", []interface{}{1, mysql.NewDecimalFromInt(1, 0)}, false, 2}, {"sum", []interface{}{nil, nil}, false, nil}, {"sum", []interface{}{nil, nil}, true, nil}, } for _, t := range tbl { f, ok := Funcs[t.F] c.Assert(ok, IsTrue) m := map[interface{}]interface{}{} m[ExprEvalFn] = new(Func) m[ExprAggDistinct] = CreateAggregateDistinct(t.F, t.Distinct) for _, arg := range t.RoundArgs { args := []interface{}{arg} _, err := f.F(args, m) c.Assert(err, IsNil) } m[ExprAggDone] = struct{}{} v, err := f.F(nil, m) c.Assert(err, IsNil) switch v.(type) { case nil: c.Assert(t.Ret, IsNil) default: // we can not check decimal type directly, but we can convert all to float64 f, err := types.ToFloat64(v) c.Assert(err, IsNil) ret, err := types.ToFloat64(t.Ret) c.Assert(err, IsNil) c.Assert(f, Equals, ret) } } }
func (s *testBuiltinSuite) TestGroupBy(c *C) { tbl := []struct { F string // args for every Eval round RoundArgs []interface{} Distinct bool Ret interface{} }{ {"avg", []interface{}{1, 1}, false, 1}, {"avg", []interface{}{1, 2}, true, 1.5}, {"avg", []interface{}{1.0, 2.0}, true, 1.5}, {"avg", []interface{}{1, 1}, true, 1}, {"avg", []interface{}{nil, nil}, true, nil}, {"count", []interface{}{1, 1}, false, 2}, {"count", []interface{}{1, 1}, true, 1}, {"count", []interface{}{nil, nil}, true, 0}, {"count", []interface{}{nil, nil}, false, 0}, {"count", []interface{}{nil, 1}, true, 1}, {"max", []interface{}{1, 2, 3}, false, 3}, {"max", []interface{}{nil, 2}, false, 2}, {"max", []interface{}{nil, nil}, false, nil}, {"min", []interface{}{1, 2, 3}, false, 1}, {"min", []interface{}{nil, 1}, false, 1}, {"min", []interface{}{nil, nil}, false, nil}, {"min", []interface{}{3, 2, 1}, false, 1}, {"sum", []interface{}{1, 1, 1, 1, 1}, false, 5}, {"sum", []interface{}{float64(1.0), float64(1.0)}, false, 2.0}, {"sum", []interface{}{1, 1, 1, 1, 1}, true, 1}, {"sum", []interface{}{1, mysql.NewDecimalFromInt(1, 0)}, false, 2}, {"sum", []interface{}{nil, nil}, false, nil}, {"sum", []interface{}{nil, nil}, true, nil}, } for _, t := range tbl { // create a call and use dummy args. e, err := NewCall(t.F, []expression.Expression{Value{nil}}, t.Distinct) c.Assert(err, IsNil) call, ok := e.(*Call) c.Assert(ok, IsTrue) m := map[interface{}]interface{}{} for _, arg := range t.RoundArgs { call.Args = []expression.Expression{Value{arg}} _, err = call.Eval(nil, m) } m[ExprAggDone] = struct{}{} v, err := e.Eval(nil, m) c.Assert(err, IsNil) switch v.(type) { case nil: c.Assert(t.Ret, IsNil) default: // we can not check decimal type directly, but we can convert all to float64 f, err := types.ToFloat64(v) c.Assert(err, IsNil) ret, err := types.ToFloat64(t.Ret) c.Assert(err, IsNil) c.Assert(f, Equals, ret) } } }