diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 289b2a658..91f203499 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -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) } diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index c7d98cdbc..3ba19a73a 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -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) } diff --git a/database/gdb/gdb_type_record.go b/database/gdb/gdb_type_record.go index 9ef5b5692..04582b5b8 100644 --- a/database/gdb/gdb_type_record.go +++ b/database/gdb/gdb_type_record.go @@ -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 to JSON format content. @@ -42,8 +43,31 @@ func (r Record) GMap() *gmap.StrAnyMap { // Struct converts to a struct. // Note that the parameter should be type of *struct/**struct. +// +// Note that if returns error if is nil an given 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) diff --git a/database/gdb/gdb_type_record_deprecated.go b/database/gdb/gdb_type_record_deprecated.go index 8da8688f8..2a0b98c1a 100644 --- a/database/gdb/gdb_type_record_deprecated.go +++ b/database/gdb/gdb_type_record_deprecated.go @@ -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) } diff --git a/database/gdb/gdb_type_result.go b/database/gdb/gdb_type_result.go index cfbeff838..34a90b7d0 100644 --- a/database/gdb/gdb_type_result.go +++ b/database/gdb/gdb_type_result.go @@ -148,27 +148,48 @@ func (r Result) RecordKeyUint(key string) map[uint]Record { // Structs converts to struct slice. // Note that the parameter 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) } diff --git a/database/gdb/gdb_type_result_deprecated.go b/database/gdb/gdb_type_result_deprecated.go index de213049a..6a1a84e38 100644 --- a/database/gdb/gdb_type_result_deprecated.go +++ b/database/gdb/gdb_type_result_deprecated.go @@ -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) } diff --git a/database/gdb/gdb_unit_z_mysql_model_test.go b/database/gdb/gdb_unit_z_mysql_model_test.go index 6310e08b4..ddc277027 100644 --- a/database/gdb/gdb_unit_z_mysql_model_test.go +++ b/database/gdb/gdb_unit_z_mysql_model_test.go @@ -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) }) } diff --git a/database/gdb/gdb_unit_z_mysql_struct_test.go b/database/gdb/gdb_unit_z_mysql_struct_test.go index fb71a8344..845e4b85d 100644 --- a/database/gdb/gdb_unit_z_mysql_struct_test.go +++ b/database/gdb/gdb_unit_z_mysql_struct_test.go @@ -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) + }) }