fix(10.1-03): fill numeric model fields from JSON numbers
- lagoon.Fill converts a json.Number (from a UseNumber decoder, as cabana's save path uses) into integer, unsigned and float fields; a fraction or an overflow is an error - before this, saving a type: number field into an *int column was a 500 - README documents the conversion
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
@@ -159,12 +160,49 @@ func convertValue(src reflect.Value, destType reflect.Type) (reflect.Value, erro
|
||||
if src.Type().AssignableTo(destType) {
|
||||
return src, nil
|
||||
}
|
||||
if n, ok := src.Interface().(json.Number); ok {
|
||||
if out, handled, err := convertNumber(n, destType); handled {
|
||||
return out, err
|
||||
}
|
||||
}
|
||||
if src.Type().ConvertibleTo(destType) {
|
||||
return src.Convert(destType), nil
|
||||
}
|
||||
return reflect.Value{}, fmt.Errorf("cannot assign %s to %s", src.Type(), destType)
|
||||
}
|
||||
|
||||
// convertNumber parses a json.Number, which a request decoder using
|
||||
// UseNumber produces, into an integer, unsigned or float field. A value that
|
||||
// does not parse as that kind or overflows it is an error. handled is false
|
||||
// for any other destination kind, so a string field still takes the number's
|
||||
// text through the ordinary conversion.
|
||||
func convertNumber(n json.Number, destType reflect.Type) (reflect.Value, bool, error) {
|
||||
out := reflect.New(destType).Elem()
|
||||
switch destType.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
i, err := strconv.ParseInt(n.String(), 10, 64)
|
||||
if err != nil || out.OverflowInt(i) {
|
||||
return reflect.Value{}, true, fmt.Errorf("cannot assign number %s to %s", n, destType)
|
||||
}
|
||||
out.SetInt(i)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
u, err := strconv.ParseUint(n.String(), 10, 64)
|
||||
if err != nil || out.OverflowUint(u) {
|
||||
return reflect.Value{}, true, fmt.Errorf("cannot assign number %s to %s", n, destType)
|
||||
}
|
||||
out.SetUint(u)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
f, err := strconv.ParseFloat(n.String(), 64)
|
||||
if err != nil || out.OverflowFloat(f) {
|
||||
return reflect.Value{}, true, fmt.Errorf("cannot assign number %s to %s", n, destType)
|
||||
}
|
||||
out.SetFloat(f)
|
||||
default:
|
||||
return reflect.Value{}, false, nil
|
||||
}
|
||||
return out, true, nil
|
||||
}
|
||||
|
||||
// encryptedFromRequest treats request input for an Encrypted column as
|
||||
// plaintext. It never falls through to Encrypted.Scan: Scan decrypts, so a
|
||||
// write path that scanned request input would reject real secrets and accept
|
||||
|
||||
Reference in New Issue
Block a user