func newTable(typ reflect.Type) *Table { table, ok := Tables.tables[typ] if ok { return table } table, ok = Tables.inFlight[typ] if ok { return table } tableName := Underscore(typ.Name()) table = &Table{ Name: inflection.Plural(tableName), ModelName: tableName, Type: typ, Fields: make([]*Field, 0, typ.NumField()), FieldsMap: make(map[string]*Field, typ.NumField()), } Tables.inFlight[typ] = table for i := 0; i < typ.NumField(); i++ { f := typ.Field(i) if f.Anonymous { typ := f.Type if typ.Kind() == reflect.Ptr { typ = typ.Elem() } for _, ff := range newTable(typ).Fields { ff = ff.Copy() ff.Index = append(f.Index, ff.Index...) table.AddField(ff) } continue } if f.PkgPath != "" && !f.Anonymous { continue } field := table.newField(typ, f) if field != nil { table.AddField(field) } } typ = reflect.PtrTo(typ) table.Methods = make(map[string]*method) for i := 0; i < typ.NumMethod(); i++ { m := typ.Method(i) if m.PkgPath != "" { continue } if m.Type.NumIn() > 1 { continue } if m.Type.NumOut() != 1 { continue } method := &method{ Index: m.Index, appender: types.Appender(m.Type.Out(0)), } table.Methods[m.Name] = method } Tables.tables[typ] = table delete(Tables.inFlight, typ) return table }
func (t *Table) newField(typ reflect.Type, f reflect.StructField) *Field { sqlName, sqlOpt := parseTag(f.Tag.Get("sql")) if f.Name == "TableName" { t.Name = sqlName return nil } skip := sqlName == "-" if skip || sqlName == "" { sqlName = Underscore(f.Name) } if field, ok := t.FieldsMap[sqlName]; ok { return field } _, pgOpt := parseTag(f.Tag.Get("pg")) ftype := indirectType(f.Type) field := Field{ GoName: f.Name, SQLName: sqlName, Index: f.Index, append: types.Appender(ftype), decode: types.Decoder(ftype), isEmpty: isEmptier(ftype.Kind()), equal: equaler(ftype.Kind()), } if skip { t.FieldsMap[field.SQLName] = &field return nil } if _, ok := pgOpt.Get("nullempty"); ok { field.flags |= NullEmptyFlag } if field.SQLName == "id" { field.flags |= PrimaryKeyFlag t.PKs = append(t.PKs, &field) } else if _, ok := sqlOpt.Get("pk"); ok { field.flags |= PrimaryKeyFlag t.PKs = append(t.PKs, &field) } else if strings.HasSuffix(field.SQLName, "_id") { field.flags |= ForeignKeyFlag } var polymorphic string if s, _ := pgOpt.Get("polymorphic:"); s != "" { polymorphic = Underscore(s) + "_" } switch ftype.Kind() { case reflect.Slice: if ftype.Elem().Kind() == reflect.Struct { joinTable := newTable(ftype.Elem()) if m2mName, _ := pgOpt.Get("many2many:"); m2mName != "" { if m2mSlice, ok := typ.FieldByName(m2mName); ok { t.addRelation(&Relation{ Field: &field, Join: joinTable, M2M: newTable(m2mSlice.Type.Elem()), }) } return nil } var fks []*Field var prefix string if polymorphic != "" { prefix = polymorphic } else { prefix = t.ModelName + "_" } for _, pk := range t.PKs { fkName := prefix + pk.SQLName fk, ok := joinTable.FieldsMap[fkName] if ok { fks = append(fks, fk) } } if len(fks) > 0 { t.addRelation(&Relation{ Polymorphic: polymorphic, Field: &field, FKs: fks, Join: joinTable, }) return nil } } case reflect.Struct: joinTable := newTable(ftype) if len(joinTable.Fields) == 0 { break } for _, ff := range joinTable.Fields { ff = ff.Copy() ff.SQLName = field.SQLName + "__" + ff.SQLName ff.Index = append(field.Index, ff.Index...) t.FieldsMap[ff.SQLName] = ff } var fks []*Field for _, pk := range joinTable.PKs { fkName := field.SQLName + "_" + pk.SQLName fk, ok := t.FieldsMap[fkName] if ok { fks = append(fks, fk) } } if len(fks) > 0 { t.addRelation(&Relation{ One: true, Field: &field, FKs: fks, Join: joinTable, }) } t.FieldsMap[field.SQLName] = &field return nil } return &field }