* fix error log when loading issues caused by a xorm bug * upgrade packages * fix fmt * fix Consistency * fix tests
		
			
				
	
	
		
			339 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			339 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // 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
 | |
| }
 |