improve struct conversion of empty result/record for package gdb

This commit is contained in:
John 2020-04-28 21:03:25 +08:00
parent 1eeeeb853e
commit a123a2c086
8 changed files with 163 additions and 137 deletions

View File

@ -203,9 +203,6 @@ func (c *Core) GetStruct(pointer interface{}, sql string, args ...interface{}) e
if err != nil {
return err
}
if len(one) == 0 {
return ErrNoRows
}
return one.Struct(pointer)
}
@ -216,9 +213,6 @@ func (c *Core) GetStructs(pointer interface{}, sql string, args ...interface{})
if err != nil {
return err
}
if len(all) == 0 {
return ErrNoRows
}
return all.Structs(pointer)
}

View File

@ -7,7 +7,6 @@
package gdb
import (
"database/sql"
"fmt"
"github.com/gogf/gf/util/gconv"
"reflect"
@ -171,9 +170,6 @@ func (m *Model) Struct(pointer interface{}, where ...interface{}) error {
if err != nil {
return err
}
if len(one) == 0 {
return sql.ErrNoRows
}
return one.Struct(pointer)
}
@ -198,9 +194,6 @@ func (m *Model) Structs(pointer interface{}, where ...interface{}) error {
if err != nil {
return err
}
if len(all) == 0 {
return sql.ErrNoRows
}
return all.Structs(pointer)
}

View File

@ -8,10 +8,11 @@ package gdb
import (
"database/sql"
"errors"
"github.com/gogf/gf/container/gmap"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/encoding/gparser"
"github.com/gogf/gf/util/gconv"
"reflect"
)
// Json converts <r> to JSON format content.
@ -42,8 +43,31 @@ func (r Record) GMap() *gmap.StrAnyMap {
// Struct converts <r> to a struct.
// Note that the parameter <pointer> should be type of *struct/**struct.
//
// Note that if returns error if <r> is nil an given <pointer> is a pointer to an existing
// struct.
func (r Record) Struct(pointer interface{}) error {
if r == nil {
// Special handling for parameter type: reflect.Value
if _, ok := pointer.(reflect.Value); ok {
return mapToStruct(r.Map(), pointer)
}
var (
reflectValue = reflect.ValueOf(pointer)
reflectKind = reflectValue.Kind()
)
if reflectKind != reflect.Ptr {
return errors.New("parameter should be type of *struct/**struct")
}
reflectValue = reflectValue.Elem()
reflectKind = reflectValue.Kind()
if reflectKind == reflect.Invalid {
return errors.New("parameter is an invalid pointer, maybe nil")
}
if reflectKind != reflect.Ptr && reflectKind != reflect.Struct {
return errors.New("parameter should be type of *struct/**struct")
}
// If the record is nil, check if returning error.
if r == nil && reflectKind == reflect.Struct {
return sql.ErrNoRows
}
return mapToStruct(r.Map(), pointer)

View File

@ -6,36 +6,22 @@
package gdb
import (
"database/sql"
"github.com/gogf/gf/encoding/gparser"
)
// Deprecated.
func (r Record) ToJson() string {
content, _ := gparser.VarToJson(r.Map())
return string(content)
return r.Json()
}
// Deprecated.
func (r Record) ToXml(rootTag ...string) string {
content, _ := gparser.VarToXml(r.Map(), rootTag...)
return string(content)
return r.Xml(rootTag...)
}
// Deprecated.
func (r Record) ToMap() Map {
m := make(map[string]interface{})
for k, v := range r {
m[k] = v.Val()
}
return m
return r.Map()
}
// Deprecated.
func (r Record) ToStruct(pointer interface{}) error {
if r == nil {
return sql.ErrNoRows
}
return mapToStruct(r.Map(), pointer)
return r.Struct(pointer)
}

View File

@ -148,27 +148,48 @@ func (r Result) RecordKeyUint(key string) map[uint]Record {
// Structs converts <r> to struct slice.
// Note that the parameter <pointer> should be type of *[]struct/*[]*struct.
func (r Result) Structs(pointer interface{}) (err error) {
l := len(r)
if l == 0 {
return sql.ErrNoRows
var (
reflectValue = reflect.ValueOf(pointer)
reflectKind = reflectValue.Kind()
)
if reflectKind != reflect.Ptr {
return fmt.Errorf("pointer should be type of pointer to struct slice, but got: %v", reflectKind)
}
t := reflect.TypeOf(pointer)
if t.Kind() != reflect.Ptr {
return fmt.Errorf("pointer should be type of pointer, but got: %v", t.Kind())
reflectValue = reflectValue.Elem()
reflectKind = reflectValue.Kind()
if reflectKind != reflect.Slice && reflectKind != reflect.Array {
return fmt.Errorf("pointer should be type of pointer to struct slice, but got: %v", reflectKind)
}
array := reflect.MakeSlice(t.Elem(), l, l)
itemType := array.Index(0).Type()
for i := 0; i < l; i++ {
if itemType.Kind() == reflect.Ptr {
length := len(r)
if length == 0 {
// The pointed slice is not empty.
if reflectValue.Len() > 0 {
// It here checks if it has struct item, which is already initialized.
// It then returns error to warn the developer its empty and no conversion.
if v := reflectValue.Index(0); v.Kind() != reflect.Ptr {
return sql.ErrNoRows
}
}
// Do nothing for empty struct slice.
return nil
}
var (
reflectType = reflect.TypeOf(pointer)
array = reflect.MakeSlice(reflectType.Elem(), length, length)
itemType = array.Index(0).Type()
itemKind = itemType.Kind()
)
for i := 0; i < length; i++ {
if itemKind == reflect.Ptr {
e := reflect.New(itemType.Elem()).Elem()
if err = r[i].Struct(e); err != nil {
return err
return fmt.Errorf(`slice element conversion failed: %s`, err.Error())
}
array.Index(i).Set(e.Addr())
} else {
e := reflect.New(itemType).Elem()
if err = r[i].Struct(e); err != nil {
return err
return fmt.Errorf(`slice element conversion failed: %s`, err.Error())
}
array.Index(i).Set(e)
}

View File

@ -6,128 +6,52 @@
package gdb
import (
"database/sql"
"fmt"
"reflect"
"github.com/gogf/gf/encoding/gparser"
)
// Deprecated.
func (r Result) ToJson() string {
content, _ := gparser.VarToJson(r.List())
return string(content)
return r.Json()
}
// Deprecated.
func (r Result) ToXml(rootTag ...string) string {
content, _ := gparser.VarToXml(r.List(), rootTag...)
return string(content)
return r.Xml(rootTag...)
}
// Deprecated.
func (r Result) ToList() List {
l := make(List, len(r))
for k, v := range r {
l[k] = v.Map()
}
return l
return r.List()
}
// Deprecated.
func (r Result) ToStringMap(key string) map[string]Map {
m := make(map[string]Map)
for _, item := range r {
if v, ok := item[key]; ok {
m[v.String()] = item.Map()
}
}
return m
return r.MapKeyStr(key)
}
// Deprecated.
func (r Result) ToIntMap(key string) map[int]Map {
m := make(map[int]Map)
for _, item := range r {
if v, ok := item[key]; ok {
m[v.Int()] = item.Map()
}
}
return m
return r.MapKeyInt(key)
}
// Deprecated.
func (r Result) ToUintMap(key string) map[uint]Map {
m := make(map[uint]Map)
for _, item := range r {
if v, ok := item[key]; ok {
m[v.Uint()] = item.Map()
}
}
return m
return r.MapKeyUint(key)
}
// Deprecated.
func (r Result) ToStringRecord(key string) map[string]Record {
m := make(map[string]Record)
for _, item := range r {
if v, ok := item[key]; ok {
m[v.String()] = item
}
}
return m
return r.RecordKeyStr(key)
}
// Deprecated.
func (r Result) ToIntRecord(key string) map[int]Record {
m := make(map[int]Record)
for _, item := range r {
if v, ok := item[key]; ok {
m[v.Int()] = item
}
}
return m
return r.RecordKeyInt(key)
}
// Deprecated.
func (r Result) ToUintRecord(key string) map[uint]Record {
m := make(map[uint]Record)
for _, item := range r {
if v, ok := item[key]; ok {
m[v.Uint()] = item
}
}
return m
return r.RecordKeyUint(key)
}
// Deprecated.
func (r Result) ToStructs(pointer interface{}) (err error) {
l := len(r)
if l == 0 {
return sql.ErrNoRows
}
t := reflect.TypeOf(pointer)
if t.Kind() != reflect.Ptr {
return fmt.Errorf("pointer should be type of pointer, but got: %v", t.Kind())
}
array := reflect.MakeSlice(t.Elem(), l, l)
itemType := array.Index(0).Type()
for i := 0; i < l; i++ {
if itemType.Kind() == reflect.Ptr {
e := reflect.New(itemType.Elem()).Elem()
if err = r[i].Struct(e); err != nil {
return err
}
array.Index(i).Set(e.Addr())
} else {
e := reflect.New(itemType).Elem()
if err = r[i].Struct(e); err != nil {
return err
}
array.Index(i).Set(e)
}
}
reflect.ValueOf(pointer).Elem().Set(array)
return nil
return r.Structs(pointer)
}

View File

@ -820,7 +820,7 @@ func Test_Model_Structs(t *testing.T) {
}
var users []*User
err := db.Table(table).Where("id<0").Structs(&users)
t.Assert(err, sql.ErrNoRows)
t.Assert(err, nil)
})
}
@ -905,12 +905,14 @@ func Test_Model_Scan(t *testing.T) {
NickName string
CreateTime *gtime.Time
}
user := new(User)
users := new([]*User)
var (
user = new(User)
users = new([]*User)
)
err1 := db.Table(table).Where("id < 0").Scan(user)
err2 := db.Table(table).Where("id < 0").Scan(users)
t.Assert(err1, sql.ErrNoRows)
t.Assert(err2, sql.ErrNoRows)
t.Assert(err2, nil)
})
}

View File

@ -94,5 +94,87 @@ func Test_Model_Inherit_MapToStruct(t *testing.T) {
t.Assert(user.CreateTime, data["create_time"])
})
}
func Test_Struct_Empty(t *testing.T) {
table := createTable()
defer dropTable(table)
type User struct {
Id int
Passport string
Password string
Nickname string
}
gtest.C(t, func(t *gtest.T) {
one, err := db.Table(table).Where("id=100").One()
t.Assert(err, nil)
user := new(User)
t.AssertNE(one.Struct(user), nil)
})
gtest.C(t, func(t *gtest.T) {
one, err := db.Table(table).Where("id=100").One()
t.Assert(err, nil)
var user *User
t.Assert(one.Struct(&user), nil)
})
gtest.C(t, func(t *gtest.T) {
one, err := db.Table(table).Where("id=100").One()
t.Assert(err, nil)
var user *User
t.AssertNE(one.Struct(user), nil)
})
}
func Test_Structs_Empty(t *testing.T) {
table := createTable()
defer dropTable(table)
type User struct {
Id int
Passport string
Password string
Nickname string
}
gtest.C(t, func(t *gtest.T) {
all, err := db.Table(table).Where("id>100").All()
t.Assert(err, nil)
users := make([]User, 0)
t.Assert(all.Structs(&users), nil)
})
gtest.C(t, func(t *gtest.T) {
all, err := db.Table(table).Where("id>100").All()
t.Assert(err, nil)
users := make([]User, 10)
t.AssertNE(all.Structs(&users), nil)
})
gtest.C(t, func(t *gtest.T) {
all, err := db.Table(table).Where("id>100").All()
t.Assert(err, nil)
var users []User
t.Assert(all.Structs(&users), nil)
})
gtest.C(t, func(t *gtest.T) {
all, err := db.Table(table).Where("id>100").All()
t.Assert(err, nil)
users := make([]*User, 0)
t.Assert(all.Structs(&users), nil)
})
gtest.C(t, func(t *gtest.T) {
all, err := db.Table(table).Where("id>100").All()
t.Assert(err, nil)
users := make([]*User, 10)
t.Assert(all.Structs(&users), nil)
})
gtest.C(t, func(t *gtest.T) {
all, err := db.Table(table).Where("id>100").All()
t.Assert(err, nil)
var users []*User
t.Assert(all.Structs(&users), nil)
})
}