1
0
mirror of https://github.com/golang/go synced 2024-11-25 10:07:56 -07:00

exp/sql: add time.Time support

Fixes #2694

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5541057
This commit is contained in:
Brad Fitzpatrick 2012-01-13 15:45:05 -08:00
parent a08c1960dd
commit bf734d62d8
6 changed files with 38 additions and 5 deletions

View File

@ -8,8 +8,11 @@ import (
"fmt" "fmt"
"reflect" "reflect"
"testing" "testing"
"time"
) )
var someTime = time.Unix(123, 0)
type conversionTest struct { type conversionTest struct {
s, d interface{} // source and destination s, d interface{} // source and destination
@ -19,6 +22,7 @@ type conversionTest struct {
wantstr string wantstr string
wantf32 float32 wantf32 float32
wantf64 float64 wantf64 float64
wanttime time.Time
wantbool bool // used if d is of type *bool wantbool bool // used if d is of type *bool
wanterr string wanterr string
} }
@ -35,12 +39,14 @@ var (
scanbool bool scanbool bool
scanf32 float32 scanf32 float32
scanf64 float64 scanf64 float64
scantime time.Time
) )
var conversionTests = []conversionTest{ var conversionTests = []conversionTest{
// Exact conversions (destination pointer type matches source type) // Exact conversions (destination pointer type matches source type)
{s: "foo", d: &scanstr, wantstr: "foo"}, {s: "foo", d: &scanstr, wantstr: "foo"},
{s: 123, d: &scanint, wantint: 123}, {s: 123, d: &scanint, wantint: 123},
{s: someTime, d: &scantime, wanttime: someTime},
// To strings // To strings
{s: []byte("byteslice"), d: &scanstr, wantstr: "byteslice"}, {s: []byte("byteslice"), d: &scanstr, wantstr: "byteslice"},
@ -106,6 +112,10 @@ func float32Value(ptr interface{}) float32 {
return *(ptr.(*float32)) return *(ptr.(*float32))
} }
func timeValue(ptr interface{}) time.Time {
return *(ptr.(*time.Time))
}
func TestConversions(t *testing.T) { func TestConversions(t *testing.T) {
for n, ct := range conversionTests { for n, ct := range conversionTests {
err := convertAssign(ct.d, ct.s) err := convertAssign(ct.d, ct.s)
@ -138,6 +148,9 @@ func TestConversions(t *testing.T) {
if bp, boolTest := ct.d.(*bool); boolTest && *bp != ct.wantbool && ct.wanterr == "" { if bp, boolTest := ct.d.(*bool); boolTest && *bp != ct.wantbool && ct.wanterr == "" {
errf("want bool %v, got %v", ct.wantbool, *bp) errf("want bool %v, got %v", ct.wantbool, *bp)
} }
if !ct.wanttime.IsZero() && !ct.wanttime.Equal(timeValue(ct.d)) {
errf("want time %v, got %v", ct.wanttime, timeValue(ct.d))
}
} }
} }

View File

@ -16,6 +16,7 @@
// nil // nil
// []byte // []byte
// string [*] everywhere except from Rows.Next. // string [*] everywhere except from Rows.Next.
// time.Time
// //
package driver package driver

View File

@ -8,6 +8,7 @@ import (
"fmt" "fmt"
"reflect" "reflect"
"strconv" "strconv"
"time"
) )
// ValueConverter is the interface providing the ConvertValue method. // ValueConverter is the interface providing the ConvertValue method.
@ -143,9 +144,10 @@ func (stringType) ConvertValue(v interface{}) (interface{}, error) {
// bool // bool
// nil // nil
// []byte // []byte
// time.Time
// string // string
// //
// This is the ame list as IsScanSubsetType, with the addition of // This is the same list as IsScanSubsetType, with the addition of
// string. // string.
func IsParameterSubsetType(v interface{}) bool { func IsParameterSubsetType(v interface{}) bool {
if IsScanSubsetType(v) { if IsScanSubsetType(v) {
@ -165,6 +167,7 @@ func IsParameterSubsetType(v interface{}) bool {
// bool // bool
// nil // nil
// []byte // []byte
// time.Time
// //
// This is the same list as IsParameterSubsetType, without string. // This is the same list as IsParameterSubsetType, without string.
func IsScanSubsetType(v interface{}) bool { func IsScanSubsetType(v interface{}) bool {
@ -172,7 +175,7 @@ func IsScanSubsetType(v interface{}) bool {
return true return true
} }
switch v.(type) { switch v.(type) {
case int64, float64, []byte, bool: case int64, float64, []byte, bool, time.Time:
return true return true
} }
return false return false

View File

@ -7,6 +7,7 @@ package driver
import ( import (
"reflect" "reflect"
"testing" "testing"
"time"
) )
type valueConverterTest struct { type valueConverterTest struct {
@ -16,6 +17,8 @@ type valueConverterTest struct {
err string err string
} }
var now = time.Now()
var valueConverterTests = []valueConverterTest{ var valueConverterTests = []valueConverterTest{
{Bool, "true", true, ""}, {Bool, "true", true, ""},
{Bool, "True", true, ""}, {Bool, "True", true, ""},
@ -33,6 +36,7 @@ var valueConverterTests = []valueConverterTest{
{Bool, uint16(0), false, ""}, {Bool, uint16(0), false, ""},
{c: Bool, in: "foo", err: "sql/driver: couldn't convert \"foo\" into type bool"}, {c: Bool, in: "foo", err: "sql/driver: couldn't convert \"foo\" into type bool"},
{c: Bool, in: 2, err: "sql/driver: couldn't convert 2 into type bool"}, {c: Bool, in: 2, err: "sql/driver: couldn't convert 2 into type bool"},
{DefaultParameterConverter, now, now, ""},
} }
func TestValueConverters(t *testing.T) { func TestValueConverters(t *testing.T) {

View File

@ -12,6 +12,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"time"
"exp/sql/driver" "exp/sql/driver"
) )
@ -220,7 +221,7 @@ func (c *fakeConn) Close() error {
func checkSubsetTypes(args []interface{}) error { func checkSubsetTypes(args []interface{}) error {
for n, arg := range args { for n, arg := range args {
switch arg.(type) { switch arg.(type) {
case int64, float64, bool, nil, []byte, string: case int64, float64, bool, nil, []byte, string, time.Time:
default: default:
return fmt.Errorf("fakedb_test: invalid argument #%d: %v, type %T", n+1, arg, arg) return fmt.Errorf("fakedb_test: invalid argument #%d: %v, type %T", n+1, arg, arg)
} }
@ -589,6 +590,8 @@ func converterForType(typ string) driver.ValueConverter {
return driver.Int32 return driver.Int32
case "string": case "string":
return driver.String return driver.String
case "datetime":
return driver.DefaultParameterConverter
} }
panic("invalid fakedb column type of " + typ) panic("invalid fakedb column type of " + typ)
} }

View File

@ -8,10 +8,13 @@ import (
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
"time"
) )
const fakeDBName = "foo" const fakeDBName = "foo"
var chrisBirthday = time.Unix(123456789, 0)
func newTestDB(t *testing.T, name string) *DB { func newTestDB(t *testing.T, name string) *DB {
db, err := Open("test", fakeDBName) db, err := Open("test", fakeDBName)
if err != nil { if err != nil {
@ -21,10 +24,10 @@ func newTestDB(t *testing.T, name string) *DB {
t.Fatalf("exec wipe: %v", err) t.Fatalf("exec wipe: %v", err)
} }
if name == "people" { if name == "people" {
exec(t, db, "CREATE|people|name=string,age=int32,photo=blob,dead=bool") exec(t, db, "CREATE|people|name=string,age=int32,photo=blob,dead=bool,bdate=datetime")
exec(t, db, "INSERT|people|name=Alice,age=?,photo=APHOTO", 1) exec(t, db, "INSERT|people|name=Alice,age=?,photo=APHOTO", 1)
exec(t, db, "INSERT|people|name=Bob,age=?,photo=BPHOTO", 2) exec(t, db, "INSERT|people|name=Bob,age=?,photo=BPHOTO", 2)
exec(t, db, "INSERT|people|name=Chris,age=?,photo=CPHOTO", 3) exec(t, db, "INSERT|people|name=Chris,age=?,photo=CPHOTO,bdate=?", 3, chrisBirthday)
} }
return db return db
} }
@ -105,12 +108,18 @@ func TestQueryRow(t *testing.T) {
defer closeDB(t, db) defer closeDB(t, db)
var name string var name string
var age int var age int
var birthday time.Time
err := db.QueryRow("SELECT|people|age,name|age=?", 3).Scan(&age) err := db.QueryRow("SELECT|people|age,name|age=?", 3).Scan(&age)
if err == nil || !strings.Contains(err.Error(), "expected 2 destination arguments") { if err == nil || !strings.Contains(err.Error(), "expected 2 destination arguments") {
t.Errorf("expected error from wrong number of arguments; actually got: %v", err) t.Errorf("expected error from wrong number of arguments; actually got: %v", err)
} }
err = db.QueryRow("SELECT|people|bdate|age=?", 3).Scan(&birthday)
if err != nil || !birthday.Equal(chrisBirthday) {
t.Errorf("chris birthday = %v, err = %v; want %v", birthday, err, chrisBirthday)
}
err = db.QueryRow("SELECT|people|age,name|age=?", 2).Scan(&age, &name) err = db.QueryRow("SELECT|people|age,name|age=?", 2).Scan(&age, &name)
if err != nil { if err != nil {
t.Fatalf("age QueryRow+Scan: %v", err) t.Fatalf("age QueryRow+Scan: %v", err)