From 9c060b8d60f14d930e5eadd7c9968ee2ba4f4131 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Mon, 6 Feb 2012 10:06:22 -0800 Subject: [PATCH] database/sql: permit scanning into interface{} See thread http://goo.gl/7zzzU for background. R=rsc CC=golang-dev https://golang.org/cl/5624051 --- src/pkg/database/sql/convert.go | 8 ++++++ src/pkg/database/sql/convert_test.go | 38 ++++++++++++++++++++++------ src/pkg/database/sql/sql.go | 4 +++ 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/pkg/database/sql/convert.go b/src/pkg/database/sql/convert.go index e80420e5bb..4924ac14e4 100644 --- a/src/pkg/database/sql/convert.go +++ b/src/pkg/database/sql/convert.go @@ -49,6 +49,11 @@ func convertAssign(dest, src interface{}) error { case *string: *d = string(s) return nil + case *interface{}: + bcopy := make([]byte, len(s)) + copy(bcopy, s) + *d = bcopy + return nil case *[]byte: *d = s return nil @@ -80,6 +85,9 @@ func convertAssign(dest, src interface{}) error { *d = bv.(bool) } return err + case *interface{}: + *d = src + return nil } if scanner, ok := dest.(ScannerInto); ok { diff --git a/src/pkg/database/sql/convert_test.go b/src/pkg/database/sql/convert_test.go index b188864f62..34ee93987f 100644 --- a/src/pkg/database/sql/convert_test.go +++ b/src/pkg/database/sql/convert_test.go @@ -18,14 +18,15 @@ type conversionTest struct { s, d interface{} // source and destination // following are used if they're non-zero - wantint int64 - wantuint uint64 - wantstr string - wantf32 float32 - wantf64 float64 - wanttime time.Time - wantbool bool // used if d is of type *bool - wanterr string + wantint int64 + wantuint uint64 + wantstr string + wantf32 float32 + wantf64 float64 + wanttime time.Time + wantbool bool // used if d is of type *bool + wanterr string + wantiface interface{} } // Target variables for scanning into. @@ -41,6 +42,7 @@ var ( scanf32 float32 scanf64 float64 scantime time.Time + scaniface interface{} ) var conversionTests = []conversionTest{ @@ -95,6 +97,14 @@ var conversionTests = []conversionTest{ {s: float64(1.5), d: &scanf32, wantf32: float32(1.5)}, {s: "1.5", d: &scanf32, wantf32: float32(1.5)}, {s: "1.5", d: &scanf64, wantf64: float64(1.5)}, + + // To interface{} + {s: float64(1.5), d: &scaniface, wantiface: float64(1.5)}, + {s: int64(1), d: &scaniface, wantiface: int64(1)}, + {s: "str", d: &scaniface, wantiface: "str"}, + {s: []byte("byteslice"), d: &scaniface, wantiface: []byte("byteslice")}, + {s: true, d: &scaniface, wantiface: true}, + {s: nil, d: &scaniface}, } func intValue(intptr interface{}) int64 { @@ -152,6 +162,18 @@ func TestConversions(t *testing.T) { if !ct.wanttime.IsZero() && !ct.wanttime.Equal(timeValue(ct.d)) { errf("want time %v, got %v", ct.wanttime, timeValue(ct.d)) } + if ifptr, ok := ct.d.(*interface{}); ok { + if !reflect.DeepEqual(ct.wantiface, scaniface) { + errf("want interface %#v, got %#v", ct.wantiface, scaniface) + continue + } + if srcBytes, ok := ct.s.([]byte); ok { + dstBytes := (*ifptr).([]byte) + if &dstBytes[0] == &srcBytes[0] { + errf("copy into interface{} didn't copy []byte data") + } + } + } } } diff --git a/src/pkg/database/sql/sql.go b/src/pkg/database/sql/sql.go index 34a7652105..436d4953ec 100644 --- a/src/pkg/database/sql/sql.go +++ b/src/pkg/database/sql/sql.go @@ -880,6 +880,10 @@ func (rs *Rows) Columns() ([]string, error) { // be modified and held indefinitely. The copy can be avoided by using // an argument of type *RawBytes instead; see the documentation for // RawBytes for restrictions on its use. +// +// If an argument has type *interface{}, Scan copies the value +// provided by the underlying driver without conversion. If the value +// is of type []byte, a copy is made and the caller owns the result. func (rs *Rows) Scan(dest ...interface{}) error { if rs.closed { return errors.New("sql: Rows closed") -- 2.48.1