model_table_m2m.go (2693B)
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 m2mModel 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 = (*m2mModel)(nil) 23 24 func newM2MModel(j *relationJoin) *m2mModel { 25 baseTable := j.BaseModel.Table() 26 joinModel := j.JoinModel.(*sliceTableModel) 27 baseValues := baseValues(joinModel, baseTable.PKs) 28 if len(baseValues) == 0 { 29 return nil 30 } 31 m := &m2mModel{ 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 *m2mModel) 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 *m2mModel) Scan(src interface{}) error { 83 column := m.columns[m.scanIndex] 84 m.scanIndex++ 85 86 field, ok := m.table.FieldMap[column] 87 if !ok { 88 return m.scanM2MColumn(column, src) 89 } 90 91 if err := field.ScanValue(m.strct, src); err != nil { 92 return err 93 } 94 95 for _, fk := range m.rel.M2MBaseFields { 96 if fk.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 *m2mModel) scanM2MColumn(column string, src interface{}) error { 106 for _, field := range m.rel.M2MBaseFields { 107 if field.Name == column { 108 dest := reflect.New(field.IndirectType).Elem() 109 if err := field.Scan(dest, src); err != nil { 110 return err 111 } 112 m.structKey = append(m.structKey, dest.Interface()) 113 break 114 } 115 } 116 117 _, err := m.scanColumn(column, src) 118 return err 119 } 120 121 func (m *m2mModel) parkStruct() error { 122 baseValues, ok := m.baseValues[internal.NewMapKey(m.structKey)] 123 if !ok { 124 return fmt.Errorf( 125 "bun: m2m relation=%s does not have base %s with key=%q (check join conditions)", 126 m.rel.Field.GoName, m.baseTable, m.structKey) 127 } 128 129 for _, v := range baseValues { 130 if m.sliceOfPtr { 131 v.Set(reflect.Append(v, m.strct.Addr())) 132 } else { 133 v.Set(reflect.Append(v, m.strct)) 134 } 135 } 136 137 return nil 138 }