Skip to content

Commit 0ce1d79

Browse files
committed
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 <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Daniel Theophanes <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent 237d7e3 commit 0ce1d79

File tree

3 files changed

+131
-4
lines changed

3 files changed

+131
-4
lines changed

Diff for: src/database/sql/convert.go

+24-2
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ func driverArgs(ds *driverStmt, args []interface{}) ([]driver.NamedValue, error)
5757
// First, see if the value itself knows how to convert
5858
// itself to a driver type. For example, a NullString
5959
// struct changing into a string or nil.
60-
if svi, ok := arg.(driver.Valuer); ok {
61-
sv, err := svi.Value()
60+
if vr, ok := arg.(driver.Valuer); ok {
61+
sv, err := callValuerValue(vr)
6262
if err != nil {
6363
return nil, fmt.Errorf("sql: argument index %d from Value: %v", n, err)
6464
}
@@ -341,3 +341,25 @@ func asBytes(buf []byte, rv reflect.Value) (b []byte, ok bool) {
341341
}
342342
return
343343
}
344+
345+
var valuerReflectType = reflect.TypeOf((*driver.Valuer)(nil)).Elem()
346+
347+
// callValuerValue returns vr.Value(), with one exception:
348+
// If vr.Value is an auto-generated method on a pointer type and the
349+
// pointer is nil, it would panic at runtime in the panicwrap
350+
// method. Treat it like nil instead.
351+
// Issue 8415.
352+
//
353+
// This is so people can implement driver.Value on value types and
354+
// still use nil pointers to those types to mean nil/NULL, just like
355+
// string/*string.
356+
//
357+
// This function is mirrored in the database/sql/driver package.
358+
func callValuerValue(vr driver.Valuer) (v driver.Value, err error) {
359+
if rv := reflect.ValueOf(vr); rv.Kind() == reflect.Ptr &&
360+
rv.IsNil() &&
361+
rv.Type().Elem().Implements(valuerReflectType) {
362+
return nil, nil
363+
}
364+
return vr.Value()
365+
}

Diff for: src/database/sql/convert_test.go

+83
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"fmt"
1010
"reflect"
1111
"runtime"
12+
"strings"
1213
"testing"
1314
"time"
1415
)
@@ -389,3 +390,85 @@ func TestUserDefinedBytes(t *testing.T) {
389390
t.Fatal("userDefinedBytes got potentially dirty driver memory")
390391
}
391392
}
393+
394+
type Valuer_V string
395+
396+
func (v Valuer_V) Value() (driver.Value, error) {
397+
return strings.ToUpper(string(v)), nil
398+
}
399+
400+
type Valuer_P string
401+
402+
func (p *Valuer_P) Value() (driver.Value, error) {
403+
if p == nil {
404+
return "nil-to-str", nil
405+
}
406+
return strings.ToUpper(string(*p)), nil
407+
}
408+
409+
func TestDriverArgs(t *testing.T) {
410+
var nilValuerVPtr *Valuer_V
411+
var nilValuerPPtr *Valuer_P
412+
var nilStrPtr *string
413+
tests := []struct {
414+
args []interface{}
415+
want []driver.NamedValue
416+
}{
417+
0: {
418+
args: []interface{}{Valuer_V("foo")},
419+
want: []driver.NamedValue{
420+
driver.NamedValue{
421+
Ordinal: 1,
422+
Value: "FOO",
423+
},
424+
},
425+
},
426+
1: {
427+
args: []interface{}{nilValuerVPtr},
428+
want: []driver.NamedValue{
429+
driver.NamedValue{
430+
Ordinal: 1,
431+
Value: nil,
432+
},
433+
},
434+
},
435+
2: {
436+
args: []interface{}{nilValuerPPtr},
437+
want: []driver.NamedValue{
438+
driver.NamedValue{
439+
Ordinal: 1,
440+
Value: "nil-to-str",
441+
},
442+
},
443+
},
444+
3: {
445+
args: []interface{}{"plain-str"},
446+
want: []driver.NamedValue{
447+
driver.NamedValue{
448+
Ordinal: 1,
449+
Value: "plain-str",
450+
},
451+
},
452+
},
453+
4: {
454+
args: []interface{}{nilStrPtr},
455+
want: []driver.NamedValue{
456+
driver.NamedValue{
457+
Ordinal: 1,
458+
Value: nil,
459+
},
460+
},
461+
},
462+
}
463+
for i, tt := range tests {
464+
ds := new(driverStmt)
465+
got, err := driverArgs(ds, tt.args)
466+
if err != nil {
467+
t.Errorf("test[%d]: %v", i, err)
468+
continue
469+
}
470+
if !reflect.DeepEqual(got, tt.want) {
471+
t.Errorf("test[%d]: got %v, want %v", i, got, tt.want)
472+
}
473+
}
474+
}

Diff for: src/database/sql/driver/types.go

+24-2
Original file line numberDiff line numberDiff line change
@@ -208,13 +208,35 @@ type defaultConverter struct{}
208208

209209
var _ ValueConverter = defaultConverter{}
210210

211+
var valuerReflectType = reflect.TypeOf((*Valuer)(nil)).Elem()
212+
213+
// callValuerValue returns vr.Value(), with one exception:
214+
// If vr.Value is an auto-generated method on a pointer type and the
215+
// pointer is nil, it would panic at runtime in the panicwrap
216+
// method. Treat it like nil instead.
217+
// Issue 8415.
218+
//
219+
// This is so people can implement driver.Value on value types and
220+
// still use nil pointers to those types to mean nil/NULL, just like
221+
// string/*string.
222+
//
223+
// This function is mirrored in the database/sql package.
224+
func callValuerValue(vr Valuer) (v Value, err error) {
225+
if rv := reflect.ValueOf(vr); rv.Kind() == reflect.Ptr &&
226+
rv.IsNil() &&
227+
rv.Type().Elem().Implements(valuerReflectType) {
228+
return nil, nil
229+
}
230+
return vr.Value()
231+
}
232+
211233
func (defaultConverter) ConvertValue(v interface{}) (Value, error) {
212234
if IsValue(v) {
213235
return v, nil
214236
}
215237

216-
if svi, ok := v.(Valuer); ok {
217-
sv, err := svi.Value()
238+
if vr, ok := v.(Valuer); ok {
239+
sv, err := callValuerValue(vr)
218240
if err != nil {
219241
return nil, err
220242
}

0 commit comments

Comments
 (0)