From 92b791eb08444285cb3a9daec48289e7f72a0ef5 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sat, 9 Jan 2021 21:05:47 +0800 Subject: [PATCH] improve Struct convertion for package gdb; improve IsNil function for package internal/empty --- database/gdb/gdb_func.go | 20 +++++++++--------- database/gdb/gdb_type_record.go | 3 ++- database/gdb/gdb_z_mysql_model_test.go | 12 +++++++++++ database/gdb/gdb_z_mysql_struct_test.go | 5 +++-- frame/g/g_func.go | 7 +++++-- internal/empty/empty.go | 27 ++++++++++++++++++++----- internal/empty/empty_test.go | 21 ++++++++++++++++++- 7 files changed, 74 insertions(+), 21 deletions(-) diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 78c9ac47b..5e7c9b7bf 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -48,9 +48,9 @@ type apiMapStrAny interface { } const ( - ORM_TAG_FOR_STRUCT = "orm" - ORM_TAG_FOR_UNIQUE = "unique" - ORM_TAG_FOR_PRIMARY = "primary" + OrmTagForStruct = "orm" + OrmTagForUnique = "unique" + OrmTagForPrimary = "primary" ) var ( @@ -58,7 +58,7 @@ var ( quoteWordReg = regexp.MustCompile(`^[a-zA-Z0-9\-_]+$`) // Priority tags for struct converting for orm field mapping. - structTagPriority = append([]string{ORM_TAG_FOR_STRUCT}, gconv.StructTagPriority...) + structTagPriority = append([]string{OrmTagForStruct}, gconv.StructTagPriority...) ) // ListItemValues retrieves and returns the elements of all item struct/map with key . @@ -315,14 +315,14 @@ func doQuoteString(s, charLeft, charRight string) string { // GetWhereConditionOfStruct returns the where condition sql and arguments by given struct pointer. // This function automatically retrieves primary or unique field and its attribute value as condition. func GetWhereConditionOfStruct(pointer interface{}) (where string, args []interface{}, err error) { - tagField, err := structs.TagFields(pointer, []string{ORM_TAG_FOR_STRUCT}) + tagField, err := structs.TagFields(pointer, []string{OrmTagForStruct}) if err != nil { return "", nil, err } array := ([]string)(nil) for _, field := range tagField { array = strings.Split(field.TagValue, ",") - if len(array) > 1 && gstr.InArray([]string{ORM_TAG_FOR_UNIQUE, ORM_TAG_FOR_PRIMARY}, array[1]) { + if len(array) > 1 && gstr.InArray([]string{OrmTagForUnique, OrmTagForPrimary}, array[1]) { return array[0], []interface{}{field.Value()}, nil } if len(where) > 0 { @@ -336,14 +336,14 @@ func GetWhereConditionOfStruct(pointer interface{}) (where string, args []interf // GetPrimaryKey retrieves and returns primary key field name from given struct. func GetPrimaryKey(pointer interface{}) (string, error) { - tagField, err := structs.TagFields(pointer, []string{ORM_TAG_FOR_STRUCT}) + tagField, err := structs.TagFields(pointer, []string{OrmTagForStruct}) if err != nil { return "", err } array := ([]string)(nil) for _, field := range tagField { array = strings.Split(field.TagValue, ",") - if len(array) > 1 && array[1] == ORM_TAG_FOR_PRIMARY { + if len(array) > 1 && array[1] == OrmTagForPrimary { return array[0], nil } } @@ -741,7 +741,7 @@ func FormatSqlWithArgs(sql string, args []interface{}) string { // convertMapToStruct maps the to given struct. // Note that the given parameter should be a pointer to s struct. func convertMapToStruct(data map[string]interface{}, pointer interface{}) error { - tagNameMap, err := structs.TagMapName(pointer, []string{ORM_TAG_FOR_STRUCT}) + tagNameMap, err := structs.TagMapName(pointer, []string{OrmTagForStruct}) if err != nil { return err } diff --git a/database/gdb/gdb_type_record.go b/database/gdb/gdb_type_record.go index 7801e18fc..3db440993 100644 --- a/database/gdb/gdb_type_record.go +++ b/database/gdb/gdb_type_record.go @@ -11,6 +11,7 @@ import ( "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/encoding/gparser" "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/internal/empty" "github.com/gogf/gf/util/gconv" "reflect" ) @@ -47,7 +48,7 @@ func (r Record) GMap() *gmap.StrAnyMap { // Note that it returns sql.ErrNoRows if is empty. func (r Record) Struct(pointer interface{}) error { // If the record is empty, it returns error. - if r.IsEmpty() { + if r.IsEmpty() && !empty.IsNil(pointer, true) { return sql.ErrNoRows } // Special handling for parameter type: reflect.Value diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index 697f78382..44ae4d520 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -981,6 +981,18 @@ func Test_Model_Struct(t *testing.T) { err := db.Table(table).Where("id=-1").Struct(user) t.Assert(err, sql.ErrNoRows) }) + gtest.C(t, func(t *gtest.T) { + type User struct { + Id int + Passport string + Password string + NickName string + CreateTime *gtime.Time + } + var user *User + err := db.Table(table).Where("id=-1").Struct(&user) + t.Assert(err, nil) + }) } func Test_Model_Struct_CustomType(t *testing.T) { diff --git a/database/gdb/gdb_z_mysql_struct_test.go b/database/gdb/gdb_z_mysql_struct_test.go index d11648aa3..d7159c4c5 100644 --- a/database/gdb/gdb_z_mysql_struct_test.go +++ b/database/gdb/gdb_z_mysql_struct_test.go @@ -1,4 +1,4 @@ -// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -262,7 +262,8 @@ func Test_Struct_Empty(t *testing.T) { one, err := db.Table(table).Where("id=100").One() t.Assert(err, nil) var user *User - t.AssertNE(one.Struct(&user), nil) + t.Assert(one.Struct(&user), nil) + t.Assert(user, nil) }) gtest.C(t, func(t *gtest.T) { diff --git a/frame/g/g_func.go b/frame/g/g_func.go index d99a77ff3..7b1afc730 100644 --- a/frame/g/g_func.go +++ b/frame/g/g_func.go @@ -52,9 +52,12 @@ func TryCatch(try func(), catch ...func(exception error)) { } // IsNil checks whether given is nil. +// Parameter is used for tracing to the source variable if given is type +// of a pinter that also points to a pointer. It returns nil if the source is nil when +// is true. // Note that it might use reflect feature which affects performance a little bit. -func IsNil(value interface{}) bool { - return empty.IsNil(value) +func IsNil(value interface{}, traceSource ...bool) bool { + return empty.IsNil(value, traceSource...) } // IsEmpty checks whether given empty. diff --git a/internal/empty/empty.go b/internal/empty/empty.go index ae0beb57a..2e59ea5a0 100644 --- a/internal/empty/empty.go +++ b/internal/empty/empty.go @@ -1,10 +1,10 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Package empty provides functions for checking empty variables. +// Package empty provides functions for checking empty/nil variables. package empty import ( @@ -152,8 +152,11 @@ func IsEmpty(value interface{}) bool { } // IsNil checks whether given is nil. +// Parameter is used for tracing to the source variable if given is type +// of a pinter that also points to a pointer. It returns nil if the source is nil when +// is true. // Note that it might use reflect feature which affects performance a little bit. -func IsNil(value interface{}) bool { +func IsNil(value interface{}, traceSource ...bool) bool { if value == nil { return true } @@ -168,10 +171,24 @@ func IsNil(value interface{}) bool { reflect.Map, reflect.Slice, reflect.Func, - reflect.Ptr, reflect.Interface, reflect.UnsafePointer: - return rv.IsNil() + return !rv.IsValid() || rv.IsNil() + + case reflect.Ptr: + if len(traceSource) > 0 && traceSource[0] { + for rv.Kind() == reflect.Ptr { + rv = rv.Elem() + } + if !rv.IsValid() { + return true + } + if rv.Kind() == reflect.Ptr { + return rv.IsNil() + } + } else { + return !rv.IsValid() || rv.IsNil() + } } return false } diff --git a/internal/empty/empty_test.go b/internal/empty/empty_test.go index 520aa64ee..2a4e6ecbd 100644 --- a/internal/empty/empty_test.go +++ b/internal/empty/empty_test.go @@ -1,4 +1,4 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, @@ -108,3 +108,22 @@ func TestIsEmpty(t *testing.T) { t.Assert(empty.IsEmpty(tmpF6), false) }) } + +func TestIsNil(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + t.Assert(empty.IsNil(nil), true) + }) + gtest.C(t, func(t *gtest.T) { + var i int + t.Assert(empty.IsNil(i), false) + }) + gtest.C(t, func(t *gtest.T) { + var i *int + t.Assert(empty.IsNil(i), true) + }) + gtest.C(t, func(t *gtest.T) { + var i *int + t.Assert(empty.IsNil(&i), false) + t.Assert(empty.IsNil(&i, true), true) + }) +}