aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrad Fitzpatrick <bradfitz@golang.org>2012-02-06 10:06:22 -0800
committerBrad Fitzpatrick <bradfitz@golang.org>2012-02-06 10:06:22 -0800
commit9c060b8d60f14d930e5eadd7c9968ee2ba4f4131 (patch)
tree64c82ef25c9a16460d64a64f60d835dbcff93044
parent6392b43a1583f5ccf5a3f7c38f096e8dd5403b0d (diff)
downloadgo-9c060b8d60f14d930e5eadd7c9968ee2ba4f4131.tar.gz
go-9c060b8d60f14d930e5eadd7c9968ee2ba4f4131.zip
database/sql: permit scanning into interface{}
See thread http://goo.gl/7zzzU for background. R=rsc CC=golang-dev https://golang.org/cl/5624051
-rw-r--r--src/pkg/database/sql/convert.go8
-rw-r--r--src/pkg/database/sql/convert_test.go38
-rw-r--r--src/pkg/database/sql/sql.go4
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")