func (d *Datum) convertToMysqlTime(target *FieldType) (Datum, error) { tp := target.Tp fsp := mysql.DefaultFsp if target.Decimal != UnspecifiedLength { fsp = target.Decimal } var ret Datum switch d.k { case KindMysqlTime: t, err := d.GetMysqlTime().Convert(tp) if err != nil { ret.SetValue(t) return ret, errors.Trace(err) } t, err = t.RoundFrac(fsp) ret.SetValue(t) if err != nil { return ret, errors.Trace(err) } case KindMysqlDuration: t, err := d.GetMysqlDuration().ConvertToTime(tp) if err != nil { ret.SetValue(t) return ret, errors.Trace(err) } t, err = t.RoundFrac(fsp) ret.SetValue(t) if err != nil { return ret, errors.Trace(err) } case KindString, KindBytes: t, err := mysql.ParseTime(d.GetString(), tp, fsp) ret.SetValue(t) if err != nil { return ret, errors.Trace(err) } case KindInt64: t, err := mysql.ParseTimeFromNum(d.GetInt64(), tp, fsp) ret.SetValue(t) if err != nil { return ret, errors.Trace(err) } default: return invalidConv(d, tp) } return ret, nil }
func getTimeValue(ctx context.Context, v interface{}, tp byte, fsp int) (interface{}, error) { value := mysql.Time{ Type: tp, Fsp: fsp, } defaultTime, err := getSystemTimestamp(ctx) if err != nil { return nil, errors.Trace(err) } switch x := v.(type) { case string: if x == CurrentTimestamp { value.Time = defaultTime } else if x == ZeroTimestamp { value, _ = mysql.ParseTimeFromNum(0, tp, fsp) } else { value, err = mysql.ParseTime(x, tp, fsp) if err != nil { return nil, errors.Trace(err) } } case Value: x.Val = types.RawData(x.Val) switch xval := x.Val.(type) { case string: value, err = mysql.ParseTime(xval, tp, fsp) if err != nil { return nil, errors.Trace(err) } case int64: value, err = mysql.ParseTimeFromNum(int64(xval), tp, fsp) if err != nil { return nil, errors.Trace(err) } case nil: return nil, nil default: return nil, errors.Trace(errDefaultValue) } case *Ident: if x.Equal(CurrentTimeExpr) { return CurrentTimestamp, nil } return nil, errors.Trace(errDefaultValue) case *UnaryOperation: // support some expression, like `-1` m := map[interface{}]interface{}{} v := Eval(x, nil, m) ft := types.NewFieldType(mysql.TypeLonglong) xval, err := types.Convert(v, ft) if err != nil { return nil, errors.Trace(err) } value, err = mysql.ParseTimeFromNum(xval.(int64), tp, fsp) if err != nil { return nil, errors.Trace(err) } default: return nil, nil } return value, nil }
// Cast casts val to certain types and does not return error. func Cast(val interface{}, target *FieldType) (v interface{}) { tp := target.Tp switch tp { case mysql.TypeString: x, _ := ToString(val) // TODO: consider target.Charset/Collate x = truncateStr(x, target.Flen) if target.Charset == charset.CharsetBin { return []byte(x) } return x case mysql.TypeDuration: var dur mysql.Duration fsp := mysql.DefaultFsp if target.Decimal != UnspecifiedLength { fsp = target.Decimal } switch x := val.(type) { case mysql.Duration: dur, _ = x.RoundFrac(fsp) case mysql.Time: dur, _ = x.ConvertToDuration() dur, _ = dur.RoundFrac(fsp) case string: dur, _ = mysql.ParseDuration(x, fsp) case *DataItem: return Cast(x.Data, target) } return dur case mysql.TypeDatetime, mysql.TypeDate: fsp := mysql.DefaultFsp if target.Decimal != UnspecifiedLength { fsp = target.Decimal } var t mysql.Time t.Type = tp switch x := val.(type) { case mysql.Time: t, _ = x.Convert(tp) t, _ = t.RoundFrac(fsp) case mysql.Duration: t, _ = x.ConvertToTime(tp) t, _ = t.RoundFrac(fsp) case string: t, _ = mysql.ParseTime(x, tp, fsp) case int64: t, _ = mysql.ParseTimeFromNum(x, tp, fsp) case *DataItem: return Cast(x.Data, target) } return t case mysql.TypeLonglong: if mysql.HasUnsignedFlag(target.Flag) { v, _ = ToUint64(val) } else { v, _ = ToInt64(val) } return case mysql.TypeNewDecimal: x, _ := ToDecimal(val) if target.Decimal != UnspecifiedLength { x = x.Round(int32(target.Decimal)) } // TODO: check Flen return x default: panic("should never happen") } }
// Convert converts the val with type tp. func Convert(val interface{}, target *FieldType) (v interface{}, err error) { tp := target.Tp if val == nil { return nil, nil } vdi, ok := val.(*DataItem) if ok { return Convert(vdi.Data, target) } switch tp { // TODO: implement mysql types convert when "CAST() AS" syntax are supported. case mysql.TypeFloat: x, err := ToFloat64(val) if err != nil { return invConv(val, tp) } // For float and following double type, we will only truncate it for float(M, D) format. // If no D is set, we will handle it like origin float whether M is set or not. if target.Flen != UnspecifiedLength && target.Decimal != UnspecifiedLength { x, err = TruncateFloat(x, target.Flen, target.Decimal) if err != nil { return nil, errors.Trace(err) } } return float32(x), nil case mysql.TypeDouble: x, err := ToFloat64(val) if err != nil { return invConv(val, tp) } if target.Flen != UnspecifiedLength && target.Decimal != UnspecifiedLength { x, err = TruncateFloat(x, target.Flen, target.Decimal) if err != nil { return nil, errors.Trace(err) } } return float64(x), nil case mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob, mysql.TypeString, mysql.TypeVarchar, mysql.TypeVarString: x, err := ToString(val) if err != nil { return invConv(val, tp) } // TODO: consider target.Charset/Collate x = truncateStr(x, target.Flen) if target.Charset == charset.CharsetBin { return []byte(x), nil } return x, nil case mysql.TypeDuration: fsp := mysql.DefaultFsp if target.Decimal != UnspecifiedLength { fsp = target.Decimal } switch x := val.(type) { case mysql.Duration: return x.RoundFrac(fsp) case mysql.Time: t, err := x.ConvertToDuration() if err != nil { return nil, errors.Trace(err) } return t.RoundFrac(fsp) case string: return mysql.ParseDuration(x, fsp) default: return invConv(val, tp) } case mysql.TypeTimestamp, mysql.TypeDatetime, mysql.TypeDate: fsp := mysql.DefaultFsp if target.Decimal != UnspecifiedLength { fsp = target.Decimal } switch x := val.(type) { case mysql.Time: t, err := x.Convert(tp) if err != nil { return nil, errors.Trace(err) } return t.RoundFrac(fsp) case mysql.Duration: t, err := x.ConvertToTime(tp) if err != nil { return nil, errors.Trace(err) } return t.RoundFrac(fsp) case string: return mysql.ParseTime(x, tp, fsp) case int64: return mysql.ParseTimeFromNum(x, tp, fsp) default: return invConv(val, tp) } case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong: unsigned := mysql.HasUnsignedFlag(target.Flag) if unsigned { return convertToUint(val, target) } return convertToInt(val, target) case mysql.TypeBit: x, err := convertToUint(val, target) if err != nil { return x, errors.Trace(err) } // check bit boundary, if bit has n width, the boundary is // in [0, (1 << n) - 1] width := target.Flen if width == 0 || width == mysql.UnspecifiedBitWidth { width = mysql.MinBitWidth } maxValue := uint64(1)<<uint64(width) - 1 if x > maxValue { return maxValue, overflow(val, tp) } return mysql.Bit{Value: x, Width: width}, nil case mysql.TypeDecimal, mysql.TypeNewDecimal: x, err := ToDecimal(val) if err != nil { return invConv(val, tp) } if target.Decimal != UnspecifiedLength { x = x.Round(int32(target.Decimal)) } // TODO: check Flen return x, nil case mysql.TypeYear: var ( intVal int64 err error ) switch x := val.(type) { case string: intVal, err = StrToInt(x) case mysql.Time: return int64(x.Year()), nil case mysql.Duration: return int64(time.Now().Year()), nil default: intVal, err = ToInt64(x) } if err != nil { return invConv(val, tp) } y, err := mysql.AdjustYear(int(intVal)) if err != nil { return invConv(val, tp) } return int64(y), nil case mysql.TypeEnum: var ( e mysql.Enum err error ) switch x := val.(type) { case string: e, err = mysql.ParseEnumName(target.Elems, x) case []byte: e, err = mysql.ParseEnumName(target.Elems, string(x)) default: var number uint64 number, err = ToUint64(x) if err != nil { return nil, errors.Trace(err) } e, err = mysql.ParseEnumValue(target.Elems, number) } if err != nil { return invConv(val, tp) } return e, nil case mysql.TypeSet: var ( s mysql.Set err error ) switch x := val.(type) { case string: s, err = mysql.ParseSetName(target.Elems, x) case []byte: s, err = mysql.ParseSetName(target.Elems, string(x)) default: var number uint64 number, err = ToUint64(x) if err != nil { return nil, errors.Trace(err) } s, err = mysql.ParseSetValue(target.Elems, number) } if err != nil { return invConv(val, tp) } return s, nil case mysql.TypeNull: return nil, nil default: panic("should never happen") } }
func getTimeValue(ctx context.Context, v interface{}, tp byte, fsp int) (d types.Datum, err error) { value := mysql.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 = defaultTime } else if upperX == ZeroTimestamp { value, _ = mysql.ParseTimeFromNum(0, tp, fsp) } else { value, err = mysql.ParseTime(x, tp, fsp) if err != nil { return d, errors.Trace(err) } } case *ast.ValueExpr: switch x.Kind() { case types.KindString: value, err = mysql.ParseTime(x.GetString(), tp, fsp) if err != nil { return d, errors.Trace(err) } case types.KindInt64: value, err = mysql.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(ft) if err != nil { return d, errors.Trace(err) } value, err = mysql.ParseTimeFromNum(xval.GetInt64(), tp, fsp) if err != nil { return d, errors.Trace(err) } default: return d, nil } d.SetMysqlTime(value) return d, nil }