Skip to content

Commit 5c11a1d

Browse files
committed
pull request jmoiron/sqlx#953
Merge remote-tracking branch 'moxar/master'
2 parents 0892a9f + 402ed0c commit 5c11a1d

2 files changed

Lines changed: 48 additions & 1 deletion

File tree

bind.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ func In(query string, args ...interface{}) (string, []interface{}, error) {
163163
for i, arg := range args {
164164
if a, ok := arg.(driver.Valuer); ok {
165165
var err error
166-
arg, err = a.Value()
166+
arg, err = callValuerValue(a)
167167
if err != nil {
168168
return "", nil, err
169169
}
@@ -263,3 +263,24 @@ func appendReflectSlice(args []interface{}, v reflect.Value, vlen int) []interfa
263263

264264
return args
265265
}
266+
267+
// callValuerValue returns vr.Value(), with one exception:
268+
// If vr.Value is an auto-generated method on a pointer type and the
269+
// pointer is nil, it would panic at runtime in the panicwrap
270+
// method. Treat it like nil instead.
271+
// Issue 8415.
272+
//
273+
// This is so people can implement driver.Value on value types and
274+
// still use nil pointers to those types to mean nil/NULL, just like
275+
// string/*string.
276+
//
277+
// This function is copied from database/sql/driver package
278+
// and mirrored in the database/sql package.
279+
func callValuerValue(vr driver.Valuer) (v driver.Value, err error) {
280+
if rv := reflect.ValueOf(vr); rv.Kind() == reflect.Pointer &&
281+
rv.IsNil() &&
282+
rv.Type().Elem().Implements(reflect.TypeOf((*driver.Valuer)(nil)).Elem()) {
283+
return nil, nil
284+
}
285+
return vr.Value()
286+
}

sqlx_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1504,6 +1504,14 @@ func TestIssue197(t *testing.T) {
15041504
})
15051505
}
15061506

1507+
type Valuer struct {
1508+
Val string
1509+
}
1510+
1511+
func (v Valuer) Value() (driver.Value, error) {
1512+
return v.Val, nil
1513+
}
1514+
15071515
func TestIn(t *testing.T) {
15081516
// some quite normal situations
15091517
type tr struct {
@@ -1538,6 +1546,24 @@ func TestIn(t *testing.T) {
15381546
}
15391547
}
15401548

1549+
// nil driver.Valuer
1550+
t.Run("with nil driver.Valuer", func(t *testing.T) {
1551+
query := `SELECT * FROM foo WHERE x = ? or y IN (?)`
1552+
_, _, err := In(query,
1553+
(*Valuer)(nil), // a non-inited pointer to valuer
1554+
[]interface{}{
1555+
"a", // a usual value
1556+
nil, // a nil value
1557+
Valuer{Val: "foo"}, // a Valuer
1558+
&Valuer{Val: "foo"}, // a pointer to valuer
1559+
(*Valuer)(nil), // a non-inited pointer to valuer
1560+
},
1561+
)
1562+
if err != nil {
1563+
t.Error(err)
1564+
}
1565+
})
1566+
15411567
// too many bindVars, but no slices, so short circuits parsing
15421568
// i'm not sure if this is the right behavior; this query/arg combo
15431569
// might not work, but we shouldn't parse if we don't need to

0 commit comments

Comments
 (0)