github.com/octohelm/storage@v0.0.0-20240516030302-1ac2cc1ea347/internal/sql/scanner/struct.go (about) 1 package scanner 2 3 import ( 4 "context" 5 "database/sql" 6 "fmt" 7 "reflect" 8 "strings" 9 10 "github.com/octohelm/storage/pkg/sqlbuilder" 11 12 "github.com/octohelm/storage/internal/sql/scanner/nullable" 13 reflectx "github.com/octohelm/x/reflect" 14 ) 15 16 func scanTo(ctx context.Context, rows *sql.Rows, v interface{}) error { 17 tpe := reflect.TypeOf(v) 18 19 if tpe.Kind() != reflect.Ptr { 20 return fmt.Errorf("scanTo target must be a ptr value, but got %T", v) 21 } 22 23 if s, ok := v.(sql.Scanner); ok { 24 return rows.Scan(s) 25 } 26 27 tpe = reflectx.Deref(tpe) 28 29 switch tpe.Kind() { 30 case reflect.Struct: 31 columns, err := rows.Columns() 32 if err != nil { 33 return err 34 } 35 36 n := len(columns) 37 if n < 1 { 38 return nil 39 } 40 41 dest := make([]interface{}, n) 42 holder := placeholder() 43 44 columnIndexes := map[string]int{} 45 46 for i, columnName := range columns { 47 columnIndexes[strings.ToLower(columnName)] = i 48 dest[i] = holder 49 } 50 51 sqlbuilder.ForEachStructFieldValue(ctx, v, func(sf *sqlbuilder.StructFieldValue) { 52 if sf.TableName != "" { 53 if i, ok := columnIndexes[sf.TableName+"__"+sf.Field.Name]; ok && i > -1 { 54 dest[i] = nullable.NewNullIgnoreScanner(sf.Value.Addr().Interface()) 55 } 56 } 57 58 if i, ok := columnIndexes[sf.Field.Name]; ok && i > -1 { 59 dest[i] = nullable.NewNullIgnoreScanner(sf.Value.Addr().Interface()) 60 } 61 }) 62 63 return rows.Scan(dest...) 64 default: 65 return rows.Scan(nullable.NewNullIgnoreScanner(v)) 66 } 67 } 68 69 func placeholder() sql.Scanner { 70 p := emptyScanner(0) 71 return &p 72 } 73 74 type emptyScanner int 75 76 func (e *emptyScanner) Scan(value interface{}) error { 77 return nil 78 }