github.com/blend/go-sdk@v1.20220411.3/db/populate.go (about) 1 /* 2 3 Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file. 5 6 */ 7 8 package db 9 10 import ( 11 "database/sql" 12 "reflect" 13 14 "github.com/blend/go-sdk/ex" 15 ) 16 17 // PopulateByName sets the values of an object from the values of a sql.Rows object using column names. 18 func PopulateByName(object interface{}, row Rows, cols *ColumnCollection) error { 19 rowColumns, err := row.Columns() 20 if err != nil { 21 return Error(err) 22 } 23 24 var values = make([]interface{}, len(rowColumns)) 25 var columnLookup = cols.Lookup() 26 for i, name := range rowColumns { 27 if col, ok := columnLookup[name]; ok { 28 initColumnValue(i, values, col) 29 } else { 30 var value interface{} 31 values[i] = &value 32 } 33 } 34 35 err = row.Scan(values...) 36 if err != nil { 37 return Error(err) 38 } 39 40 var colName string 41 var field *Column 42 var ok bool 43 44 objectValue := ReflectValue(object) 45 for i, v := range values { 46 colName = rowColumns[i] 47 if field, ok = columnLookup[colName]; ok { 48 err = field.SetValueReflected(objectValue, v) 49 if err != nil { 50 return err 51 } 52 } 53 } 54 55 return nil 56 } 57 58 // PopulateInOrder sets the values of an object in order from a sql.Rows object. 59 // Only use this method if you're certain of the column order. It is faster than populateByName. 60 // Optionally if your object implements Populatable this process will be skipped completely, which is even faster. 61 func PopulateInOrder(object DatabaseMapped, row Scanner, cols *ColumnCollection) (err error) { 62 var values = make([]interface{}, cols.Len()) 63 64 for i, col := range cols.Columns() { 65 initColumnValue(i, values, &col) 66 } 67 if err = row.Scan(values...); err != nil { 68 return Error(err) 69 } 70 71 objectValue := ReflectValue(object) 72 columns := cols.Columns() 73 var field Column 74 for i, v := range values { 75 field = columns[i] 76 if err = field.SetValueReflected(objectValue, v); err != nil { 77 err = ex.New(err) 78 return 79 } 80 } 81 82 return 83 } 84 85 // Zero resets an object. 86 func Zero(object interface{}) error { 87 objectValue := reflect.ValueOf(object) 88 if !objectValue.Elem().CanSet() { 89 return ex.New("zero; cannot set object, did you pass a reference?") 90 } 91 objectValue.Elem().Set(reflect.Zero(objectValue.Type().Elem())) 92 return nil 93 } 94 95 // initColumnValue inserts the correct placeholder in the scan array of values. 96 // it will use `sql.Null` forms where appropriate. 97 // JSON fields are implicitly nullable. 98 func initColumnValue(index int, values []interface{}, col *Column) { 99 if col.IsJSON { 100 values[index] = &sql.NullString{} 101 } else if col.FieldType.Kind() == reflect.Ptr { 102 values[index] = reflect.New(col.FieldType).Interface() 103 } else { 104 values[index] = reflect.New(reflect.PtrTo(col.FieldType)).Interface() 105 } 106 }