// Copyright 2019 The Xorm Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package core import ( "database/sql" "errors" "reflect" "sync" ) type Rows struct { *sql.Rows db *DB } func (rs *Rows) ToMapString() ([]map[string]string, error) { cols, err := rs.Columns() if err != nil { return nil, err } var results = make([]map[string]string, 0, 10) for rs.Next() { var record = make(map[string]string, len(cols)) err = rs.ScanMap(&record) if err != nil { return nil, err } results = append(results, record) } return results, nil } // scan data to a struct's pointer according field index func (rs *Rows) ScanStructByIndex(dest ...interface{}) error { if len(dest) == 0 { return errors.New("at least one struct") } vvvs := make([]reflect.Value, len(dest)) for i, s := range dest { vv := reflect.ValueOf(s) if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { return errors.New("dest should be a struct's pointer") } vvvs[i] = vv.Elem() } cols, err := rs.Columns() if err != nil { return err } newDest := make([]interface{}, len(cols)) var i = 0 for _, vvv := range vvvs { for j := 0; j < vvv.NumField(); j++ { newDest[i] = vvv.Field(j).Addr().Interface() i = i + 1 } } return rs.Rows.Scan(newDest...) } var ( fieldCache = make(map[reflect.Type]map[string]int) fieldCacheMutex sync.RWMutex ) func fieldByName(v reflect.Value, name string) reflect.Value { t := v.Type() fieldCacheMutex.RLock() cache, ok := fieldCache[t] fieldCacheMutex.RUnlock() if !ok { cache = make(map[string]int) for i := 0; i < v.NumField(); i++ { cache[t.Field(i).Name] = i } fieldCacheMutex.Lock() fieldCache[t] = cache fieldCacheMutex.Unlock() } if i, ok := cache[name]; ok { return v.Field(i) } return reflect.Zero(t) } // scan data to a struct's pointer according field name func (rs *Rows) ScanStructByName(dest interface{}) error { vv := reflect.ValueOf(dest) if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { return errors.New("dest should be a struct's pointer") } cols, err := rs.Columns() if err != nil { return err } newDest := make([]interface{}, len(cols)) var v EmptyScanner for j, name := range cols { f := fieldByName(vv.Elem(), rs.db.Mapper.Table2Obj(name)) if f.IsValid() { newDest[j] = f.Addr().Interface() } else { newDest[j] = &v } } return rs.Rows.Scan(newDest...) } // scan data to a slice's pointer, slice's length should equal to columns' number func (rs *Rows) ScanSlice(dest interface{}) error { vv := reflect.ValueOf(dest) if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Slice { return errors.New("dest should be a slice's pointer") } vvv := vv.Elem() cols, err := rs.Columns() if err != nil { return err } newDest := make([]interface{}, len(cols)) for j := 0; j < len(cols); j++ { if j >= vvv.Len() { newDest[j] = reflect.New(vvv.Type().Elem()).Interface() } else { newDest[j] = vvv.Index(j).Addr().Interface() } } err = rs.Rows.Scan(newDest...) if err != nil { return err } srcLen := vvv.Len() for i := srcLen; i < len(cols); i++ { vvv = reflect.Append(vvv, reflect.ValueOf(newDest[i]).Elem()) } return nil } // scan data to a map's pointer func (rs *Rows) ScanMap(dest interface{}) error { vv := reflect.ValueOf(dest) if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { return errors.New("dest should be a map's pointer") } cols, err := rs.Columns() if err != nil { return err } newDest := make([]interface{}, len(cols)) vvv := vv.Elem() for i := range cols { newDest[i] = rs.db.reflectNew(vvv.Type().Elem()).Interface() } err = rs.Rows.Scan(newDest...) if err != nil { return err } for i, name := range cols { vname := reflect.ValueOf(name) vvv.SetMapIndex(vname, reflect.ValueOf(newDest[i]).Elem()) } return nil } type Row struct { rows *Rows // One of these two will be non-nil: err error // deferred error for easy chaining } // ErrorRow return an error row func ErrorRow(err error) *Row { return &Row{ err: err, } } // NewRow from rows func NewRow(rows *Rows, err error) *Row { return &Row{rows, err} } func (row *Row) Columns() ([]string, error) { if row.err != nil { return nil, row.err } return row.rows.Columns() } func (row *Row) Scan(dest ...interface{}) error { if row.err != nil { return row.err } defer row.rows.Close() for _, dp := range dest { if _, ok := dp.(*sql.RawBytes); ok { return errors.New("sql: RawBytes isn't allowed on Row.Scan") } } if !row.rows.Next() { if err := row.rows.Err(); err != nil { return err } return sql.ErrNoRows } err := row.rows.Scan(dest...) if err != nil { return err } // Make sure the query can be processed to completion with no errors. return row.rows.Close() } func (row *Row) ScanStructByName(dest interface{}) error { if row.err != nil { return row.err } defer row.rows.Close() if !row.rows.Next() { if err := row.rows.Err(); err != nil { return err } return sql.ErrNoRows } err := row.rows.ScanStructByName(dest) if err != nil { return err } // Make sure the query can be processed to completion with no errors. return row.rows.Close() } func (row *Row) ScanStructByIndex(dest interface{}) error { if row.err != nil { return row.err } defer row.rows.Close() if !row.rows.Next() { if err := row.rows.Err(); err != nil { return err } return sql.ErrNoRows } err := row.rows.ScanStructByIndex(dest) if err != nil { return err } // Make sure the query can be processed to completion with no errors. return row.rows.Close() } // scan data to a slice's pointer, slice's length should equal to columns' number func (row *Row) ScanSlice(dest interface{}) error { if row.err != nil { return row.err } defer row.rows.Close() if !row.rows.Next() { if err := row.rows.Err(); err != nil { return err } return sql.ErrNoRows } err := row.rows.ScanSlice(dest) if err != nil { return err } // Make sure the query can be processed to completion with no errors. return row.rows.Close() } // scan data to a map's pointer func (row *Row) ScanMap(dest interface{}) error { if row.err != nil { return row.err } defer row.rows.Close() if !row.rows.Next() { if err := row.rows.Err(); err != nil { return err } return sql.ErrNoRows } err := row.rows.ScanMap(dest) if err != nil { return err } // Make sure the query can be processed to completion with no errors. return row.rows.Close() } func (row *Row) ToMapString() (map[string]string, error) { cols, err := row.Columns() if err != nil { return nil, err } var record = make(map[string]string, len(cols)) err = row.ScanMap(&record) if err != nil { return nil, err } return record, nil }