package dbmeta import ( "fmt" "reflect" "strings" "git.bit5.ru/backend/db" "git.bit5.ru/backend/errors" "git.bit5.ru/backend/meta" ) func LoadMetaStruct(db *db.DBC, data meta.IMetaStruct, ownerId uint32) error { return loadStruct(db, data, ownerId) } func loadStruct(db *db.DBC, itemInter interface{}, ownerId uint32) error { refItem := reflect.ValueOf(itemInter) mType := refItem.Type() var fprops map[string]map[string]string if refItem.CanInterface() { imeta, _ := refItem.Interface().(meta.IClassProps) if imeta != nil { fprops = *imeta.CLASS_FIELDS_PROPS() } } if mType.Kind() == reflect.Ptr { refItem = refItem.Elem() mType = mType.Elem() } for i := 0; i < mType.NumField(); i++ { field := mType.Field(i) tfield := field.Type if fprops != nil { if props, ok := fprops[field.Name]; ok { if _, ok := props["db_skip_load"]; ok { continue } } } // skip unexported fields if len(field.PkgPath) > 0 { continue } switch tfield.Kind() { case reflect.Slice: if err := loadMetaCollection(db, refItem.Field(i).Addr().Interface(), ownerId); err != nil { return err } break case reflect.Struct: if err := loadStruct(db, refItem.Field(i).Addr().Interface(), ownerId); err != nil { return err } break case reflect.Map: case reflect.Ptr: case reflect.Array: case reflect.UnsafePointer: // Now we don't use these types in meta. If we will - fix it. return errors.Errorf("I don't now what I should do with it: %s", tfield.Kind()) default: break } } var row meta.IMetaStruct if refItem.CanInterface() { row = refItem.Addr().Interface().(meta.IMetaStruct) } if row == nil { return errors.Errorf("Couldn't convert to IdataRow: %s", mType.Name()) } props := *row.CLASS_PROPS() if _, ok := props["POD"]; !ok { // skip struct return nil } if ownerField, ok := props["owner"]; ok { field := refItem.FieldByName(strings.Title(ownerField)) if !field.IsValid() { return errors.Errorf("Owner field \"%s\" is not found in struct \"%s\"", ownerField, mType.Name()) } field.Set(reflect.ValueOf(ownerId)) } if pkeyField, ok := props["pkey"]; ok { field := refItem.FieldByName(strings.Title(pkeyField)) if !field.IsValid() { return errors.Errorf("Pkey field \"%s\" is not found in struct \"%s\"", pkeyField, mType.Name()) } err := FindOne(db, row, fmt.Sprintf("WHERE `%s`=?", pkeyField), []interface{}{field.Interface()}) if err != nil { return err } refItem.Set(reflect.ValueOf(row).Elem()) } else { return errors.Errorf("Struct \"%s\" doesn't have pkey field", mType.Name()) } return nil } func LoadMetaMStruct(db *db.DBC, data interface{}, ownerId uint32) error { return loadMetaCollection(db, data, ownerId) } func loadMetaCollection(db *db.DBC, dataItem interface{}, ownerId uint32) error { var err error refSlice := reflect.ValueOf(dataItem) if refSlice.Kind() == reflect.Ptr { refSlice = refSlice.Elem() } if refSlice.Kind() != reflect.Slice { return errors.Errorf("It isn't slice: %s", refSlice.Kind()) } sliceItem := reflect.New(refSlice.Type().Elem().Elem()) item := sliceItem.Interface().(meta.IMetaDataItem) if item == nil { return errors.Errorf("Couldn't convert to IMetaDataItem: %s", refSlice.Type().Elem()) } ownerFieldIndex := -1 ownerField := item.GetOwnerFieldName() for index, field := range item.GetDbFields() { if field == ownerField { ownerFieldIndex = index break } } if ownerFieldIndex == -1 { return errors.New("Owner field not found in fields list") } sql := "SELECT `" + strings.Join(item.GetDbFields(), "`, `") + "` FROM " + "`" + item.GetDbTableName() + "`" + " WHERE `" + ownerField + "`=" + fmt.Sprintf("%d", ownerId) _, err = db.SelectBySQL(sql).LoadStructs(refSlice.Addr().Interface()) if err != nil { return err } return nil } func FindOne(dbc *db.DBC, dataItem meta.IMetaStruct, appendSQL string, params []interface{}) error { info, err := makeDataRowInfo(dataItem) if err != nil { return err } sql := "SELECT `" + strings.Join(info.fields, "`, `") + "` FROM `" + info.tableName + "` " + appendSQL err = dbc.SelectBySQL(sql, params...).LoadStruct(dataItem) if db.IsNotFoundError(err) { return nil } return err }