@@ -132,15 +132,25 @@ func (stmt *mysqlStmt) query(args []driver.Value) (*binaryRows, error) {
132132
133133type converter struct {}
134134
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.
135140func (c converter ) ConvertValue (v interface {}) (driver.Value , error ) {
136141 if driver .IsValue (v ) {
137142 return v , nil
138143 }
139144
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 )
143152 }
153+ return sv , nil
144154 }
145155
146156 rv := reflect .ValueOf (v )
@@ -149,8 +159,9 @@ func (c converter) ConvertValue(v interface{}) (driver.Value, error) {
149159 // indirect pointers
150160 if rv .IsNil () {
151161 return nil , nil
162+ } else {
163+ return c .ConvertValue (rv .Elem ().Interface ())
152164 }
153- return c .ConvertValue (rv .Elem ().Interface ())
154165 case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
155166 return rv .Int (), nil
156167 case reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 :
@@ -176,3 +187,25 @@ func (c converter) ConvertValue(v interface{}) (driver.Value, error) {
176187 }
177188 return nil , fmt .Errorf ("unsupported type %T, a %s" , v , rv .Kind ())
178189}
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