Пример #1
0
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

}
Пример #2
0
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)
	}
}
Пример #3
0
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)
	}
}
Пример #4
0
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
	}
}
Пример #5
0
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)
		}
	}
}
Пример #6
0
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)
}
Пример #7
0
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)
		}
	}
}
Пример #8
0
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)
		}
	}
}