diff options
Diffstat (limited to 'src/pkg/database/sql')
| -rw-r--r-- | src/pkg/database/sql/convert.go | 50 | ||||
| -rw-r--r-- | src/pkg/database/sql/convert_test.go | 48 |
2 files changed, 85 insertions, 13 deletions
diff --git a/src/pkg/database/sql/convert.go b/src/pkg/database/sql/convert.go index c04adde1fc..c0b38a2494 100644 --- a/src/pkg/database/sql/convert.go +++ b/src/pkg/database/sql/convert.go @@ -160,27 +160,19 @@ func convertAssign(dest, src interface{}) error { reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64: - *d = fmt.Sprintf("%v", src) + *d = asString(src) return nil } case *[]byte: sv = reflect.ValueOf(src) - switch sv.Kind() { - case reflect.Bool, - reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, - reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, - reflect.Float32, reflect.Float64: - *d = []byte(fmt.Sprintf("%v", src)) + if b, ok := asBytes(nil, sv); ok { + *d = b return nil } case *RawBytes: sv = reflect.ValueOf(src) - switch sv.Kind() { - case reflect.Bool, - reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, - reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, - reflect.Float32, reflect.Float64: - *d = RawBytes(fmt.Sprintf("%v", src)) + if b, ok := asBytes([]byte(*d)[:0], sv); ok { + *d = RawBytes(b) return nil } case *bool: @@ -271,5 +263,37 @@ func asString(src interface{}) string { case []byte: return string(v) } + rv := reflect.ValueOf(src) + switch rv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return strconv.FormatInt(rv.Int(), 10) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return strconv.FormatUint(rv.Uint(), 10) + case reflect.Float64: + return strconv.FormatFloat(rv.Float(), 'g', -1, 64) + case reflect.Float32: + return strconv.FormatFloat(rv.Float(), 'g', -1, 32) + case reflect.Bool: + return strconv.FormatBool(rv.Bool()) + } return fmt.Sprintf("%v", src) } + +func asBytes(buf []byte, rv reflect.Value) (b []byte, ok bool) { + switch rv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return strconv.AppendInt(buf, rv.Int(), 10), true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return strconv.AppendUint(buf, rv.Uint(), 10), true + case reflect.Float32: + return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 32), true + case reflect.Float64: + return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 64), true + case reflect.Bool: + return strconv.AppendBool(buf, rv.Bool()), true + case reflect.String: + s := rv.String() + return append(buf, s...), true + } + return +} diff --git a/src/pkg/database/sql/convert_test.go b/src/pkg/database/sql/convert_test.go index a39c2c54fb..aa0b6f116a 100644 --- a/src/pkg/database/sql/convert_test.go +++ b/src/pkg/database/sql/convert_test.go @@ -279,3 +279,51 @@ func TestValueConverters(t *testing.T) { } } } + +// Tests that assigning to RawBytes doesn't allocate (and also works). +func TestRawBytesAllocs(t *testing.T) { + buf := make(RawBytes, 10) + test := func(name string, in interface{}, want string) { + if err := convertAssign(&buf, in); err != nil { + t.Fatalf("%s: convertAssign = %v", name, err) + } + match := len(buf) == len(want) + if match { + for i, b := range buf { + if want[i] != b { + match = false + break + } + } + } + if !match { + t.Fatalf("%s: got %q (len %d); want %q (len %d)", name, buf, len(buf), want, len(want)) + } + } + n := testing.AllocsPerRun(100, func() { + test("uint64", uint64(12345678), "12345678") + test("uint32", uint32(1234), "1234") + test("uint16", uint16(12), "12") + test("uint8", uint8(1), "1") + test("uint", uint(123), "123") + test("int", int(123), "123") + test("int8", int8(1), "1") + test("int16", int16(12), "12") + test("int32", int32(1234), "1234") + test("int64", int64(12345678), "12345678") + test("float32", float32(1.5), "1.5") + test("float64", float64(64), "64") + test("bool", false, "false") + }) + if n > 0.5 { + t.Fatalf("allocs = %v; want 0", n) + } + + // This one involves a convT2E allocation, string -> interface{} + n = testing.AllocsPerRun(100, func() { + test("string", "foo", "foo") + }) + if n > 1.5 { + t.Fatalf("allocs = %v; want max 1", n) + } +} |
