77package reflectx
88
99import (
10- "database/sql"
11- "fmt"
1210 "reflect"
1311 "runtime"
14- "strconv"
1512 "strings"
1613 "sync"
14+ _ "unsafe"
1715)
1816
1917// A FieldInfo is metadata for a struct field.
@@ -224,17 +222,15 @@ func (o *ObjectContext) NewRow(value reflect.Value) {
224222// a nestedFieldScanner is returned instead of the standard value reference
225223func (o * ObjectContext ) FieldForIndexes (indexes []int ) reflect.Value {
226224 if len (indexes ) == 1 {
227- val := FieldByIndexes (o .value , indexes )
228- return val
225+ return FieldByIndexes (o .value , indexes )
229226 }
230227
231228 obj := & nestedFieldScanner {
232229 parent : o ,
233230 indexes : indexes ,
234231 }
235232
236- v := reflect .ValueOf (obj ).Elem ()
237- return v
233+ return reflect .ValueOf (obj ).Elem ()
238234}
239235
240236// nestedFieldScanner will only forward the Scan to the nested value if
@@ -245,149 +241,16 @@ type nestedFieldScanner struct {
245241}
246242
247243// Scan implements sql.Scanner.
248- // This method largely mirrors the sql.convertAssign() method with some minor changes
249244func (o * nestedFieldScanner ) Scan (src interface {}) error {
250245 if src == nil {
251246 return nil
252247 }
253-
254- dv := FieldByIndexes (o .parent .value , o .indexes )
255- // Dereference pointer fields to avoid double pointers **T
256- if dv .Kind () == reflect .Pointer {
257- dv .Set (reflect .New (dv .Type ().Elem ()))
258- dv = dv .Elem ()
259- }
260- iface := dv .Addr ().Interface ()
261-
262- if scan , ok := iface .(sql.Scanner ); ok {
263- return scan .Scan (src )
264- }
265-
266- sv := reflect .ValueOf (src )
267-
268- // below is taken from https://cs.opensource.google/go/go/+/refs/tags/go1.19.5:src/database/sql/convert.go
269- // with a few minor edits
270-
271- if sv .IsValid () && sv .Type ().AssignableTo (dv .Type ()) {
272- switch b := src .(type ) {
273- case []byte :
274- dv .Set (reflect .ValueOf (bytesClone (b )))
275- default :
276- dv .Set (sv )
277- }
278-
279- return nil
280- }
281-
282- if dv .Kind () == sv .Kind () && sv .Type ().ConvertibleTo (dv .Type ()) {
283- dv .Set (sv .Convert (dv .Type ()))
284- return nil
285- }
286-
287- // The following conversions use a string value as an intermediate representation
288- // to convert between various numeric types.
289- //
290- // This also allows scanning into user defined types such as "type Int int64".
291- // For symmetry, also check for string destination types.
292- switch dv .Kind () {
293- case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
294- if src == nil {
295- return fmt .Errorf ("converting NULL to %s is unsupported" , dv .Kind ())
296- }
297- s := asString (src )
298- i64 , err := strconv .ParseInt (s , 10 , dv .Type ().Bits ())
299- if err != nil {
300- err = strconvErr (err )
301- return fmt .Errorf ("converting driver.Value type %T (%q) to a %s: %v" , src , s , dv .Kind (), err )
302- }
303- dv .SetInt (i64 )
304- return nil
305- case reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 :
306- if src == nil {
307- return fmt .Errorf ("converting NULL to %s is unsupported" , dv .Kind ())
308- }
309- s := asString (src )
310- u64 , err := strconv .ParseUint (s , 10 , dv .Type ().Bits ())
311- if err != nil {
312- err = strconvErr (err )
313- return fmt .Errorf ("converting driver.Value type %T (%q) to a %s: %v" , src , s , dv .Kind (), err )
314- }
315- dv .SetUint (u64 )
316- return nil
317- case reflect .Float32 , reflect .Float64 :
318- if src == nil {
319- return fmt .Errorf ("converting NULL to %s is unsupported" , dv .Kind ())
320- }
321- s := asString (src )
322- f64 , err := strconv .ParseFloat (s , dv .Type ().Bits ())
323- if err != nil {
324- err = strconvErr (err )
325- return fmt .Errorf ("converting driver.Value type %T (%q) to a %s: %v" , src , s , dv .Kind (), err )
326- }
327- dv .SetFloat (f64 )
328- return nil
329- case reflect .String :
330- if src == nil {
331- return fmt .Errorf ("converting NULL to %s is unsupported" , dv .Kind ())
332- }
333- switch v := src .(type ) {
334- case string :
335- dv .SetString (v )
336- return nil
337- case []byte :
338- dv .SetString (string (v ))
339- return nil
340- }
341- }
342-
343- return fmt .Errorf ("don't know how to parse type %T -> %T" , src , iface )
248+ dest := FieldByIndexes (o .parent .value , o .indexes )
249+ return convertAssign (dest .Addr ().Interface (), src )
344250}
345251
346- // returns internal conversion error if available
347- // taken from https://cs.opensource.google/go/go/+/refs/tags/go1.19.5:src/database/sql/convert.go
348- func strconvErr (err error ) error {
349- if ne , ok := err .(* strconv.NumError ); ok {
350- return ne .Err
351- }
352- return err
353- }
354-
355- // converts value to it's string value
356- // taken from https://cs.opensource.google/go/go/+/refs/tags/go1.19.5:src/database/sql/convert.go
357- func asString (src interface {}) string {
358- switch v := src .(type ) {
359- case string :
360- return v
361- case []byte :
362- return string (v )
363- }
364- rv := reflect .ValueOf (src )
365- switch rv .Kind () {
366- case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
367- return strconv .FormatInt (rv .Int (), 10 )
368- case reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 :
369- return strconv .FormatUint (rv .Uint (), 10 )
370- case reflect .Float64 :
371- return strconv .FormatFloat (rv .Float (), 'g' , - 1 , 64 )
372- case reflect .Float32 :
373- return strconv .FormatFloat (rv .Float (), 'g' , - 1 , 32 )
374- case reflect .Bool :
375- return strconv .FormatBool (rv .Bool ())
376- }
377- return fmt .Sprintf ("%v" , src )
378- }
379-
380- // bytesClone returns a copy of b[:len(b)].
381- // The result may have additional unused capacity.
382- // Clone(nil) returns nil.
383- //
384- // bytesClone is a mirror of bytes.Clone while our go.mod is on an older version
385- func bytesClone (b []byte ) []byte {
386- if b == nil {
387- return nil
388- }
389- return append ([]byte {}, b ... )
390- }
252+ //go:linkname convertAssign database/sql.convertAssign
253+ func convertAssign (dest , src interface {}) error
391254
392255// FieldByIndexes returns a value for the field given by the struct traversal
393256// for the given value.
@@ -396,8 +259,7 @@ func FieldByIndexes(v reflect.Value, indexes []int) reflect.Value {
396259 v = reflect .Indirect (v ).Field (i )
397260 // if this is a pointer and it's nil, allocate a new value and set it
398261 if v .Kind () == reflect .Ptr && v .IsNil () {
399- alloc := reflect .New (Deref (v .Type ()))
400- v .Set (alloc )
262+ v .Set (reflect .New (v .Type ().Elem ()))
401263 }
402264 if v .Kind () == reflect .Map && v .IsNil () {
403265 v .Set (reflect .MakeMap (v .Type ()))
0 commit comments