From 8ae0bd148b950a98f018b98613e4d930e01e573d Mon Sep 17 00:00:00 2001 From: jianchenma Date: Fri, 5 Feb 2021 17:42:05 +0800 Subject: [PATCH] improve ScanList for package gdb; improve Structs for package gconv --- database/gdb/gdb_type_result_scanlist.go | 19 +- database/gdb/gdb_z_mysql_internal_test.go | 320 ++++++++++++++++++ .../gdb/gdb_z_mysql_time_maintain_test.go | 6 +- util/gconv/gconv_structs.go | 43 ++- 4 files changed, 363 insertions(+), 25 deletions(-) diff --git a/database/gdb/gdb_type_result_scanlist.go b/database/gdb/gdb_type_result_scanlist.go index 9494ef514..c74815389 100644 --- a/database/gdb/gdb_type_result_scanlist.go +++ b/database/gdb/gdb_type_result_scanlist.go @@ -207,7 +207,12 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio } case reflect.Ptr: - e := reflect.New(bindToAttrType.Elem()).Elem() + var element reflect.Value + if bindToAttrValue.IsNil() { + element = reflect.New(bindToAttrType.Elem()).Elem() + } else { + element = bindToAttrValue.Elem() + } if len(relationDataMap) > 0 { relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToSubAttrName) if relationFromAttrField.IsValid() { @@ -216,7 +221,7 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio // There's no relational data. continue } - if err = gconv.Struct(v, e); err != nil { + if err = gconv.Struct(v, element); err != nil { return err } } else { @@ -229,14 +234,13 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio // There's no relational data. continue } - if err = gconv.Struct(v, e); err != nil { + if err = gconv.Struct(v, element); err != nil { return err } } - bindToAttrValue.Set(e.Addr()) + bindToAttrValue.Set(element.Addr()) case reflect.Struct: - e := reflect.New(bindToAttrType).Elem() if len(relationDataMap) > 0 { relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToSubAttrName) if relationFromAttrField.IsValid() { @@ -245,7 +249,7 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio // There's no relational data. continue } - if err = gconv.Struct(relationDataItem, e); err != nil { + if err = gconv.Struct(relationDataItem, bindToAttrValue); err != nil { return err } } else { @@ -258,11 +262,10 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio // There's no relational data. continue } - if err = gconv.Struct(relationDataItem, e); err != nil { + if err = gconv.Struct(relationDataItem, bindToAttrValue); err != nil { return err } } - bindToAttrValue.Set(e) default: return gerror.Newf(`unsupported attribute type: %s`, bindToAttrKind.String()) diff --git a/database/gdb/gdb_z_mysql_internal_test.go b/database/gdb/gdb_z_mysql_internal_test.go index 4e3bf14c5..45946fc31 100644 --- a/database/gdb/gdb_z_mysql_internal_test.go +++ b/database/gdb/gdb_z_mysql_internal_test.go @@ -336,3 +336,323 @@ func TestResult_Structs1(t *testing.T) { t.Assert(array[1].Name, "smith") }) } + +// https://github.com/gogf/gf/issues/1159 +func Test_ScanList_NoRecreate_PtrAttribute(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type S1 struct { + Id int + Name string + Age int + Score int + } + type S3 struct { + One *S1 + } + var ( + s []*S3 + err error + ) + r1 := Result{ + Record{ + "id": gvar.New(1), + "name": gvar.New("john"), + "age": gvar.New(16), + }, + Record{ + "id": gvar.New(2), + "name": gvar.New("smith"), + "age": gvar.New(18), + }, + } + err = r1.ScanList(&s, "One") + t.Assert(err, nil) + t.Assert(len(s), 2) + t.Assert(s[0].One.Name, "john") + t.Assert(s[0].One.Age, 16) + t.Assert(s[1].One.Name, "smith") + t.Assert(s[1].One.Age, 18) + + r2 := Result{ + Record{ + "id": gvar.New(1), + "age": gvar.New(20), + }, + Record{ + "id": gvar.New(2), + "age": gvar.New(21), + }, + } + err = r2.ScanList(&s, "One", "One", "id:Id") + t.Assert(err, nil) + t.Assert(len(s), 2) + t.Assert(s[0].One.Name, "john") + t.Assert(s[0].One.Age, 20) + t.Assert(s[1].One.Name, "smith") + t.Assert(s[1].One.Age, 21) + }) +} + +// https://github.com/gogf/gf/issues/1159 +func Test_ScanList_NoRecreate_StructAttribute(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type S1 struct { + Id int + Name string + Age int + Score int + } + type S3 struct { + One S1 + } + var ( + s []*S3 + err error + ) + r1 := Result{ + Record{ + "id": gvar.New(1), + "name": gvar.New("john"), + "age": gvar.New(16), + }, + Record{ + "id": gvar.New(2), + "name": gvar.New("smith"), + "age": gvar.New(18), + }, + } + err = r1.ScanList(&s, "One") + t.Assert(err, nil) + t.Assert(len(s), 2) + t.Assert(s[0].One.Name, "john") + t.Assert(s[0].One.Age, 16) + t.Assert(s[1].One.Name, "smith") + t.Assert(s[1].One.Age, 18) + + r2 := Result{ + Record{ + "id": gvar.New(1), + "age": gvar.New(20), + }, + Record{ + "id": gvar.New(2), + "age": gvar.New(21), + }, + } + err = r2.ScanList(&s, "One", "One", "id:Id") + t.Assert(err, nil) + t.Assert(len(s), 2) + t.Assert(s[0].One.Name, "john") + t.Assert(s[0].One.Age, 20) + t.Assert(s[1].One.Name, "smith") + t.Assert(s[1].One.Age, 21) + }) +} + +// https://github.com/gogf/gf/issues/1159 +func Test_ScanList_NoRecreate_SliceAttribute_Ptr(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type S1 struct { + Id int + Name string + Age int + Score int + } + type S2 struct { + Id int + Pid int + Name string + Age int + Score int + } + type S3 struct { + One *S1 + Many []*S2 + } + var ( + s []*S3 + err error + ) + r1 := Result{ + Record{ + "id": gvar.New(1), + "name": gvar.New("john"), + "age": gvar.New(16), + }, + Record{ + "id": gvar.New(2), + "name": gvar.New("smith"), + "age": gvar.New(18), + }, + } + err = r1.ScanList(&s, "One") + t.Assert(err, nil) + t.Assert(len(s), 2) + t.Assert(s[0].One.Name, "john") + t.Assert(s[0].One.Age, 16) + t.Assert(s[1].One.Name, "smith") + t.Assert(s[1].One.Age, 18) + + r2 := Result{ + Record{ + "id": gvar.New(100), + "pid": gvar.New(1), + "age": gvar.New(30), + "name": gvar.New("john"), + }, + Record{ + "id": gvar.New(200), + "pid": gvar.New(1), + "age": gvar.New(31), + "name": gvar.New("smith"), + }, + } + err = r2.ScanList(&s, "Many", "One", "pid:Id") + //fmt.Printf("%+v", err) + t.Assert(err, nil) + t.Assert(len(s), 2) + t.Assert(s[0].One.Name, "john") + t.Assert(s[0].One.Age, 16) + t.Assert(len(s[0].Many), 2) + t.Assert(s[0].Many[0].Name, "john") + t.Assert(s[0].Many[0].Age, 30) + t.Assert(s[0].Many[1].Name, "smith") + t.Assert(s[0].Many[1].Age, 31) + + t.Assert(s[1].One.Name, "smith") + t.Assert(s[1].One.Age, 18) + t.Assert(len(s[1].Many), 0) + + r3 := Result{ + Record{ + "id": gvar.New(100), + "pid": gvar.New(1), + "age": gvar.New(40), + }, + Record{ + "id": gvar.New(200), + "pid": gvar.New(1), + "age": gvar.New(41), + }, + } + err = r3.ScanList(&s, "Many", "One", "pid:Id") + //fmt.Printf("%+v", err) + t.Assert(err, nil) + t.Assert(len(s), 2) + t.Assert(s[0].One.Name, "john") + t.Assert(s[0].One.Age, 16) + t.Assert(len(s[0].Many), 2) + t.Assert(s[0].Many[0].Name, "john") + t.Assert(s[0].Many[0].Age, 40) + t.Assert(s[0].Many[1].Name, "smith") + t.Assert(s[0].Many[1].Age, 41) + + t.Assert(s[1].One.Name, "smith") + t.Assert(s[1].One.Age, 18) + t.Assert(len(s[1].Many), 0) + }) +} + +// https://github.com/gogf/gf/issues/1159 +func Test_ScanList_NoRecreate_SliceAttribute_Struct(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + type S1 struct { + Id int + Name string + Age int + Score int + } + type S2 struct { + Id int + Pid int + Name string + Age int + Score int + } + type S3 struct { + One S1 + Many []S2 + } + var ( + s []S3 + err error + ) + r1 := Result{ + Record{ + "id": gvar.New(1), + "name": gvar.New("john"), + "age": gvar.New(16), + }, + Record{ + "id": gvar.New(2), + "name": gvar.New("smith"), + "age": gvar.New(18), + }, + } + err = r1.ScanList(&s, "One") + t.Assert(err, nil) + t.Assert(len(s), 2) + t.Assert(s[0].One.Name, "john") + t.Assert(s[0].One.Age, 16) + t.Assert(s[1].One.Name, "smith") + t.Assert(s[1].One.Age, 18) + + r2 := Result{ + Record{ + "id": gvar.New(100), + "pid": gvar.New(1), + "age": gvar.New(30), + "name": gvar.New("john"), + }, + Record{ + "id": gvar.New(200), + "pid": gvar.New(1), + "age": gvar.New(31), + "name": gvar.New("smith"), + }, + } + err = r2.ScanList(&s, "Many", "One", "pid:Id") + //fmt.Printf("%+v", err) + t.Assert(err, nil) + t.Assert(len(s), 2) + t.Assert(s[0].One.Name, "john") + t.Assert(s[0].One.Age, 16) + t.Assert(len(s[0].Many), 2) + t.Assert(s[0].Many[0].Name, "john") + t.Assert(s[0].Many[0].Age, 30) + t.Assert(s[0].Many[1].Name, "smith") + t.Assert(s[0].Many[1].Age, 31) + + t.Assert(s[1].One.Name, "smith") + t.Assert(s[1].One.Age, 18) + t.Assert(len(s[1].Many), 0) + + r3 := Result{ + Record{ + "id": gvar.New(100), + "pid": gvar.New(1), + "age": gvar.New(40), + }, + Record{ + "id": gvar.New(200), + "pid": gvar.New(1), + "age": gvar.New(41), + }, + } + err = r3.ScanList(&s, "Many", "One", "pid:Id") + //fmt.Printf("%+v", err) + t.Assert(err, nil) + t.Assert(len(s), 2) + t.Assert(s[0].One.Name, "john") + t.Assert(s[0].One.Age, 16) + t.Assert(len(s[0].Many), 2) + t.Assert(s[0].Many[0].Name, "john") + t.Assert(s[0].Many[0].Age, 40) + t.Assert(s[0].Many[1].Name, "smith") + t.Assert(s[0].Many[1].Age, 41) + + t.Assert(s[1].One.Name, "smith") + t.Assert(s[1].One.Age, 18) + t.Assert(len(s[1].Many), 0) + }) +} diff --git a/database/gdb/gdb_z_mysql_time_maintain_test.go b/database/gdb/gdb_z_mysql_time_maintain_test.go index 4bb71398a..74bce2b97 100644 --- a/database/gdb/gdb_z_mysql_time_maintain_test.go +++ b/database/gdb/gdb_z_mysql_time_maintain_test.go @@ -467,7 +467,7 @@ CREATE TABLE %s ( } func Test_SoftDelete(t *testing.T) { - table := "time_test_table" + table := "time_test_table_" + gtime.TimestampNanoStr() if _, err := db.Exec(fmt.Sprintf(` CREATE TABLE %s ( id int(11) NOT NULL, @@ -606,7 +606,7 @@ CREATE TABLE %s ( } func Test_SoftDelete_WhereAndOr(t *testing.T) { - table := "time_test_table" + table := "time_test_table_" + gtime.TimestampNanoStr() if _, err := db.Exec(fmt.Sprintf(` CREATE TABLE %s ( id int(11) NOT NULL, @@ -648,7 +648,7 @@ CREATE TABLE %s ( } func Test_CreateUpdateTime_Struct(t *testing.T) { - table := "time_test_table" + table := "time_test_table_" + gtime.TimestampNanoStr() if _, err := db.Exec(fmt.Sprintf(` CREATE TABLE %s ( id int(11) NOT NULL, diff --git a/util/gconv/gconv_structs.go b/util/gconv/gconv_structs.go index 84133ca41..0eccae8aa 100644 --- a/util/gconv/gconv_structs.go +++ b/util/gconv/gconv_structs.go @@ -91,27 +91,42 @@ func doStructs(params interface{}, pointer interface{}, mapping ...map[string]st return nil } var ( - array = reflect.MakeSlice(pointerRv.Type().Elem(), len(paramsMaps), len(paramsMaps)) - itemType = array.Index(0).Type() + reflectElemArray = reflect.MakeSlice(pointerRv.Type().Elem(), len(paramsMaps), len(paramsMaps)) + itemType = reflectElemArray.Index(0).Type() + itemTypeKind = itemType.Kind() + pointerRvElem = pointerRv.Elem() + pointerRvLength = pointerRvElem.Len() ) - for i := 0; i < len(paramsMaps); i++ { - if itemType.Kind() == reflect.Ptr { - // Slice element is type pointer. - e := reflect.New(itemType.Elem()).Elem() - if err = Struct(paramsMaps[i], e, mapping...); err != nil { + if itemTypeKind == reflect.Ptr { + // Pointer element. + for i := 0; i < len(paramsMaps); i++ { + var tempReflectValue reflect.Value + if i < pointerRvLength { + tempReflectValue = pointerRvElem.Index(i).Elem() + } else { + tempReflectValue = reflect.New(itemType.Elem()).Elem() + } + if err = Struct(paramsMaps[i], tempReflectValue, mapping...); err != nil { return err } - array.Index(i).Set(e.Addr()) - } else { - // Slice element is not type of pointer. - e := reflect.New(itemType).Elem() - if err = Struct(paramsMaps[i], e, mapping...); err != nil { + reflectElemArray.Index(i).Set(tempReflectValue.Addr()) + } + } else { + // Struct element. + for i := 0; i < len(paramsMaps); i++ { + var tempReflectValue reflect.Value + if i < pointerRvLength { + tempReflectValue = pointerRvElem.Index(i) + } else { + tempReflectValue = reflect.New(itemType).Elem() + } + if err = Struct(paramsMaps[i], tempReflectValue, mapping...); err != nil { return err } - array.Index(i).Set(e) + reflectElemArray.Index(i).Set(tempReflectValue) } } - pointerRv.Elem().Set(array) + pointerRv.Elem().Set(reflectElemArray) return nil }