it returns error when Scan to a none empty slice with empty Result for package gdb (#2858)

This commit is contained in:
John Guo 2023-08-17 20:29:06 +08:00 committed by GitHub
parent 3df5969348
commit ac3481ed43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 118 additions and 47 deletions

View File

@ -8,6 +8,7 @@ package mssql_test
import ( import (
"context" "context"
"database/sql"
"fmt" "fmt"
"testing" "testing"
"time" "time"
@ -804,8 +805,7 @@ func Test_DB_ToJson(t *testing.T) {
t.Assert(users[0].CreateTime, resultJson.Get("0.CREATE_TIME").String()) t.Assert(users[0].CreateTime, resultJson.Get("0.CREATE_TIME").String())
result = nil result = nil
err = result.Structs(&users) t.Assert(result.Structs(&users), sql.ErrNoRows)
t.AssertNil(err)
}) })
gtest.C(t, func(t *gtest.T) { gtest.C(t, func(t *gtest.T) {

View File

@ -8,6 +8,7 @@ package mysql_test
import ( import (
"context" "context"
"database/sql"
"fmt" "fmt"
"testing" "testing"
"time" "time"
@ -892,8 +893,7 @@ func Test_DB_ToJson(t *testing.T) {
t.Assert(users[0].CreateTime, resultJson.Get("0.create_time").String()) t.Assert(users[0].CreateTime, resultJson.Get("0.create_time").String())
result = nil result = nil
err = result.Structs(&users) t.Assert(result.Structs(&users), sql.ErrNoRows)
t.AssertNil(err)
}) })
gtest.C(t, func(t *gtest.T) { gtest.C(t, func(t *gtest.T) {

View File

@ -301,7 +301,7 @@ func Test_Structs_Empty(t *testing.T) {
all, err := db.Model(table).Where("id>100").All() all, err := db.Model(table).Where("id>100").All()
t.AssertNil(err) t.AssertNil(err)
users := make([]User, 10) users := make([]User, 10)
t.Assert(all.Structs(&users), nil) t.Assert(all.Structs(&users), sql.ErrNoRows)
}) })
gtest.C(t, func(t *gtest.T) { gtest.C(t, func(t *gtest.T) {
all, err := db.Model(table).Where("id>100").All() all, err := db.Model(table).Where("id>100").All()
@ -320,7 +320,7 @@ func Test_Structs_Empty(t *testing.T) {
all, err := db.Model(table).Where("id>100").All() all, err := db.Model(table).Where("id>100").All()
t.AssertNil(err) t.AssertNil(err)
users := make([]*User, 10) users := make([]*User, 10)
t.Assert(all.Structs(&users), nil) t.Assert(all.Structs(&users), sql.ErrNoRows)
}) })
gtest.C(t, func(t *gtest.T) { gtest.C(t, func(t *gtest.T) {
all, err := db.Model(table).Where("id>100").All() all, err := db.Model(table).Where("id>100").All()

View File

@ -4665,3 +4665,44 @@ func Test_Builder_OmitEmptyWhere(t *testing.T) {
t.Assert(count, int64(TableSize)) t.Assert(count, int64(TableSize))
}) })
} }
func Test_Scan_Nil_Result_Error(t *testing.T) {
table := createInitTable()
defer dropTable(table)
type S struct {
Id int
Name string
Age int
Score int
}
gtest.C(t, func(t *gtest.T) {
var s *S
err := db.Model(table).Where("id", 1).Scan(&s)
t.AssertNil(err)
t.Assert(s.Id, 1)
})
gtest.C(t, func(t *gtest.T) {
var s *S
err := db.Model(table).Where("id", 100).Scan(&s)
t.AssertNil(err)
t.Assert(s, nil)
})
gtest.C(t, func(t *gtest.T) {
var s S
err := db.Model(table).Where("id", 100).Scan(&s)
t.Assert(err, sql.ErrNoRows)
})
gtest.C(t, func(t *gtest.T) {
var ss []*S
err := db.Model(table).Scan(&ss)
t.AssertNil(err)
t.Assert(len(ss), TableSize)
})
// If the result is empty, it returns error.
gtest.C(t, func(t *gtest.T) {
var ss = make([]*S, 10)
err := db.Model(table).WhereGT("id", 100).Scan(&ss)
t.Assert(err, sql.ErrNoRows)
})
}

View File

@ -8,6 +8,7 @@ package sqlite_test
import ( import (
"context" "context"
"database/sql"
"fmt" "fmt"
"testing" "testing"
"time" "time"
@ -839,8 +840,7 @@ func Test_DB_ToJson(t *testing.T) {
t.Assert(users[0].CreateTime, resultJson.Get("0.create_time").String()) t.Assert(users[0].CreateTime, resultJson.Get("0.create_time").String())
result = nil result = nil
err = result.Structs(&users) t.Assert(result.Structs(&users), sql.ErrNoRows)
t.AssertNil(err)
}) })
gtest.C(t, func(t *gtest.T) { gtest.C(t, func(t *gtest.T) {

View File

@ -8,6 +8,7 @@ package sqlitecgo_test
import ( import (
"context" "context"
"database/sql"
"fmt" "fmt"
"testing" "testing"
"time" "time"
@ -839,8 +840,7 @@ func Test_DB_ToJson(t *testing.T) {
t.Assert(users[0].CreateTime, resultJson.Get("0.create_time").String()) t.Assert(users[0].CreateTime, resultJson.Get("0.create_time").String())
result = nil result = nil
err = result.Structs(&users) t.Assert(result.Structs(&users), sql.ErrNoRows)
t.AssertNil(err)
}) })
gtest.C(t, func(t *gtest.T) { gtest.C(t, func(t *gtest.T) {

View File

@ -7,10 +7,12 @@
package gdb package gdb
import ( import (
"database/sql"
"math" "math"
"github.com/gogf/gf/v2/container/gvar" "github.com/gogf/gf/v2/container/gvar"
"github.com/gogf/gf/v2/encoding/gjson" "github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/util/gconv" "github.com/gogf/gf/v2/util/gconv"
) )
@ -191,5 +193,12 @@ func (r Result) RecordKeyUint(key string) map[uint]Record {
// Structs converts `r` to struct slice. // Structs converts `r` to struct slice.
// Note that the parameter `pointer` should be type of *[]struct/*[]*struct. // Note that the parameter `pointer` should be type of *[]struct/*[]*struct.
func (r Result) Structs(pointer interface{}) (err error) { func (r Result) Structs(pointer interface{}) (err error) {
// If the result is empty and the target pointer is not empty, it returns error.
if r.IsEmpty() {
if !empty.IsEmpty(pointer, true) {
return sql.ErrNoRows
}
return nil
}
return gconv.StructsTag(r, pointer, OrmTagForStruct) return gconv.StructsTag(r, pointer, OrmTagForStruct)
} }

View File

@ -80,7 +80,7 @@ func TryCatch(ctx context.Context, try func(ctx context.Context), catch ...func(
// IsNil checks whether given `value` is nil. // IsNil checks whether given `value` is nil.
// Parameter `traceSource` is used for tracing to the source variable if given `value` is type // Parameter `traceSource` is used for tracing to the source variable if given `value` is type
// of pinter that also points to a pointer. It returns nil if the source is nil when `traceSource` // of pointer that also points to a pointer. It returns nil if the source is nil when `traceSource`
// is true. // is true.
// Note that it might use reflect feature which affects performance a little. // Note that it might use reflect feature which affects performance a little.
func IsNil(value interface{}, traceSource ...bool) bool { func IsNil(value interface{}, traceSource ...bool) bool {
@ -90,8 +90,12 @@ func IsNil(value interface{}, traceSource ...bool) bool {
// IsEmpty checks whether given `value` empty. // IsEmpty checks whether given `value` empty.
// It returns true if `value` is in: 0, nil, false, "", len(slice/map/chan) == 0. // It returns true if `value` is in: 0, nil, false, "", len(slice/map/chan) == 0.
// Or else it returns true. // Or else it returns true.
func IsEmpty(value interface{}) bool { //
return empty.IsEmpty(value) // The parameter `traceSource` is used for tracing to the source variable if given `value` is type of pointer
// that also points to a pointer. It returns true if the source is empty when `traceSource` is true.
// Note that it might use reflect feature which affects performance a little.
func IsEmpty(value interface{}, traceSource ...bool) bool {
return empty.IsEmpty(value, traceSource...)
} }
// RequestFromCtx retrieves and returns the Request object from context. // RequestFromCtx retrieves and returns the Request object from context.

View File

@ -1,3 +1,9 @@
// 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,
// You can obtain one at https://github.com/gogf/gf.
package g_test package g_test
import ( import (

View File

@ -37,7 +37,11 @@ type iTime interface {
// IsEmpty checks whether given `value` empty. // IsEmpty checks whether given `value` empty.
// It returns true if `value` is in: 0, nil, false, "", len(slice/map/chan) == 0, // It returns true if `value` is in: 0, nil, false, "", len(slice/map/chan) == 0,
// or else it returns false. // or else it returns false.
func IsEmpty(value interface{}) bool { //
// The parameter `traceSource` is used for tracing to the source variable if given `value` is type of pointer
// that also points to a pointer. It returns true if the source is empty when `traceSource` is true.
// Note that it might use reflect feature which affects performance a little.
func IsEmpty(value interface{}, traceSource ...bool) bool {
if value == nil { if value == nil {
return true return true
} }
@ -88,6 +92,11 @@ func IsEmpty(value interface{}) bool {
return len(result) == 0 return len(result) == 0
default: default:
// Finally, using reflect.
var rv reflect.Value
if v, ok := value.(reflect.Value); ok {
rv = v
} else {
// ========================= // =========================
// Common interfaces checks. // Common interfaces checks.
// ========================= // =========================
@ -115,11 +124,7 @@ func IsEmpty(value interface{}) bool {
} }
return len(f.MapStrAny()) == 0 return len(f.MapStrAny()) == 0
} }
// Finally, using reflect.
var rv reflect.Value
if v, ok := value.(reflect.Value); ok {
rv = v
} else {
rv = reflect.ValueOf(value) rv = reflect.ValueOf(value)
} }
@ -169,21 +174,27 @@ func IsEmpty(value interface{}) bool {
reflect.Array: reflect.Array:
return rv.Len() == 0 return rv.Len() == 0
case reflect.Ptr:
if len(traceSource) > 0 && traceSource[0] {
return IsEmpty(rv.Elem())
}
return rv.IsNil()
case case
reflect.Func, reflect.Func,
reflect.Ptr,
reflect.Interface, reflect.Interface,
reflect.UnsafePointer: reflect.UnsafePointer:
if rv.IsNil() { return rv.IsNil()
case reflect.Invalid:
return true return true
} }
} }
}
return false return false
} }
// IsNil checks whether given `value` is nil, especially for interface{} type value. // IsNil checks whether given `value` is nil, especially for interface{} type value.
// Parameter `traceSource` is used for tracing to the source variable if given `value` is type of pinter // Parameter `traceSource` is used for tracing to the source variable if given `value` is type of pointer
// that also points to a pointer. It returns nil if the source is nil when `traceSource` is true. // that also points to a pointer. It returns nil if the source is nil when `traceSource` is true.
// Note that it might use reflect feature which affects performance a little. // Note that it might use reflect feature which affects performance a little.
func IsNil(value interface{}, traceSource ...bool) bool { func IsNil(value interface{}, traceSource ...bool) bool {

View File

@ -62,7 +62,7 @@ func newFilePool(p *Pool, path string, flag int, perm os.FileMode, ttl time.Dura
// File retrieves file item from the file pointer pool and returns it. It creates one if // File retrieves file item from the file pointer pool and returns it. It creates one if
// the file pointer pool is empty. // the file pointer pool is empty.
// Note that it should be closed when it will never be used. When it's closed, it is not // Note that it should be closed when it will never be used. When it's closed, it is not
// really closed the underlying file pointer but put back to the file pinter pool. // really closed the underlying file pointer but put back to the file pointer pool.
func (p *Pool) File() (*File, error) { func (p *Pool) File() (*File, error) {
if v, err := p.pool.Get(); err != nil { if v, err := p.pool.Get(); err != nil {
return nil, err return nil, err