model_table_has_many.go (3105B)
1 package bun 2 3 import ( 4 "context" 5 "database/sql" 6 "fmt" 7 "reflect" 8 9 "github.com/uptrace/bun/internal" 10 "github.com/uptrace/bun/schema" 11 ) 12 13 type hasManyModel struct { 14 *sliceTableModel 15 baseTable *schema.Table 16 rel *schema.Relation 17 18 baseValues map[internal.MapKey][]reflect.Value 19 structKey []interface{} 20 } 21 22 var _ TableModel = (*hasManyModel)(nil) 23 24 func newHasManyModel(j *relationJoin) *hasManyModel { 25 baseTable := j.BaseModel.Table() 26 joinModel := j.JoinModel.(*sliceTableModel) 27 baseValues := baseValues(joinModel, j.Relation.BaseFields) 28 if len(baseValues) == 0 { 29 return nil 30 } 31 m := hasManyModel{ 32 sliceTableModel: joinModel, 33 baseTable: baseTable, 34 rel: j.Relation, 35 36 baseValues: baseValues, 37 } 38 if !m.sliceOfPtr { 39 m.strct = reflect.New(m.table.Type).Elem() 40 } 41 return &m 42 } 43 44 func (m *hasManyModel) ScanRows(ctx context.Context, rows *sql.Rows) (int, error) { 45 columns, err := rows.Columns() 46 if err != nil { 47 return 0, err 48 } 49 50 m.columns = columns 51 dest := makeDest(m, len(columns)) 52 53 var n int 54 55 for rows.Next() { 56 if m.sliceOfPtr { 57 m.strct = reflect.New(m.table.Type).Elem() 58 } else { 59 m.strct.Set(m.table.ZeroValue) 60 } 61 m.structInited = false 62 63 m.scanIndex = 0 64 m.structKey = m.structKey[:0] 65 if err := rows.Scan(dest...); err != nil { 66 return 0, err 67 } 68 69 if err := m.parkStruct(); err != nil { 70 return 0, err 71 } 72 73 n++ 74 } 75 if err := rows.Err(); err != nil { 76 return 0, err 77 } 78 79 return n, nil 80 } 81 82 func (m *hasManyModel) Scan(src interface{}) error { 83 column := m.columns[m.scanIndex] 84 m.scanIndex++ 85 86 field, err := m.table.Field(column) 87 if err != nil { 88 return err 89 } 90 91 if err := field.ScanValue(m.strct, src); err != nil { 92 return err 93 } 94 95 for _, f := range m.rel.JoinFields { 96 if f.Name == field.Name { 97 m.structKey = append(m.structKey, field.Value(m.strct).Interface()) 98 break 99 } 100 } 101 102 return nil 103 } 104 105 func (m *hasManyModel) parkStruct() error { 106 baseValues, ok := m.baseValues[internal.NewMapKey(m.structKey)] 107 if !ok { 108 return fmt.Errorf( 109 "bun: has-many relation=%s does not have base %s with id=%q (check join conditions)", 110 m.rel.Field.GoName, m.baseTable, m.structKey) 111 } 112 113 for i, v := range baseValues { 114 if !m.sliceOfPtr { 115 v.Set(reflect.Append(v, m.strct)) 116 continue 117 } 118 119 if i == 0 { 120 v.Set(reflect.Append(v, m.strct.Addr())) 121 continue 122 } 123 124 clone := reflect.New(m.strct.Type()).Elem() 125 clone.Set(m.strct) 126 v.Set(reflect.Append(v, clone.Addr())) 127 } 128 129 return nil 130 } 131 132 func baseValues(model TableModel, fields []*schema.Field) map[internal.MapKey][]reflect.Value { 133 fieldIndex := model.Relation().Field.Index 134 m := make(map[internal.MapKey][]reflect.Value) 135 key := make([]interface{}, 0, len(fields)) 136 walk(model.rootValue(), model.parentIndex(), func(v reflect.Value) { 137 key = modelKey(key[:0], v, fields) 138 mapKey := internal.NewMapKey(key) 139 m[mapKey] = append(m[mapKey], v.FieldByIndex(fieldIndex)) 140 }) 141 return m 142 } 143 144 func modelKey(key []interface{}, strct reflect.Value, fields []*schema.Field) []interface{} { 145 for _, f := range fields { 146 key = append(key, f.Value(strct).Interface()) 147 } 148 return key 149 }