Skip to content

Commit 99f906a

Browse files
committed
Resynchronize ConvertValue from database/sql/driver (#739)
This simply copies recent changes to ConvertValue from database/sql/driver to ensure that our behaviour only differs for uint64. Fixes #739
1 parent bc14601 commit 99f906a

File tree

2 files changed

+36
-4
lines changed

2 files changed

+36
-4
lines changed

driver_test.go

+5
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,7 @@ func TestValuerWithValidation(t *testing.T) {
547547
var out string
548548
var rows *sql.Rows
549549

550+
dbt.mustExec("DROP TABLE IF EXISTS testValuer")
550551
dbt.mustExec("CREATE TABLE testValuer (value VARCHAR(255)) CHARACTER SET utf8")
551552
dbt.mustExec("INSERT INTO testValuer VALUES (?)", in)
552553

@@ -570,6 +571,10 @@ func TestValuerWithValidation(t *testing.T) {
570571
dbt.Errorf("Failed to check nil")
571572
}
572573

574+
if _, err := dbt.db.Exec("INSERT INTO testValuer VALUES (?)", (*testValuerWithValidation)(nil)); err != nil {
575+
dbt.Errorf("Failed to check typed nil")
576+
}
577+
573578
if _, err := dbt.db.Exec("INSERT INTO testValuer VALUES (?)", map[string]bool{}); err == nil {
574579
dbt.Errorf("Failed to check not valuer")
575580
}

statement.go

+31-4
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,15 @@ func (c converter) ConvertValue(v interface{}) (driver.Value, error) {
137137
return v, nil
138138
}
139139

140-
if v != nil {
141-
if valuer, ok := v.(driver.Valuer); ok {
142-
return valuer.Value()
140+
if vr, ok := v.(driver.Valuer); ok {
141+
sv, err := callValuerValue(vr)
142+
if err != nil {
143+
return nil, err
144+
}
145+
if !driver.IsValue(sv) {
146+
return nil, fmt.Errorf("non-Value type %T returned from Value", sv)
143147
}
148+
return sv, nil
144149
}
145150

146151
rv := reflect.ValueOf(v)
@@ -149,8 +154,9 @@ func (c converter) ConvertValue(v interface{}) (driver.Value, error) {
149154
// indirect pointers
150155
if rv.IsNil() {
151156
return nil, nil
157+
} else {
158+
return c.ConvertValue(rv.Elem().Interface())
152159
}
153-
return c.ConvertValue(rv.Elem().Interface())
154160
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
155161
return rv.Int(), nil
156162
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32:
@@ -176,3 +182,24 @@ func (c converter) ConvertValue(v interface{}) (driver.Value, error) {
176182
}
177183
return nil, fmt.Errorf("unsupported type %T, a %s", v, rv.Kind())
178184
}
185+
186+
var valuerReflectType = reflect.TypeOf((*driver.Valuer)(nil)).Elem()
187+
188+
// callValuerValue returns vr.Value(), with one exception:
189+
// If vr.Value is an auto-generated method on a pointer type and the
190+
// pointer is nil, it would panic at runtime in the panicwrap
191+
// method. Treat it like nil instead.
192+
//
193+
// This is so people can implement driver.Value on value types and
194+
// still use nil pointers to those types to mean nil/NULL, just like
195+
// string/*string.
196+
//
197+
// This function is copied from the database/sql package.
198+
func callValuerValue(vr driver.Valuer) (v driver.Value, err error) {
199+
if rv := reflect.ValueOf(vr); rv.Kind() == reflect.Ptr &&
200+
rv.IsNil() &&
201+
rv.Type().Elem().Implements(valuerReflectType) {
202+
return nil, nil
203+
}
204+
return vr.Value()
205+
}

0 commit comments

Comments
 (0)