func (s *testEvaluatorSuite) TestLength(c *C) { defer testleak.AfterTest(c)() d, err := builtinLength(types.MakeDatums([]interface{}{nil}...), s.ctx) c.Assert(err, IsNil) c.Assert(d.Kind(), Equals, types.KindNull) tbl := []struct { Input interface{} Expected int64 }{ {"abc", 3}, {1, 1}, {3.14, 4}, {types.Time{Time: types.FromGoTime(time.Now()), Fsp: 6, Type: mysql.TypeDatetime}, 26}, {types.Bit{Value: 1, Width: 8}, 1}, {types.Hex{Value: 1}, 1}, {types.Set{Value: 1, Name: "abc"}, 3}, } dtbl := tblToDtbl(tbl) for _, t := range dtbl { d, err = builtinLength(t["Input"], s.ctx) c.Assert(err, IsNil) c.Assert(d, testutil.DatumEquals, t["Expected"][0]) } }
func dumpBinaryDateTime(t types.Time, loc *time.Location) (data []byte) { if t.Type == mysql.TypeTimestamp && loc != nil { t1, err := t.Time.GoTime() if err != nil { // TODO: Fix here. } t.Time = types.FromGoTime(t1.In(loc)) } year, mon, day := t.Time.Year(), t.Time.Month(), t.Time.Day() if t.IsZero() { year, mon, day = 1, int(time.January), 1 } switch t.Type { case mysql.TypeTimestamp, mysql.TypeDatetime: data = append(data, 11) data = append(data, dumpUint16(uint16(year))...) data = append(data, byte(mon), byte(day), byte(t.Time.Hour()), byte(t.Time.Minute()), byte(t.Time.Second())) data = append(data, dumpUint32(uint32(t.Time.Microsecond()))...) case mysql.TypeDate, mysql.TypeNewDate: data = append(data, 4) data = append(data, dumpUint16(uint16(year))...) //year data = append(data, byte(mon), byte(day)) } return }
func builtinNow(args []types.Datum, ctx context.Context) (d types.Datum, err error) { // TODO: if NOW is used in stored function or trigger, NOW will return the beginning time // of the execution. fsp := 0 sc := ctx.GetSessionVars().StmtCtx if len(args) == 1 && !args[0].IsNull() { if fsp, err = checkFsp(sc, args[0]); err != nil { d.SetNull() return d, errors.Trace(err) } } tr, err := types.RoundFrac(time.Now(), int(fsp)) if err != nil { d.SetNull() return d, errors.Trace(err) } t := types.Time{ Time: types.FromGoTime(tr), Type: mysql.TypeDatetime, // set unspecified for later round Fsp: fsp, } d.SetMysqlTime(t) return d, nil }
// See https://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_utc-date func builtinUTCDate(args []types.Datum, _ context.Context) (d types.Datum, err error) { year, month, day := time.Now().UTC().Date() t := types.Time{ Time: types.FromGoTime(time.Date(year, month, day, 0, 0, 0, 0, time.UTC)), Type: mysql.TypeDate, Fsp: types.UnspecifiedFsp} d.SetMysqlTime(t) return d, nil }
// checkArgs makes sure all the arguments' types are known and can be handled. // integer types are converted to int64 and uint64, time.Time is converted to types.Time. // time.Duration is converted to types.Duration, other known types are leaved as it is. func checkArgs(args ...interface{}) error { for i, v := range args { switch x := v.(type) { case bool: if x { args[i] = int64(1) } else { args[i] = int64(0) } case int8: args[i] = int64(x) case int16: args[i] = int64(x) case int32: args[i] = int64(x) case int: args[i] = int64(x) case uint8: args[i] = uint64(x) case uint16: args[i] = uint64(x) case uint32: args[i] = uint64(x) case uint: args[i] = uint64(x) case int64: case uint64: case float32: case float64: case string: case []byte: case time.Duration: args[i] = types.Duration{Duration: x} case time.Time: args[i] = types.Time{Time: types.FromGoTime(x), Type: mysql.TypeDatetime} case nil: default: return errors.Errorf("cannot use arg[%d] (type %T):unsupported type", i, v) } } return nil }
func builtinDateArith(args []types.Datum, ctx context.Context) (d types.Datum, err error) { // Op is used for distinguishing date_add and date_sub. // args[0] -> Op // args[1] -> Date // args[2] -> DateArithInterval // health check for date and interval if args[1].IsNull() { return d, nil } nodeDate := args[1] nodeInterval := args[2].GetInterface().(ast.DateArithInterval) nodeIntervalIntervalDatum := nodeInterval.Interval.GetDatum() if nodeIntervalIntervalDatum.IsNull() { return d, nil } // parse date fieldType := mysql.TypeDate var resultField *types.FieldType switch nodeDate.Kind() { case types.KindMysqlTime: x := nodeDate.GetMysqlTime() if (x.Type == mysql.TypeDatetime) || (x.Type == mysql.TypeTimestamp) { fieldType = mysql.TypeDatetime } case types.KindString: x := nodeDate.GetString() if !types.IsDateFormat(x) { fieldType = mysql.TypeDatetime } case types.KindInt64: x := nodeDate.GetInt64() if t, err1 := types.ParseTimeFromInt64(x); err1 == nil { if (t.Type == mysql.TypeDatetime) || (t.Type == mysql.TypeTimestamp) { fieldType = mysql.TypeDatetime } } } sc := ctx.GetSessionVars().StmtCtx if types.IsClockUnit(nodeInterval.Unit) { fieldType = mysql.TypeDatetime } resultField = types.NewFieldType(fieldType) resultField.Decimal = types.MaxFsp value, err := nodeDate.ConvertTo(ctx.GetSessionVars().StmtCtx, resultField) if err != nil { return d, ErrInvalidOperation.Gen("DateArith invalid args, need date but get %T", nodeDate) } if value.IsNull() { return d, ErrInvalidOperation.Gen("DateArith invalid args, need date but get %v", value.GetValue()) } if value.Kind() != types.KindMysqlTime { return d, ErrInvalidOperation.Gen("DateArith need time type, but got %T", value.GetValue()) } result := value.GetMysqlTime() // parse interval var interval string if strings.ToLower(nodeInterval.Unit) == "day" { day, err1 := parseDayInterval(sc, *nodeIntervalIntervalDatum) if err1 != nil { return d, ErrInvalidOperation.Gen("DateArith invalid day interval, need int but got %T", nodeIntervalIntervalDatum.GetString()) } interval = fmt.Sprintf("%d", day) } else { if nodeIntervalIntervalDatum.Kind() == types.KindString { interval = fmt.Sprintf("%v", nodeIntervalIntervalDatum.GetString()) } else { ii, err1 := nodeIntervalIntervalDatum.ToInt64(sc) if err1 != nil { return d, errors.Trace(err1) } interval = fmt.Sprintf("%v", ii) } } year, month, day, duration, err := types.ExtractTimeValue(nodeInterval.Unit, interval) if err != nil { return d, errors.Trace(err) } op := args[0].GetInterface().(ast.DateArithType) if op == ast.DateSub { year, month, day, duration = -year, -month, -day, -duration } t, err := result.Time.GoTime() if err != nil { return d, errors.Trace(err) } t = t.Add(duration) t = t.AddDate(int(year), int(month), int(day)) if t.Nanosecond() == 0 { result.Fsp = 0 } result.Time = types.FromGoTime(t) d.SetMysqlTime(result) return d, nil }
// See http://dev.mysql.com/doc/refman/5.7/en/date-and-time-functions.html#function_from-unixtime func builtinFromUnixTime(args []types.Datum, ctx context.Context) (d types.Datum, err error) { sc := ctx.GetSessionVars().StmtCtx unixTimeStamp, err := args[0].ToDecimal(sc) if err != nil { return d, errors.Trace(err) } // 0 <= unixTimeStamp <= INT32_MAX if unixTimeStamp.IsNegative() { return } integralPart, err := unixTimeStamp.ToInt() if err == types.ErrTruncated { err = nil } if err != nil { return d, errors.Trace(err) } if integralPart > int64(math.MaxInt32) { return } // Split the integral part and fractional part of a decimal timestamp. // e.g. for timestamp 12345.678, // first get the integral part 12345, // then (12345.678 - 12345) * (10^9) to get the decimal part and convert it to nanosecond precision. integerDecimalTp := new(types.MyDecimal).FromInt(integralPart) fracDecimalTp := new(types.MyDecimal) err = types.DecimalSub(unixTimeStamp, integerDecimalTp, fracDecimalTp) if err != nil { return d, errors.Trace(err) } nano := new(types.MyDecimal).FromInt(int64(time.Second)) x := new(types.MyDecimal) err = types.DecimalMul(fracDecimalTp, nano, x) if err != nil { return d, errors.Trace(err) } fractionalPart, err := x.ToInt() // here fractionalPart is result multiplying the original fractional part by 10^9. if err == types.ErrTruncated { err = nil } if err != nil { return d, errors.Trace(err) } _, fracDigitsNumber := unixTimeStamp.PrecisionAndFrac() fsp := fracDigitsNumber if fracDigitsNumber > types.MaxFsp { fsp = types.MaxFsp } tr, err := types.RoundFrac(time.Unix(integralPart, fractionalPart), fsp) if err != nil { return d, errors.Trace(err) } t := types.Time{ Time: types.FromGoTime(tr), Type: mysql.TypeDatetime, Fsp: fsp, } if args[0].Kind() == types.KindString { // Keep consistent with MySQL. t.Fsp = types.MaxFsp } d.SetMysqlTime(t) if len(args) == 1 { return } return builtinDateFormat([]types.Datum{d, args[1]}, ctx) }
func getTimeValue(ctx context.Context, v interface{}, tp byte, fsp int) (d types.Datum, err error) { value := types.Time{ Type: tp, Fsp: fsp, } defaultTime, err := getSystemTimestamp(ctx) if err != nil { return d, errors.Trace(err) } switch x := v.(type) { case string: upperX := strings.ToUpper(x) if upperX == CurrentTimestamp { value.Time = types.FromGoTime(defaultTime) } else if upperX == ZeroTimestamp { value, _ = types.ParseTimeFromNum(0, tp, fsp) } else { value, err = types.ParseTime(x, tp, fsp) if err != nil { return d, errors.Trace(err) } } case *ast.ValueExpr: switch x.Kind() { case types.KindString: value, err = types.ParseTime(x.GetString(), tp, fsp) if err != nil { return d, errors.Trace(err) } case types.KindInt64: value, err = types.ParseTimeFromNum(x.GetInt64(), tp, fsp) if err != nil { return d, errors.Trace(err) } case types.KindNull: return d, nil default: return d, errors.Trace(errDefaultValue) } case *ast.FuncCallExpr: if x.FnName.L == currentTimestampL { d.SetString(CurrentTimestamp) return d, nil } return d, errors.Trace(errDefaultValue) case *ast.UnaryOperationExpr: // support some expression, like `-1` v, err := Eval(ctx, x) if err != nil { return d, errors.Trace(err) } ft := types.NewFieldType(mysql.TypeLonglong) xval, err := v.ConvertTo(ctx.GetSessionVars().StmtCtx, ft) if err != nil { return d, errors.Trace(err) } value, err = types.ParseTimeFromNum(xval.GetInt64(), tp, fsp) if err != nil { return d, errors.Trace(err) } default: return d, nil } d.SetMysqlTime(value) return d, nil }
func (s *testEvaluatorSuite) TestUnaryOp(c *C) { defer testleak.AfterTest(c)() tbl := []struct { arg interface{} op opcode.Op result interface{} }{ // test NOT. {1, opcode.Not, int64(0)}, {0, opcode.Not, int64(1)}, {nil, opcode.Not, nil}, {types.Hex{Value: 0}, opcode.Not, int64(1)}, {types.Bit{Value: 0, Width: 1}, opcode.Not, int64(1)}, {types.Enum{Name: "a", Value: 1}, opcode.Not, int64(0)}, {types.Set{Name: "a", Value: 1}, opcode.Not, int64(0)}, // test BitNeg. {nil, opcode.BitNeg, nil}, {-1, opcode.BitNeg, uint64(0)}, // test Plus. {nil, opcode.Plus, nil}, {float64(1.0), opcode.Plus, float64(1.0)}, {int64(1), opcode.Plus, int64(1)}, {int64(1), opcode.Plus, int64(1)}, {uint64(1), opcode.Plus, uint64(1)}, {"1.0", opcode.Plus, "1.0"}, {[]byte("1.0"), opcode.Plus, []byte("1.0")}, {types.Hex{Value: 1}, opcode.Plus, types.Hex{Value: 1}}, {types.Bit{Value: 1, Width: 1}, opcode.Plus, types.Bit{Value: 1, Width: 1}}, {true, opcode.Plus, int64(1)}, {false, opcode.Plus, int64(0)}, {types.Enum{Name: "a", Value: 1}, opcode.Plus, types.Enum{Name: "a", Value: 1}}, {types.Set{Name: "a", Value: 1}, opcode.Plus, types.Set{Name: "a", Value: 1}}, // test Minus. {nil, opcode.Minus, nil}, {float64(1.0), opcode.Minus, float64(-1.0)}, {int64(1), opcode.Minus, int64(-1)}, {int64(1), opcode.Minus, int64(-1)}, {uint64(1), opcode.Minus, -int64(1)}, {"1.0", opcode.Minus, -1.0}, {[]byte("1.0"), opcode.Minus, -1.0}, {types.Hex{Value: 1}, opcode.Minus, -1.0}, {types.Bit{Value: 1, Width: 1}, opcode.Minus, -1.0}, {true, opcode.Minus, int64(-1)}, {false, opcode.Minus, int64(0)}, {types.Enum{Name: "a", Value: 1}, opcode.Minus, -1.0}, {types.Set{Name: "a", Value: 1}, opcode.Minus, -1.0}, } for i, t := range tbl { expr := &ast.UnaryOperationExpr{} expr.Op = t.op expr.V = ast.NewValueExpr(t.arg) result, err := Eval(s.ctx, expr) c.Assert(err, IsNil) c.Assert(result, testutil.DatumEquals, types.NewDatum(t.result), Commentf("%d", i)) } tbl = []struct { arg interface{} op opcode.Op result interface{} }{ {types.NewDecFromInt(1), opcode.Plus, types.NewDecFromInt(1)}, {types.Duration{Duration: time.Duration(838*3600 + 59*60 + 59), Fsp: types.DefaultFsp}, opcode.Plus, types.Duration{Duration: time.Duration(838*3600 + 59*60 + 59), Fsp: types.DefaultFsp}}, {types.Time{ Time: types.FromGoTime(time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)), Type: mysql.TypeDatetime, Fsp: 0}, opcode.Plus, types.Time{ Time: types.FromGoTime(time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)), Type: mysql.TypeDatetime, Fsp: 0}}, {types.NewDecFromInt(1), opcode.Minus, types.NewDecFromInt(-1)}, {types.ZeroDuration, opcode.Minus, new(types.MyDecimal)}, {types.Time{Time: types.FromGoTime(time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)), Type: mysql.TypeDatetime, Fsp: 0}, opcode.Minus, types.NewDecFromInt(-20091110230000)}, } for _, t := range tbl { expr := &ast.UnaryOperationExpr{Op: t.op, V: ast.NewValueExpr(t.arg)} result, err := Eval(s.ctx, expr) c.Assert(err, IsNil) ret, err := result.CompareDatum(s.ctx.GetSessionVars().StmtCtx, types.NewDatum(t.result)) c.Assert(err, IsNil) c.Assert(ret, Equals, 0) } }
func (s *testEvaluatorSuite) TestBinopNumeric(c *C) { defer testleak.AfterTest(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, types.NewDecFromInt(1), 2}, {uint64(1), opcode.Plus, 1, 2}, {uint64(1), opcode.Plus, uint64(1), 2}, {uint64(1), opcode.Plus, -1, 0}, {1, opcode.Plus, []byte("1"), 2}, {1, opcode.Plus, types.Hex{Value: 1}, 2}, {1, opcode.Plus, types.Bit{Value: 1, Width: 1}, 2}, {1, opcode.Plus, types.Enum{Name: "a", Value: 1}, 2}, {1, opcode.Plus, types.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, types.NewDecFromInt(1), 0}, {uint64(1), opcode.Minus, 1, 0}, {uint64(1), opcode.Minus, uint64(1), 0}, {types.NewDecFromInt(1), 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, types.NewDecFromInt(1), 1}, {uint64(1), opcode.Mul, 1, 1}, {uint64(1), opcode.Mul, uint64(1), 1}, {types.Time{Time: types.FromDate(0, 0, 0, 0, 0, 0, 0)}, opcode.Mul, 0, 0}, {types.ZeroDuration, opcode.Mul, 0, 0}, {types.Time{Time: types.FromGoTime(time.Now()), Fsp: 0, Type: mysql.TypeDatetime}, opcode.Mul, 0, 0}, {types.Time{Time: types.FromGoTime(time.Now()), Fsp: 6, Type: mysql.TypeDatetime}, opcode.Mul, 0, 0}, {types.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}, {types.NewDecFromInt(10), opcode.Mod, 2, 0}, {types.NewDecFromInt(10), 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(s.ctx, expr) c.Assert(err, IsNil) switch v.Kind() { case types.KindNull: c.Assert(t.ret, IsNil) default: // we use float64 as the result type check for all. sc := s.ctx.GetSessionVars().StmtCtx f, err := v.ToFloat64(sc) c.Assert(err, IsNil) d := types.NewDatum(t.ret) r, err := d.ToFloat64(sc) c.Assert(err, IsNil) c.Assert(r, Equals, f) } } }