@@ -132,15 +132,25 @@ func (stmt *mysqlStmt) query(args []driver.Value) (*binaryRows, error) {
132
132
133
133
type converter struct {}
134
134
135
+ // ConvertValue mirrors the reference/default converter in database/sql/driver
136
+ // with _one_ exception. We support uint64 with their high bit and the default
137
+ // implementation does not. This function should be kept in sync with
138
+ // database/sql/driver defaultConverter.ConvertValue() except for that
139
+ // deliberate difference.
135
140
func (c converter ) ConvertValue (v interface {}) (driver.Value , error ) {
136
141
if driver .IsValue (v ) {
137
142
return v , nil
138
143
}
139
144
140
- if v != nil {
141
- if valuer , ok := v .(driver.Valuer ); ok {
142
- return valuer .Value ()
145
+ if vr , ok := v .(driver.Valuer ); ok {
146
+ sv , err := callValuerValue (vr )
147
+ if err != nil {
148
+ return nil , err
149
+ }
150
+ if ! driver .IsValue (sv ) {
151
+ return nil , fmt .Errorf ("non-Value type %T returned from Value" , sv )
143
152
}
153
+ return sv , nil
144
154
}
145
155
146
156
rv := reflect .ValueOf (v )
@@ -149,8 +159,9 @@ func (c converter) ConvertValue(v interface{}) (driver.Value, error) {
149
159
// indirect pointers
150
160
if rv .IsNil () {
151
161
return nil , nil
162
+ } else {
163
+ return c .ConvertValue (rv .Elem ().Interface ())
152
164
}
153
- return c .ConvertValue (rv .Elem ().Interface ())
154
165
case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
155
166
return rv .Int (), nil
156
167
case reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 :
@@ -176,3 +187,25 @@ func (c converter) ConvertValue(v interface{}) (driver.Value, error) {
176
187
}
177
188
return nil , fmt .Errorf ("unsupported type %T, a %s" , v , rv .Kind ())
178
189
}
190
+
191
+ var valuerReflectType = reflect .TypeOf ((* driver .Valuer )(nil )).Elem ()
192
+
193
+ // callValuerValue returns vr.Value(), with one exception:
194
+ // If vr.Value is an auto-generated method on a pointer type and the
195
+ // pointer is nil, it would panic at runtime in the panicwrap
196
+ // method. Treat it like nil instead.
197
+ //
198
+ // This is so people can implement driver.Value on value types and
199
+ // still use nil pointers to those types to mean nil/NULL, just like
200
+ // string/*string.
201
+ //
202
+ // This is an exact copy of the same-named unexported function from the
203
+ // database/sql package.
204
+ func callValuerValue (vr driver.Valuer ) (v driver.Value , err error ) {
205
+ if rv := reflect .ValueOf (vr ); rv .Kind () == reflect .Ptr &&
206
+ rv .IsNil () &&
207
+ rv .Type ().Elem ().Implements (valuerReflectType ) {
208
+ return nil , nil
209
+ }
210
+ return vr .Value ()
211
+ }
0 commit comments