Skip to content

Commit 8e2faff

Browse files
committed
Resynchronize ConvertValue from database/sql/driver (go-sql-driver#739)
This simply copies recent changes to ConvertValue from database/sql/driver to ensure that our behaviour only differs for uint64. Fixes go-sql-driver#739
1 parent 02eb68a commit 8e2faff

File tree

3 files changed

+46
-4
lines changed

3 files changed

+46
-4
lines changed

AUTHORS

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
Aaron Hopkins <go-sql-driver at die.net>
1515
Achille Roussel <achille.roussel at gmail.com>
1616
Alexey Palazhchenko <alexey.palazhchenko at gmail.com>
17+
Andrew Reid <andrew.reid at tixtrack.com>
1718
Arne Hormann <arnehormann at gmail.com>
1819
Asta Xie <xiemengjun at gmail.com>
1920
Bulat Gaifullin <gaifullinbf at gmail.com>

driver_go18_test.go

+14
Original file line numberDiff line numberDiff line change
@@ -796,3 +796,17 @@ func TestRowsColumnTypes(t *testing.T) {
796796
})
797797
}
798798
}
799+
800+
func TestValuerWithValueReceiverGivenNilValue(t *testing.T) {
801+
runTests(t, dsn, func(dbt *DBTest) {
802+
dbt.mustExec("CREATE TABLE test (value VARCHAR(255))")
803+
804+
defer func() {
805+
if r := recover(); r != nil {
806+
dbt.Errorf("Failed to check typed nil before calling valuer with value receiver")
807+
}
808+
}()
809+
810+
dbt.db.Exec("INSERT INTO test VALUES (?)", (*testValuer)(nil))
811+
})
812+
}

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)