mirror of https://github.com/go-gorm/gorm.git
Scan Rows into struct, fix #59
This commit is contained in:
parent
a7097106b1
commit
6546ec3b5e
157
README.md
157
README.md
|
@ -68,15 +68,15 @@ go get -u github.com/jinzhu/gorm
|
||||||
- [Limit](#limit)
|
- [Limit](#limit)
|
||||||
- [Offset](#offset)
|
- [Offset](#offset)
|
||||||
- [Count](#count)
|
- [Count](#count)
|
||||||
- [Pluck](#pluck)
|
|
||||||
- [Raw SQL](#raw-sql)
|
|
||||||
- [Row & Rows](#row--rows)
|
|
||||||
- [Scan](#scan)
|
|
||||||
- [Group & Having](#group--having)
|
- [Group & Having](#group--having)
|
||||||
- [Joins](#joins)
|
- [Joins](#joins)
|
||||||
- [Transactions](#transactions)
|
- [Transactions](#transactions)
|
||||||
- [Scopes](#scopes)
|
- [Scopes](#scopes)
|
||||||
- [Callbacks](#callbacks)
|
- [Callbacks](#callbacks)
|
||||||
|
- [Pluck](#pluck)
|
||||||
|
- [Scan](#scan)
|
||||||
|
- [Raw SQL](#raw-sql)
|
||||||
|
- [Row & Rows](#row--rows)
|
||||||
- [Specifying The Table Name](#specifying-the-table-name)
|
- [Specifying The Table Name](#specifying-the-table-name)
|
||||||
- [Error Handling](#error-handling)
|
- [Error Handling](#error-handling)
|
||||||
- [Logger](#logger)
|
- [Logger](#logger)
|
||||||
|
@ -866,73 +866,6 @@ db.Table("deleted_users").Count(&count)
|
||||||
//// SELECT count(*) FROM deleted_users;
|
//// SELECT count(*) FROM deleted_users;
|
||||||
```
|
```
|
||||||
|
|
||||||
## Pluck
|
|
||||||
|
|
||||||
Get selected attributes as map
|
|
||||||
|
|
||||||
```go
|
|
||||||
var ages []int64
|
|
||||||
db.Find(&users).Pluck("age", &ages)
|
|
||||||
|
|
||||||
var names []string
|
|
||||||
db.Model(&User{}).Pluck("name", &names)
|
|
||||||
|
|
||||||
db.Table("deleted_users").Pluck("name", &names)
|
|
||||||
|
|
||||||
// Requesting more than one column? Do it like this:
|
|
||||||
db.Select("name, age").Find(&users)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Raw SQL
|
|
||||||
|
|
||||||
```go
|
|
||||||
db.Exec("DROP TABLE users;")
|
|
||||||
db.Exec("UPDATE orders SET shipped_at=? WHERE id IN (?)", time.Now, []int64{11,22,33})
|
|
||||||
```
|
|
||||||
|
|
||||||
## Row & Rows
|
|
||||||
|
|
||||||
It is even possible to get query result as `*sql.Row` or `*sql.Rows`
|
|
||||||
|
|
||||||
```go
|
|
||||||
row := db.Table("users").Where("name = ?", "jinzhu").Select("name, age").Row() // (*sql.Row)
|
|
||||||
row.Scan(&name, &age)
|
|
||||||
|
|
||||||
rows, err := db.Model(User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows() // (*sql.Rows, error)
|
|
||||||
defer rows.Close()
|
|
||||||
for rows.Next() {
|
|
||||||
...
|
|
||||||
rows.Scan(&name, &age, &email)
|
|
||||||
...
|
|
||||||
}
|
|
||||||
|
|
||||||
// Raw SQL
|
|
||||||
rows, err := db.Raw("select name, age, email from users where name = ?", "jinzhu").Rows() // (*sql.Rows, error)
|
|
||||||
defer rows.Close()
|
|
||||||
for rows.Next() {
|
|
||||||
...
|
|
||||||
rows.Scan(&name, &age, &email)
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Scan
|
|
||||||
|
|
||||||
Scan results into another struct.
|
|
||||||
|
|
||||||
```go
|
|
||||||
type Result struct {
|
|
||||||
Name string
|
|
||||||
Age int
|
|
||||||
}
|
|
||||||
|
|
||||||
var result Result
|
|
||||||
db.Table("users").Select("name, age").Where("name = ?", 3).Scan(&result)
|
|
||||||
|
|
||||||
// Raw SQL
|
|
||||||
db.Raw("SELECT name, age FROM users WHERE name = ?", 3).Scan(&result)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Group & Having
|
## Group & Having
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
@ -1108,7 +1041,7 @@ func (u *User) AfterCreate() (err error) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Save/delete operations in gorm are running in a transaction.
|
Save/delete operations in gorm are running in a transaction.
|
||||||
Changes made in that transaction are not visible unless it is commited.
|
Changes made in that transaction are not visible unless it is commited.
|
||||||
So if you want to use those changes in your callbacks, you need to run your SQL in the same transaction.
|
So if you want to use those changes in your callbacks, you need to run your SQL in the same transaction.
|
||||||
For this Gorm supports passing transactions to callbacks like this:
|
For this Gorm supports passing transactions to callbacks like this:
|
||||||
|
@ -1120,6 +1053,86 @@ func (u *User) AfterCreate(tx *gorm.DB) (err error) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Pluck
|
||||||
|
|
||||||
|
Get selected attributes as map
|
||||||
|
|
||||||
|
```go
|
||||||
|
var ages []int64
|
||||||
|
db.Find(&users).Pluck("age", &ages)
|
||||||
|
|
||||||
|
var names []string
|
||||||
|
db.Model(&User{}).Pluck("name", &names)
|
||||||
|
|
||||||
|
db.Table("deleted_users").Pluck("name", &names)
|
||||||
|
|
||||||
|
// Requesting more than one column? Do it like this:
|
||||||
|
db.Select("name, age").Find(&users)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Scan
|
||||||
|
|
||||||
|
Scan results into another struct.
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Result struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
|
||||||
|
var result Result
|
||||||
|
db.Table("users").Select("name, age").Where("name = ?", 3).Scan(&result)
|
||||||
|
|
||||||
|
// Raw SQL
|
||||||
|
db.Raw("SELECT name, age FROM users WHERE name = ?", 3).Scan(&result)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Raw SQL
|
||||||
|
|
||||||
|
```go
|
||||||
|
db.Exec("DROP TABLE users;")
|
||||||
|
db.Exec("UPDATE orders SET shipped_at=? WHERE id IN (?)", time.Now, []int64{11,22,33})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Row & Rows
|
||||||
|
|
||||||
|
It is even possible to get query result as `*sql.Row` or `*sql.Rows`
|
||||||
|
|
||||||
|
```go
|
||||||
|
row := db.Table("users").Where("name = ?", "jinzhu").Select("name, age").Row() // (*sql.Row)
|
||||||
|
row.Scan(&name, &age)
|
||||||
|
|
||||||
|
rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows() // (*sql.Rows, error)
|
||||||
|
defer rows.Close()
|
||||||
|
for rows.Next() {
|
||||||
|
...
|
||||||
|
rows.Scan(&name, &age, &email)
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raw SQL
|
||||||
|
rows, err := db.Raw("select name, age, email from users where name = ?", "jinzhu").Rows() // (*sql.Rows, error)
|
||||||
|
defer rows.Close()
|
||||||
|
for rows.Next() {
|
||||||
|
...
|
||||||
|
rows.Scan(&name, &age, &email)
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Scan Rows
|
||||||
|
|
||||||
|
```go
|
||||||
|
rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows() // (*sql.Rows, error)
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var user User
|
||||||
|
db.ScanRows(rows, &user)
|
||||||
|
// do something
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Specifying The Table Name
|
## Specifying The Table Name
|
||||||
|
|
||||||
```go
|
```go
|
||||||
|
|
14
main.go
14
main.go
|
@ -224,6 +224,20 @@ func (s *DB) Rows() (*sql.Rows, error) {
|
||||||
return s.NewScope(s.Value).rows()
|
return s.NewScope(s.Value).rows()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DB) ScanRows(rows *sql.Rows, value interface{}) error {
|
||||||
|
var (
|
||||||
|
clone = s.clone()
|
||||||
|
scope = clone.NewScope(value)
|
||||||
|
columns, err = rows.Columns()
|
||||||
|
)
|
||||||
|
|
||||||
|
if clone.AddError(err) == nil {
|
||||||
|
scope.scan(rows, columns, scope.Fields())
|
||||||
|
}
|
||||||
|
|
||||||
|
return clone.Error
|
||||||
|
}
|
||||||
|
|
||||||
func (s *DB) Pluck(column string, value interface{}) *DB {
|
func (s *DB) Pluck(column string, value interface{}) *DB {
|
||||||
return s.NewScope(s.Value).pluck(column, value).db
|
return s.NewScope(s.Value).pluck(column, value).db
|
||||||
}
|
}
|
||||||
|
|
36
main_test.go
36
main_test.go
|
@ -4,6 +4,7 @@ import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"database/sql/driver"
|
"database/sql/driver"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
_ "github.com/denisenkom/go-mssqldb"
|
_ "github.com/denisenkom/go-mssqldb"
|
||||||
|
@ -376,7 +377,7 @@ func TestRows(t *testing.T) {
|
||||||
|
|
||||||
rows, err := DB.Table("users").Where("name = ? or name = ?", user2.Name, user3.Name).Select("name, age").Rows()
|
rows, err := DB.Table("users").Where("name = ? or name = ?", user2.Name, user3.Name).Select("name, age").Rows()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Not error should happen, but got")
|
t.Errorf("Not error should happen, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
count := 0
|
count := 0
|
||||||
|
@ -386,8 +387,39 @@ func TestRows(t *testing.T) {
|
||||||
rows.Scan(&name, &age)
|
rows.Scan(&name, &age)
|
||||||
count++
|
count++
|
||||||
}
|
}
|
||||||
|
|
||||||
if count != 2 {
|
if count != 2 {
|
||||||
t.Errorf("Should found two records with name 3")
|
t.Errorf("Should found two records")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScanRows(t *testing.T) {
|
||||||
|
user1 := User{Name: "ScanRowsUser1", Age: 1, Birthday: now.MustParse("2000-1-1")}
|
||||||
|
user2 := User{Name: "ScanRowsUser2", Age: 10, Birthday: now.MustParse("2010-1-1")}
|
||||||
|
user3 := User{Name: "ScanRowsUser3", Age: 20, Birthday: now.MustParse("2020-1-1")}
|
||||||
|
DB.Save(&user1).Save(&user2).Save(&user3)
|
||||||
|
|
||||||
|
rows, err := DB.Table("users").Where("name = ? or name = ?", user2.Name, user3.Name).Select("name, age").Rows()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Not error should happen, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Result struct {
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
|
||||||
|
var results []Result
|
||||||
|
for rows.Next() {
|
||||||
|
var result Result
|
||||||
|
if err := DB.ScanRows(rows, &result); err != nil {
|
||||||
|
t.Errorf("should get no error, but got %v", err)
|
||||||
|
}
|
||||||
|
results = append(results, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(results, []Result{{Name: "ScanRowsUser2", Age: 10}, {Name: "ScanRowsUser3", Age: 20}}) {
|
||||||
|
t.Errorf("Should find expected results")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
128
utils.go
128
utils.go
|
@ -2,8 +2,11 @@ package gorm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"database/sql/driver"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
@ -50,6 +53,7 @@ const (
|
||||||
upper strCase = true
|
upper strCase = true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ToDBName convert string to db name
|
||||||
func ToDBName(name string) string {
|
func ToDBName(name string) string {
|
||||||
if v := smap.Get(name); v != "" {
|
if v := smap.Get(name); v != "" {
|
||||||
return v
|
return v
|
||||||
|
@ -94,11 +98,14 @@ func ToDBName(name string) string {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SQL expression
|
||||||
type expr struct {
|
type expr struct {
|
||||||
expr string
|
expr string
|
||||||
args []interface{}
|
args []interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Expr generate raw SQL expression for SQL, for example:
|
||||||
|
// DB.Model(&product).Update("price", gorm.Expr("price * ? + ?", 2, 100))
|
||||||
func Expr(expression string, args ...interface{}) *expr {
|
func Expr(expression string, args ...interface{}) *expr {
|
||||||
return &expr{expr: expression, args: args}
|
return &expr{expr: expression, args: args}
|
||||||
}
|
}
|
||||||
|
@ -148,3 +155,124 @@ func toQueryValues(values [][]interface{}) (results []interface{}) {
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fileWithLineNum() string {
|
||||||
|
for i := 2; i < 15; i++ {
|
||||||
|
_, file, line, ok := runtime.Caller(i)
|
||||||
|
if ok && (!regexp.MustCompile(`jinzhu/gorm/.*.go`).MatchString(file) || regexp.MustCompile(`jinzhu/gorm/.*test.go`).MatchString(file)) {
|
||||||
|
return fmt.Sprintf("%v:%v", file, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func isBlank(value reflect.Value) bool {
|
||||||
|
return reflect.DeepEqual(value.Interface(), reflect.Zero(value.Type()).Interface())
|
||||||
|
}
|
||||||
|
|
||||||
|
func toSearchableMap(attrs ...interface{}) (result interface{}) {
|
||||||
|
if len(attrs) > 1 {
|
||||||
|
if str, ok := attrs[0].(string); ok {
|
||||||
|
result = map[string]interface{}{str: attrs[1]}
|
||||||
|
}
|
||||||
|
} else if len(attrs) == 1 {
|
||||||
|
if attr, ok := attrs[0].(map[string]interface{}); ok {
|
||||||
|
result = attr
|
||||||
|
}
|
||||||
|
|
||||||
|
if attr, ok := attrs[0].(interface{}); ok {
|
||||||
|
result = attr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertInterfaceToMap(values interface{}) map[string]interface{} {
|
||||||
|
attrs := map[string]interface{}{}
|
||||||
|
|
||||||
|
switch value := values.(type) {
|
||||||
|
case map[string]interface{}:
|
||||||
|
for k, v := range value {
|
||||||
|
attrs[k] = v
|
||||||
|
}
|
||||||
|
case []interface{}:
|
||||||
|
for _, v := range value {
|
||||||
|
for key, value := range convertInterfaceToMap(v) {
|
||||||
|
attrs[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case interface{}:
|
||||||
|
reflectValue := reflect.ValueOf(values)
|
||||||
|
|
||||||
|
switch reflectValue.Kind() {
|
||||||
|
case reflect.Map:
|
||||||
|
for _, key := range reflectValue.MapKeys() {
|
||||||
|
attrs[ToDBName(key.Interface().(string))] = reflectValue.MapIndex(key).Interface()
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
for _, field := range (&Scope{Value: values}).Fields() {
|
||||||
|
if !field.IsBlank && !field.IsIgnored {
|
||||||
|
attrs[field.DBName] = field.Field.Interface()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return attrs
|
||||||
|
}
|
||||||
|
|
||||||
|
func equalAsString(a interface{}, b interface{}) bool {
|
||||||
|
return toString(a) == toString(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func toString(str interface{}) string {
|
||||||
|
if values, ok := str.([]interface{}); ok {
|
||||||
|
var results []string
|
||||||
|
for _, value := range values {
|
||||||
|
results = append(results, toString(value))
|
||||||
|
}
|
||||||
|
return strings.Join(results, "_")
|
||||||
|
} else if bytes, ok := str.([]byte); ok {
|
||||||
|
return string(bytes)
|
||||||
|
} else if reflectValue := reflect.Indirect(reflect.ValueOf(str)); reflectValue.IsValid() {
|
||||||
|
return fmt.Sprintf("%v", reflectValue.Interface())
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeSlice(elemType reflect.Type) interface{} {
|
||||||
|
if elemType.Kind() == reflect.Slice {
|
||||||
|
elemType = elemType.Elem()
|
||||||
|
}
|
||||||
|
sliceType := reflect.SliceOf(elemType)
|
||||||
|
slice := reflect.New(sliceType)
|
||||||
|
slice.Elem().Set(reflect.MakeSlice(sliceType, 0, 0))
|
||||||
|
return slice.Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
func strInSlice(a string, list []string) bool {
|
||||||
|
for _, b := range list {
|
||||||
|
if b == a {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// getValueFromFields return given fields's value
|
||||||
|
func getValueFromFields(value reflect.Value, fieldNames []string) (results []interface{}) {
|
||||||
|
// If value is a nil pointer, Indirect returns a zero Value!
|
||||||
|
// Therefor we need to check for a zero value,
|
||||||
|
// as FieldByName could panic
|
||||||
|
if indirectValue := reflect.Indirect(value); indirectValue.IsValid() {
|
||||||
|
for _, fieldName := range fieldNames {
|
||||||
|
if fieldValue := indirectValue.FieldByName(fieldName); fieldValue.IsValid() {
|
||||||
|
result := fieldValue.Interface()
|
||||||
|
if r, ok := result.(driver.Valuer); ok {
|
||||||
|
result, _ = r.Value()
|
||||||
|
}
|
||||||
|
results = append(results, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
131
utils_private.go
131
utils_private.go
|
@ -1,131 +0,0 @@
|
||||||
package gorm
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql/driver"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"regexp"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func fileWithLineNum() string {
|
|
||||||
for i := 2; i < 15; i++ {
|
|
||||||
_, file, line, ok := runtime.Caller(i)
|
|
||||||
if ok && (!regexp.MustCompile(`jinzhu/gorm/.*.go`).MatchString(file) || regexp.MustCompile(`jinzhu/gorm/.*test.go`).MatchString(file)) {
|
|
||||||
return fmt.Sprintf("%v:%v", file, line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func isBlank(value reflect.Value) bool {
|
|
||||||
return reflect.DeepEqual(value.Interface(), reflect.Zero(value.Type()).Interface())
|
|
||||||
}
|
|
||||||
|
|
||||||
func toSearchableMap(attrs ...interface{}) (result interface{}) {
|
|
||||||
if len(attrs) > 1 {
|
|
||||||
if str, ok := attrs[0].(string); ok {
|
|
||||||
result = map[string]interface{}{str: attrs[1]}
|
|
||||||
}
|
|
||||||
} else if len(attrs) == 1 {
|
|
||||||
if attr, ok := attrs[0].(map[string]interface{}); ok {
|
|
||||||
result = attr
|
|
||||||
}
|
|
||||||
|
|
||||||
if attr, ok := attrs[0].(interface{}); ok {
|
|
||||||
result = attr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertInterfaceToMap(values interface{}) map[string]interface{} {
|
|
||||||
attrs := map[string]interface{}{}
|
|
||||||
|
|
||||||
switch value := values.(type) {
|
|
||||||
case map[string]interface{}:
|
|
||||||
for k, v := range value {
|
|
||||||
attrs[k] = v
|
|
||||||
}
|
|
||||||
case []interface{}:
|
|
||||||
for _, v := range value {
|
|
||||||
for key, value := range convertInterfaceToMap(v) {
|
|
||||||
attrs[key] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case interface{}:
|
|
||||||
reflectValue := reflect.ValueOf(values)
|
|
||||||
|
|
||||||
switch reflectValue.Kind() {
|
|
||||||
case reflect.Map:
|
|
||||||
for _, key := range reflectValue.MapKeys() {
|
|
||||||
attrs[ToDBName(key.Interface().(string))] = reflectValue.MapIndex(key).Interface()
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
for _, field := range (&Scope{Value: values}).Fields() {
|
|
||||||
if !field.IsBlank && !field.IsIgnored {
|
|
||||||
attrs[field.DBName] = field.Field.Interface()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return attrs
|
|
||||||
}
|
|
||||||
|
|
||||||
func equalAsString(a interface{}, b interface{}) bool {
|
|
||||||
return toString(a) == toString(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func toString(str interface{}) string {
|
|
||||||
if values, ok := str.([]interface{}); ok {
|
|
||||||
var results []string
|
|
||||||
for _, value := range values {
|
|
||||||
results = append(results, toString(value))
|
|
||||||
}
|
|
||||||
return strings.Join(results, "_")
|
|
||||||
} else if bytes, ok := str.([]byte); ok {
|
|
||||||
return string(bytes)
|
|
||||||
} else if reflectValue := reflect.Indirect(reflect.ValueOf(str)); reflectValue.IsValid() {
|
|
||||||
return fmt.Sprintf("%v", reflectValue.Interface())
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeSlice(elemType reflect.Type) interface{} {
|
|
||||||
if elemType.Kind() == reflect.Slice {
|
|
||||||
elemType = elemType.Elem()
|
|
||||||
}
|
|
||||||
sliceType := reflect.SliceOf(elemType)
|
|
||||||
slice := reflect.New(sliceType)
|
|
||||||
slice.Elem().Set(reflect.MakeSlice(sliceType, 0, 0))
|
|
||||||
return slice.Interface()
|
|
||||||
}
|
|
||||||
|
|
||||||
func strInSlice(a string, list []string) bool {
|
|
||||||
for _, b := range list {
|
|
||||||
if b == a {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// getValueFromFields return given fields's value
|
|
||||||
func getValueFromFields(value reflect.Value, fieldNames []string) (results []interface{}) {
|
|
||||||
// If value is a nil pointer, Indirect returns a zero Value!
|
|
||||||
// Therefor we need to check for a zero value,
|
|
||||||
// as FieldByName could panic
|
|
||||||
if indirectValue := reflect.Indirect(value); indirectValue.IsValid() {
|
|
||||||
for _, fieldName := range fieldNames {
|
|
||||||
if fieldValue := indirectValue.FieldByName(fieldName); fieldValue.IsValid() {
|
|
||||||
result := fieldValue.Interface()
|
|
||||||
if r, ok := result.(driver.Valuer); ok {
|
|
||||||
result, _ = r.Value()
|
|
||||||
}
|
|
||||||
results = append(results, result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
Loading…
Reference in New Issue