mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
database/sql: support scanning into user defined string types
User defined numeric types such as "type Int int64" have been able to be scanned into without a custom scanner by using the reflect scan code path used to convert between various numeric types. Add in a path for string types for symmetry and least surprise. Fixes #18101 Change-Id: I00553bcf021ffe6d95047eca0067ee94b54ff501 Reviewed-on: https://go-review.googlesource.com/39031 Run-TryBot: Daniel Theophanes <kardianos@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
bfd8093c96
commit
5a45a157f2
3 changed files with 42 additions and 3 deletions
|
|
@ -270,6 +270,11 @@ func convertAssign(dest, src interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The following conversions use a string value as an intermediate representation
|
||||||
|
// to convert between various numeric types.
|
||||||
|
//
|
||||||
|
// This also allows scanning into user defined types such as "type Int int64".
|
||||||
|
// For symmetry, also check for string destination types.
|
||||||
switch dv.Kind() {
|
switch dv.Kind() {
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
if src == nil {
|
if src == nil {
|
||||||
|
|
@ -306,6 +311,15 @@ func convertAssign(dest, src interface{}) error {
|
||||||
}
|
}
|
||||||
dv.SetFloat(f64)
|
dv.SetFloat(f64)
|
||||||
return nil
|
return nil
|
||||||
|
case reflect.String:
|
||||||
|
switch v := src.(type) {
|
||||||
|
case string:
|
||||||
|
dv.SetString(v)
|
||||||
|
return nil
|
||||||
|
case []byte:
|
||||||
|
dv.SetString(string(v))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("unsupported Scan, storing driver.Value type %T into type %T", src, dest)
|
return fmt.Errorf("unsupported Scan, storing driver.Value type %T into type %T", src, dest)
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,11 @@ import (
|
||||||
var someTime = time.Unix(123, 0)
|
var someTime = time.Unix(123, 0)
|
||||||
var answer int64 = 42
|
var answer int64 = 42
|
||||||
|
|
||||||
type userDefined float64
|
type (
|
||||||
|
userDefined float64
|
||||||
type userDefinedSlice []int
|
userDefinedSlice []int
|
||||||
|
userDefinedString string
|
||||||
|
)
|
||||||
|
|
||||||
type conversionTest struct {
|
type conversionTest struct {
|
||||||
s, d interface{} // source and destination
|
s, d interface{} // source and destination
|
||||||
|
|
@ -39,6 +41,7 @@ type conversionTest struct {
|
||||||
wantptr *int64 // if non-nil, *d's pointed value must be equal to *wantptr
|
wantptr *int64 // if non-nil, *d's pointed value must be equal to *wantptr
|
||||||
wantnil bool // if true, *d must be *int64(nil)
|
wantnil bool // if true, *d must be *int64(nil)
|
||||||
wantusrdef userDefined
|
wantusrdef userDefined
|
||||||
|
wantusrstr userDefinedString
|
||||||
}
|
}
|
||||||
|
|
||||||
// Target variables for scanning into.
|
// Target variables for scanning into.
|
||||||
|
|
@ -171,6 +174,7 @@ var conversionTests = []conversionTest{
|
||||||
{s: int64(123), d: new(userDefined), wantusrdef: 123},
|
{s: int64(123), d: new(userDefined), wantusrdef: 123},
|
||||||
{s: "1.5", d: new(userDefined), wantusrdef: 1.5},
|
{s: "1.5", d: new(userDefined), wantusrdef: 1.5},
|
||||||
{s: []byte{1, 2, 3}, d: new(userDefinedSlice), wanterr: `unsupported Scan, storing driver.Value type []uint8 into type *sql.userDefinedSlice`},
|
{s: []byte{1, 2, 3}, d: new(userDefinedSlice), wanterr: `unsupported Scan, storing driver.Value type []uint8 into type *sql.userDefinedSlice`},
|
||||||
|
{s: "str", d: new(userDefinedString), wantusrstr: "str"},
|
||||||
|
|
||||||
// Other errors
|
// Other errors
|
||||||
{s: complex(1, 2), d: &scanstr, wanterr: `unsupported Scan, storing driver.Value type complex128 into type *string`},
|
{s: complex(1, 2), d: &scanstr, wanterr: `unsupported Scan, storing driver.Value type complex128 into type *string`},
|
||||||
|
|
@ -260,6 +264,9 @@ func TestConversions(t *testing.T) {
|
||||||
if ct.wantusrdef != 0 && ct.wantusrdef != *ct.d.(*userDefined) {
|
if ct.wantusrdef != 0 && ct.wantusrdef != *ct.d.(*userDefined) {
|
||||||
errf("want userDefined %f, got %f", ct.wantusrdef, *ct.d.(*userDefined))
|
errf("want userDefined %f, got %f", ct.wantusrdef, *ct.d.(*userDefined))
|
||||||
}
|
}
|
||||||
|
if len(ct.wantusrstr) != 0 && ct.wantusrstr != *ct.d.(*userDefinedString) {
|
||||||
|
errf("want userDefined %q, got %q", ct.wantusrstr, *ct.d.(*userDefinedString))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3155,6 +3155,24 @@ func TestPing(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Issue 18101.
|
||||||
|
func TestTypedString(t *testing.T) {
|
||||||
|
db := newTestDB(t, "people")
|
||||||
|
defer closeDB(t, db)
|
||||||
|
|
||||||
|
type Str string
|
||||||
|
var scanned Str
|
||||||
|
|
||||||
|
err := db.QueryRow("SELECT|people|name|name=?", "Alice").Scan(&scanned)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
expected := Str("Alice")
|
||||||
|
if scanned != expected {
|
||||||
|
t.Errorf("expected %+v, got %+v", expected, scanned)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkConcurrentDBExec(b *testing.B) {
|
func BenchmarkConcurrentDBExec(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
ct := new(concurrentDBExecTest)
|
ct := new(concurrentDBExecTest)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue