diff options
| author | Brad Fitzpatrick <bradfitz@golang.org> | 2016-10-11 08:34:58 -0700 |
|---|---|---|
| committer | Brad Fitzpatrick <bradfitz@golang.org> | 2016-10-17 15:26:25 +0000 |
| commit | 0ce1d79a6a771f7449ec493b993ed2a720917870 (patch) | |
| tree | fc1ef0ac4bd678c24236278e40ff8964a98d4c2f /src/database/sql/convert_test.go | |
| parent | 237d7e34bc7579ec499a029191c33106dc5476a1 (diff) | |
| download | go-0ce1d79a6a771f7449ec493b993ed2a720917870.tar.xz | |
database/sql: accept nil pointers to Valuers implemented on value receivers
The driver.Valuer interface lets types map their Go representation to
a suitable database/sql/driver.Value.
If a user defines the Value method with a value receiver, such as:
type MyStr string
func (s MyStr) Value() (driver.Value, error) {
return strings.ToUpper(string(s)), nil
}
Then they can't use (*MyStr)(nil) as an argument to an SQL call via
database/sql, because *MyStr also implements driver.Value, but via a
compiler-generated wrapper which checks whether the pointer is nil and
panics if so.
We now accept (*MyStr)(nil) and map it to "nil" (an SQL "NULL")
if the Valuer method is implemented on MyStr instead of *MyStr.
If a user implements the driver.Value interface with a pointer
receiver, they retain full control of what nil means:
type MyStr string
func (s *MyStr) Value() (driver.Value, error) {
if s == nil {
return "missing MyStr", nil
}
return strings.ToUpper(string(*s)), nil
}
Adds tests for both cases.
Fixes #8415
Change-Id: I897d609d80d46e2354d2669a8a3e090688eee3ad
Reviewed-on: https://go-review.googlesource.com/31259
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Daniel Theophanes <kardianos@gmail.com>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Diffstat (limited to 'src/database/sql/convert_test.go')
| -rw-r--r-- | src/database/sql/convert_test.go | 83 |
1 files changed, 83 insertions, 0 deletions
diff --git a/src/database/sql/convert_test.go b/src/database/sql/convert_test.go index ab81f2f65a..4dfab1f6be 100644 --- a/src/database/sql/convert_test.go +++ b/src/database/sql/convert_test.go @@ -9,6 +9,7 @@ import ( "fmt" "reflect" "runtime" + "strings" "testing" "time" ) @@ -389,3 +390,85 @@ func TestUserDefinedBytes(t *testing.T) { t.Fatal("userDefinedBytes got potentially dirty driver memory") } } + +type Valuer_V string + +func (v Valuer_V) Value() (driver.Value, error) { + return strings.ToUpper(string(v)), nil +} + +type Valuer_P string + +func (p *Valuer_P) Value() (driver.Value, error) { + if p == nil { + return "nil-to-str", nil + } + return strings.ToUpper(string(*p)), nil +} + +func TestDriverArgs(t *testing.T) { + var nilValuerVPtr *Valuer_V + var nilValuerPPtr *Valuer_P + var nilStrPtr *string + tests := []struct { + args []interface{} + want []driver.NamedValue + }{ + 0: { + args: []interface{}{Valuer_V("foo")}, + want: []driver.NamedValue{ + driver.NamedValue{ + Ordinal: 1, + Value: "FOO", + }, + }, + }, + 1: { + args: []interface{}{nilValuerVPtr}, + want: []driver.NamedValue{ + driver.NamedValue{ + Ordinal: 1, + Value: nil, + }, + }, + }, + 2: { + args: []interface{}{nilValuerPPtr}, + want: []driver.NamedValue{ + driver.NamedValue{ + Ordinal: 1, + Value: "nil-to-str", + }, + }, + }, + 3: { + args: []interface{}{"plain-str"}, + want: []driver.NamedValue{ + driver.NamedValue{ + Ordinal: 1, + Value: "plain-str", + }, + }, + }, + 4: { + args: []interface{}{nilStrPtr}, + want: []driver.NamedValue{ + driver.NamedValue{ + Ordinal: 1, + Value: nil, + }, + }, + }, + } + for i, tt := range tests { + ds := new(driverStmt) + got, err := driverArgs(ds, tt.args) + if err != nil { + t.Errorf("test[%d]: %v", i, err) + continue + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("test[%d]: got %v, want %v", i, got, tt.want) + } + } +} |
