diff --git a/src/database/sql/convert.go b/src/database/sql/convert.go index ea2f377810eac9..630a585ab223dc 100644 --- a/src/database/sql/convert.go +++ b/src/database/sql/convert.go @@ -270,6 +270,11 @@ func convertAssign(dest, src interface{}) error { 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() { case reflect.Ptr: if src == nil { @@ -306,6 +311,15 @@ func convertAssign(dest, src interface{}) error { } dv.SetFloat(f64) 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) diff --git a/src/database/sql/convert_test.go b/src/database/sql/convert_test.go index 4dfab1f6bef6a8..853a12ce959f38 100644 --- a/src/database/sql/convert_test.go +++ b/src/database/sql/convert_test.go @@ -17,9 +17,11 @@ import ( var someTime = time.Unix(123, 0) var answer int64 = 42 -type userDefined float64 - -type userDefinedSlice []int +type ( + userDefined float64 + userDefinedSlice []int + userDefinedString string +) type conversionTest struct { 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 wantnil bool // if true, *d must be *int64(nil) wantusrdef userDefined + wantusrstr userDefinedString } // Target variables for scanning into. @@ -171,6 +174,7 @@ var conversionTests = []conversionTest{ {s: int64(123), d: new(userDefined), wantusrdef: 123}, {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: "str", d: new(userDefinedString), wantusrstr: "str"}, // Other errors {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) { 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)) + } } } diff --git a/src/database/sql/sql_test.go b/src/database/sql/sql_test.go index 4c1adf51b61ed9..f511aa4ac3553c 100644 --- a/src/database/sql/sql_test.go +++ b/src/database/sql/sql_test.go @@ -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) { b.ReportAllocs() ct := new(concurrentDBExecTest)