diff --git a/README.md b/README.md index 180546c..e5ee847 100644 --- a/README.md +++ b/README.md @@ -228,3 +228,7 @@ ok github.com/vingarcia/kissorm 21.740s - Implement support for nested objects with prefixed table names - Improve error messages - Add tests for tables using composite keys + +### Optimizations Oportunities + +- Test if using a pointer on the field info is faster or not diff --git a/kiss_orm.go b/kiss_orm.go index cf19fec..5ca4e8f 100644 --- a/kiss_orm.go +++ b/kiss_orm.go @@ -381,7 +381,7 @@ func (c DB) insertWithReturningID( for _, id := range idNames { scanFields = append( scanFields, - v.Elem().Field(info.Index[id]).Addr().Interface(), + v.Elem().Field(info.ByName(id).Index).Addr().Interface(), ) } err = rows.Scan(scanFields...) @@ -420,7 +420,7 @@ func (c DB) insertWithLastInsertID( vID := reflect.ValueOf(id) tID := vID.Type() - fieldAddr := v.Elem().Field(info.Index[idName]).Addr() + fieldAddr := v.Elem().Field(info.ByName(idName).Index).Addr() fieldType := fieldAddr.Type().Elem() if !tID.ConvertibleTo(fieldType) { @@ -753,10 +753,11 @@ func scanRows(rows *sql.Rows, record interface{}) error { scanArgs := []interface{}{} for _, name := range names { - idx, found := info.Index[name] - valueScanner := v.Field(idx).Addr() - if !found { - valueScanner = nopScannerValue + fieldInfo := info.ByName(name) + + valueScanner := nopScannerValue + if fieldInfo.Valid { + valueScanner = v.Field(fieldInfo.Index).Addr() } scanArgs = append(scanArgs, valueScanner.Interface()) diff --git a/structs/structs.go b/structs/structs.go index e36dbba..e298539 100644 --- a/structs/structs.go +++ b/structs/structs.go @@ -8,8 +8,36 @@ import ( ) type structInfo struct { - Names map[int]string - Index map[string]int + byIndex map[int]*fieldInfo + byName map[string]*fieldInfo +} + +type fieldInfo struct { + Name string + Index int + Valid bool +} + +func (s structInfo) ByIndex(idx int) *fieldInfo { + field, found := s.byIndex[idx] + if !found { + return &fieldInfo{} + } + return field +} + +func (s structInfo) ByName(name string) *fieldInfo { + field, found := s.byName[name] + if !found { + return &fieldInfo{} + } + return field +} + +func (s structInfo) Add(field fieldInfo) { + field.Valid = true + s.byIndex[field.Index] = &field + s.byName[field.Name] = &field } // This cache is kept as a pkg variable @@ -72,7 +100,7 @@ func StructToMap(obj interface{}) (map[string]interface{}, error) { field = field.Elem() } - m[info.Names[i]] = field.Interface() + m[info.ByIndex(i).Name] = field.Interface() } return m, nil @@ -107,15 +135,15 @@ func FillStructWith(record interface{}, dbRow map[string]interface{}) error { info := getCachedTagInfo(tagInfoCache, t) for colName, rawSrc := range dbRow { - fieldIdx, found := info.Index[colName] - if !found { - // Ignore columns not tagged with `kissorm:""` + fieldInfo := info.ByName(colName) + if !fieldInfo.Valid { + // Ignore columns not tagged with `kissorm:"..."` continue } src := NewPtrConverter(rawSrc) - dest := v.Field(fieldIdx) - destType := t.Field(info.Index[colName]).Type + dest := v.Field(fieldInfo.Index) + destType := t.Field(fieldInfo.Index).Type destValue, err := src.Convert(destType) if err != nil { @@ -259,16 +287,19 @@ func FillSliceWith(entities interface{}, dbRows []map[string]interface{}) error // which improves performance by a lot. func getTagNames(t reflect.Type) structInfo { info := structInfo{ - Names: map[int]string{}, - Index: map[string]int{}, + byIndex: map[int]*fieldInfo{}, + byName: map[string]*fieldInfo{}, } for i := 0; i < t.NumField(); i++ { name := t.Field(i).Tag.Get("kissorm") if name == "" { continue } - info.Names[i] = name - info.Index[name] = i + + info.Add(fieldInfo{ + Name: name, + Index: i, + }) } return info