func floatOrDecimalBuiltin1(f func(float64) (Datum, error)) []builtin { return []builtin{ { types: argTypes{floatType}, returnType: typeFloat, fn: func(_ EvalContext, args DTuple) (Datum, error) { return f(float64(args[0].(DFloat))) }, }, { types: argTypes{decimalType}, returnType: typeDecimal, fn: func(_ EvalContext, args DTuple) (Datum, error) { dec := args[0].(*DDecimal) v, err := decimal.Float64FromDec(&dec.Dec) if err != nil { return nil, err } r, err := f(v) if err != nil { return r, err } rf := float64(r.(DFloat)) if math.IsNaN(rf) || math.IsInf(rf, 0) { // TODO(nvanbenschoten) NaN semmantics should be introduced // into the decimal library to support it here. return nil, fmt.Errorf("decimal does not support NaN") } dd := &DDecimal{} decimal.SetFromFloat(&dd.Dec, rf) return dd, nil }, }, } }
func ddecimal(f float64) copyableExpr { return func() Expr { dd := &DDecimal{} decimal.SetFromFloat(&dd.Dec, f) return dd } }
func makeDecimalTestDatum(count int) []parser.Datum { rng, _ := randutil.NewPseudoRand() vals := make([]parser.Datum, count) for i := range vals { dd := &parser.DDecimal{} decimal.SetFromFloat(&dd.Dec, rng.Float64()) vals[i] = dd } return vals }
// Eval implements the Expr interface. func (expr *CastExpr) Eval(ctx EvalContext) (Datum, error) { d, err := expr.Expr.Eval(ctx) if err != nil { return nil, err } // NULL cast to anything is NULL. if d == DNull { return d, nil } switch expr.Type.(type) { case *BoolType: switch v := d.(type) { case DBool: return d, nil case DInt: return DBool(v != 0), nil case DFloat: return DBool(v != 0), nil case *DDecimal: return DBool(v.Sign() != 0), nil case DString: // TODO(pmattis): strconv.ParseBool is more permissive than the SQL // spec. Is that ok? b, err := strconv.ParseBool(string(v)) if err != nil { return nil, err } return DBool(b), nil } case *IntType: switch v := d.(type) { case DBool: if v { return DInt(1), nil } return DInt(0), nil case DInt: return d, nil case DFloat: f, err := round(float64(v), 0) if err != nil { panic(fmt.Sprintf("round should never fail with digits hardcoded to 0: %s", err)) } return DInt(f.(DFloat)), nil case *DDecimal: dec := new(inf.Dec) dec.Round(&v.Dec, 0, inf.RoundHalfUp) i, ok := dec.Unscaled() if !ok { return nil, errIntOutOfRange } return DInt(i), nil case DString: i, err := strconv.ParseInt(string(v), 0, 64) if err != nil { return nil, err } return DInt(i), nil } case *FloatType: switch v := d.(type) { case DBool: if v { return DFloat(1), nil } return DFloat(0), nil case DInt: return DFloat(v), nil case DFloat: return d, nil case *DDecimal: f, err := decimal.Float64FromDec(&v.Dec) if err != nil { return nil, errFloatOutOfRange } return DFloat(f), nil case DString: f, err := strconv.ParseFloat(string(v), 64) if err != nil { return nil, err } return DFloat(f), nil } case *DecimalType: dd := &DDecimal{} switch v := d.(type) { case DBool: if v { dd.SetUnscaled(1) } return dd, nil case DInt: dd.SetUnscaled(int64(v)) return dd, nil case DFloat: decimal.SetFromFloat(&dd.Dec, float64(v)) return dd, nil case *DDecimal: return d, nil case DString: if _, ok := dd.SetString(string(v)); !ok { return nil, fmt.Errorf("could not parse string %q as decimal", v) } return dd, nil } case *StringType: var s DString switch t := d.(type) { case DBool, DInt, DFloat, *DDecimal, dNull: s = DString(d.String()) case DString: s = t case DBytes: if !utf8.ValidString(string(t)) { return nil, fmt.Errorf("invalid utf8: %q", string(t)) } s = DString(t) } if c, ok := expr.Type.(*StringType); ok { // If the CHAR type specifies a limit we truncate to that limit: // 'hello'::CHAR(2) -> 'he' if c.N > 0 && c.N < len(s) { s = s[:c.N] } } return s, nil case *BytesType: switch t := d.(type) { case DString: return DBytes(t), nil case DBytes: return d, nil } case *DateType: switch d := d.(type) { case DString: return ParseDate(d) case DTimestamp: return ctx.makeDDate(d.Time) } case *TimestampType: switch d := d.(type) { case DString: return ctx.ParseTimestamp(d) case DDate: loc, err := ctx.GetLocation() if err != nil { return nil, err } year, month, day := time.Unix(int64(d)*secondsInDay, 0).UTC().Date() return DTimestamp{Time: time.Date(year, month, day, 0, 0, 0, 0, loc)}, nil } case *IntervalType: switch d.(type) { case DString: // We use the Golang format for specifying duration. // TODO(vivek): we might consider using the postgres format as well. d, err := time.ParseDuration(string(d.(DString))) return DInterval{Duration: d}, err case DInt: // An integer duration represents a duration in nanoseconds. return DInterval{Duration: time.Duration(d.(DInt))}, nil } } return nil, fmt.Errorf("invalid cast: %s -> %s", d.Type(), expr.Type) }
func TestNumericConstantAvailableTypes(t *testing.T) { wantInt := numValAvailIntFloatDec wantFloatButCanBeInt := numValAvailFloatIntDec wantFloat := numValAvailFloatDec testCases := []struct { str string avail []Datum }{ {"1", wantInt}, {"0", wantInt}, {"-1", wantInt}, {"9223372036854775807", wantInt}, {"1.0", wantFloatButCanBeInt}, {"-1234.0000", wantFloatButCanBeInt}, {"1e10", wantFloatButCanBeInt}, {"1E10", wantFloatButCanBeInt}, {"1.1", wantFloat}, {"1e-10", wantFloat}, {"1E-10", wantFloat}, {"-1231.131", wantFloat}, {"876543234567898765436787654321", wantFloat}, } for i, test := range testCases { tok := token.INT if strings.ContainsAny(test.str, ".eE") { tok = token.FLOAT } val := constant.MakeFromLiteral(test.str, tok, 0) if val.Kind() == constant.Unknown { t.Fatalf("%d: could not parse value string %q", i, test.str) } // Check available types. c := &NumVal{Value: val} avail := c.AvailableTypes() if !reflect.DeepEqual(avail, test.avail) { t.Errorf("%d: expected the available type set %v for %v, found %v", i, test.avail, c.Value.ExactString(), avail) } // Make sure it can be resolved as each of those types. for _, availType := range avail { if res, err := c.ResolveAsType(availType); err != nil { t.Errorf("%d: expected resolving %v as available type %s would succeed, found %v", i, c.Value.ExactString(), availType.Type(), err) } else { resErr := func(parsed, resolved interface{}) { t.Errorf("%d: expected resolving %v as available type %s would produce a Datum with the value %v, found %v", i, c, availType.Type(), parsed, resolved) } switch typ := res.(type) { case *DInt: var i int64 var err error if tok == token.INT { if i, err = strconv.ParseInt(test.str, 10, 64); err != nil { t.Fatal(err) } } else { var f float64 if f, err = strconv.ParseFloat(test.str, 64); err != nil { t.Fatal(err) } i = int64(f) } if resI := int64(*typ); i != resI { resErr(i, resI) } case *DFloat: f, err := strconv.ParseFloat(test.str, 64) if err != nil { t.Fatal(err) } if resF := float64(*typ); f != resF { resErr(f, resF) } case *DDecimal: d := new(inf.Dec) if !strings.ContainsAny(test.str, "eE") { if _, ok := d.SetString(test.str); !ok { t.Fatal(fmt.Sprintf("could not set %q on decimal", test.str)) } } else { f, err := strconv.ParseFloat(test.str, 64) if err != nil { t.Fatal(err) } decimal.SetFromFloat(d, f) } if resD := &typ.Dec; d.Cmp(resD) != 0 { resErr(d, resD) } } } } } }