mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
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>
This commit is contained in:
parent
237d7e34bc
commit
0ce1d79a6a
3 changed files with 131 additions and 4 deletions
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue