This commit is contained in:
John Guo
2025-02-28 12:17:24 +08:00
parent e83d378e71
commit e31163038e
12 changed files with 133 additions and 197 deletions

View File

@ -1,169 +0,0 @@
// 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 mysql_test
import (
"database/sql"
"fmt"
"reflect"
"strings"
"testing"
"github.com/gogf/gf/v2/test/gtest"
)
// DBConfig represents database configuration
type DBConfig struct {
Username string
Password string
Host string
DBName string
}
// QueryAndScan executes a query and scans the results into a slice of struct pointers
// Parameters:
// - query: SQL query string
// - args: Query arguments
// - dest: Pointer to slice of struct pointers where results will be stored
//
// Returns error if any occurs during the process
func QueryAndScan(config DBConfig, query string, args []interface{}, dest interface{}) error {
// Validate input parameters
destValue := reflect.ValueOf(dest)
if destValue.Kind() != reflect.Ptr || destValue.Elem().Kind() != reflect.Slice {
return fmt.Errorf("dest must be a pointer to slice")
}
// Connect to database
dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?parseTime=true",
config.Username,
config.Password,
config.Host,
config.DBName,
)
db, err := sql.Open("mysql", dsn)
if err != nil {
return fmt.Errorf("failed to connect to database: %v", err)
}
defer db.Close()
// Execute query
rows, err := db.Query(query, args...)
if err != nil {
return fmt.Errorf("failed to execute query: %v", err)
}
defer rows.Close()
// Get column names
columns, err := rows.Columns()
if err != nil {
return fmt.Errorf("failed to get column names: %v", err)
}
// Get the type of slice elements
sliceType := destValue.Elem().Type()
elementType := sliceType.Elem()
if elementType.Kind() == reflect.Ptr {
elementType = elementType.Elem()
}
// Create a map of field names to struct fields
fieldMap := make(map[string]int)
for i := 0; i < elementType.NumField(); i++ {
field := elementType.Field(i)
// Check orm tag first, then json tag, then field name
tagName := field.Tag.Get("orm")
if tagName == "" {
tagName = field.Tag.Get("json")
}
if tagName == "" {
tagName = strings.ToLower(field.Name)
}
fieldMap[tagName] = i
}
// Prepare slice to store results
sliceValue := destValue.Elem()
// Scan rows
for rows.Next() {
// Create a new struct instance
newElem := reflect.New(elementType)
// Create scan destinations that point directly to struct fields
scanDest := make([]interface{}, len(columns))
for i, colName := range columns {
if fieldIndex, ok := fieldMap[colName]; ok {
field := newElem.Elem().Field(fieldIndex)
if field.CanAddr() {
scanDest[i] = field.Addr().Interface()
} else {
// For fields that can't be addressed, use a temporary variable
var v interface{}
scanDest[i] = &v
}
} else {
// Column doesn't map to any field, use a placeholder
var v interface{}
scanDest[i] = &v
}
}
// Scan the row directly into struct fields
if err := rows.Scan(scanDest...); err != nil {
return fmt.Errorf("failed to scan row: %v", err)
}
// Append the new element to the result slice
if sliceType.Elem().Kind() == reflect.Ptr {
sliceValue.Set(reflect.Append(sliceValue, newElem))
} else {
sliceValue.Set(reflect.Append(sliceValue, newElem.Elem()))
}
}
// Check for errors from iterating over rows
if err := rows.Err(); err != nil {
return fmt.Errorf("error iterating over rows: %v", err)
}
return nil
}
func Test_Issue4086_2(t *testing.T) {
config := DBConfig{
Username: "root",
Password: "12345678",
Host: "127.0.0.1",
DBName: "test1",
}
gtest.C(t, func(t *gtest.T) {
type ProxyParam struct {
ProxyId int64 `json:"proxyId" orm:"proxy_id"`
RecommendIds []int64 `json:"recommendIds" orm:"recommend_ids"`
Photos []string `json:"photos" orm:"photos"`
}
var proxyParamList []*ProxyParam
err := QueryAndScan(config, "SELECT * FROM issue4086", nil, &proxyParamList)
fmt.Println(err)
t.Assert(proxyParamList, []*ProxyParam{
{
ProxyId: 1,
RecommendIds: []int64{584, 585},
Photos: nil,
},
{
ProxyId: 2,
RecommendIds: []int64{},
Photos: nil,
},
})
})
}

View File

@ -1719,9 +1719,9 @@ func Test_Issue4086(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
type ProxyParam struct {
ProxyId int64 `json:"proxyId" orm:"proxy_id"`
RecommendIds []int64 `json:"recommendIds" orm:"recommend_ids"`
Photos []string `json:"photos" orm:"photos"`
ProxyId int64 `json:"proxyId" orm:"proxy_id"`
RecommendIds []int64 `json:"recommendIds" orm:"recommend_ids"`
Photos []int64 `json:"photos" orm:"photos"`
}
var proxyParamList []*ProxyParam
@ -1744,9 +1744,34 @@ func Test_Issue4086(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
type ProxyParam struct {
ProxyId int64 `json:"proxyId" orm:"proxy_id"`
RecommendIds []int64 `json:"recommendIds" orm:"recommend_ids"`
Photos []int64 `json:"photos" orm:"photos"`
ProxyId int64 `json:"proxyId" orm:"proxy_id"`
RecommendIds []int64 `json:"recommendIds" orm:"recommend_ids"`
Photos []float32 `json:"photos" orm:"photos"`
}
var proxyParamList []*ProxyParam
err := db.Model(table).Ctx(ctx).Scan(&proxyParamList)
t.AssertNil(err)
t.Assert(len(proxyParamList), 2)
t.Assert(proxyParamList, []*ProxyParam{
{
ProxyId: 1,
RecommendIds: []int64{584, 585},
Photos: nil,
},
{
ProxyId: 2,
RecommendIds: []int64{},
Photos: nil,
},
})
})
gtest.C(t, func(t *gtest.T) {
type ProxyParam struct {
ProxyId int64 `json:"proxyId" orm:"proxy_id"`
RecommendIds []int64 `json:"recommendIds" orm:"recommend_ids"`
Photos []string `json:"photos" orm:"photos"`
}
var proxyParamList []*ProxyParam

View File

@ -17,6 +17,7 @@ import (
"github.com/gogf/gf/v2/encoding/gbinary"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/internal/reflection"
"github.com/gogf/gf/v2/os/gtime"
@ -43,7 +44,7 @@ func Bytes(any any) []byte {
}
func doBytes(any any) ([]byte, error) {
if any == nil {
if empty.IsNil(any) {
return nil, nil
}
switch value := any.(type) {
@ -133,7 +134,7 @@ func String(any any) string {
}
func doString(any any) (string, error) {
if any == nil {
if empty.IsNil(any) {
return "", nil
}
switch value := any.(type) {
@ -254,7 +255,7 @@ func Bool(any any) bool {
}
func doBool(any any) (bool, error) {
if any == nil {
if empty.IsNil(any) {
return false, nil
}
switch value := any.(type) {

View File

@ -13,6 +13,7 @@ import (
"github.com/gogf/gf/v2/encoding/gbinary"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
)
@ -23,7 +24,7 @@ func Float32(any any) float32 {
}
func doFloat32(any any) (float32, error) {
if any == nil {
if empty.IsNil(any) {
return 0, nil
}
switch value := any.(type) {
@ -86,7 +87,7 @@ func Float64(any any) float64 {
}
func doFloat64(any any) (float64, error) {
if any == nil {
if empty.IsNil(any) {
return 0, nil
}
switch value := any.(type) {

View File

@ -14,6 +14,7 @@ import (
"github.com/gogf/gf/v2/encoding/gbinary"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
)
@ -92,7 +93,7 @@ func Int64(any any) int64 {
}
func doInt64(any any) (int64, error) {
if any == nil {
if empty.IsNil(any) {
return 0, nil
}
if v, ok := any.(int64); ok {

View File

@ -7,8 +7,11 @@
package gconv
import (
"bytes"
"reflect"
"strings"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/internal/reflection"
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
@ -21,7 +24,7 @@ func SliceAny(any interface{}) []interface{} {
// Interfaces converts `any` to []interface{}.
func Interfaces(any interface{}) []interface{} {
if any == nil {
if empty.IsNil(any) {
return nil
}
var array []interface{}
@ -68,6 +71,9 @@ func Interfaces(any interface{}) []interface{} {
if _ = json.UnmarshalUseNumber(value, &array); array != nil {
return array
}
if bytes.EqualFold([]byte("null"), value) {
return nil
}
}
array = make([]interface{}, len(value))
for k, v := range value {
@ -79,6 +85,9 @@ func Interfaces(any interface{}) []interface{} {
if _ = json.UnmarshalUseNumber(byteValue, &array); array != nil {
return array
}
if strings.EqualFold(value, "null") {
return nil
}
}
case []uint16:

View File

@ -7,8 +7,11 @@
package gconv
import (
"bytes"
"reflect"
"strings"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/internal/reflection"
"github.com/gogf/gf/v2/internal/utils"
@ -37,7 +40,7 @@ func Floats(any interface{}) []float64 {
// Float32s converts `any` to []float32.
func Float32s(any interface{}) []float32 {
if any == nil {
if empty.IsNil(any) {
return nil
}
var (
@ -83,6 +86,9 @@ func Float32s(any interface{}) []float32 {
if _ = json.UnmarshalUseNumber(value, &array); array != nil {
return array
}
if bytes.EqualFold([]byte("null"), value) {
return nil
}
}
array = make([]float32, len(value))
for k, v := range value {
@ -94,6 +100,9 @@ func Float32s(any interface{}) []float32 {
if _ = json.UnmarshalUseNumber(byteValue, &array); array != nil {
return array
}
if strings.EqualFold(value, "null") {
return nil
}
}
if value == "" {
return []float32{}
@ -166,7 +175,7 @@ func Float32s(any interface{}) []float32 {
// Float64s converts `any` to []float64.
func Float64s(any interface{}) []float64 {
if any == nil {
if empty.IsNil(any) {
return nil
}
var (
@ -212,6 +221,9 @@ func Float64s(any interface{}) []float64 {
if _ = json.UnmarshalUseNumber(value, &array); array != nil {
return array
}
if bytes.EqualFold([]byte("null"), value) {
return nil
}
}
array = make([]float64, len(value))
for k, v := range value {
@ -223,6 +235,9 @@ func Float64s(any interface{}) []float64 {
if _ = json.UnmarshalUseNumber(byteValue, &array); array != nil {
return array
}
if strings.EqualFold(value, "null") {
return nil
}
}
if value == "" {
return []float64{}

View File

@ -7,8 +7,11 @@
package gconv
import (
"bytes"
"reflect"
"strings"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/internal/reflection"
"github.com/gogf/gf/v2/internal/utils"
@ -32,7 +35,7 @@ func SliceInt64(any interface{}) []int64 {
// Ints converts `any` to []int.
func Ints(any interface{}) []int {
if any == nil {
if empty.IsNil(any) {
return nil
}
var (
@ -76,6 +79,9 @@ func Ints(any interface{}) []int {
if _ = json.UnmarshalUseNumber(value, &array); array != nil {
return array
}
if bytes.EqualFold([]byte("null"), value) {
return nil
}
}
array = make([]int, len(value))
for k, v := range value {
@ -87,6 +93,9 @@ func Ints(any interface{}) []int {
if _ = json.UnmarshalUseNumber(byteValue, &array); array != nil {
return array
}
if strings.EqualFold(value, "null") {
return nil
}
}
if value == "" {
return []int{}
@ -171,7 +180,7 @@ func Ints(any interface{}) []int {
// Int32s converts `any` to []int32.
func Int32s(any interface{}) []int32 {
if any == nil {
if empty.IsNil(any) {
return nil
}
var (
@ -215,6 +224,9 @@ func Int32s(any interface{}) []int32 {
if _ = json.UnmarshalUseNumber(value, &array); array != nil {
return array
}
if bytes.EqualFold([]byte("null"), value) {
return nil
}
}
array = make([]int32, len(value))
for k, v := range value {
@ -226,6 +238,9 @@ func Int32s(any interface{}) []int32 {
if _ = json.UnmarshalUseNumber(byteValue, &array); array != nil {
return array
}
if strings.EqualFold(value, "null") {
return nil
}
}
if value == "" {
return []int32{}
@ -310,7 +325,7 @@ func Int32s(any interface{}) []int32 {
// Int64s converts `any` to []int64.
func Int64s(any interface{}) []int64 {
if any == nil {
if empty.IsNil(any) {
return nil
}
var (
@ -354,6 +369,9 @@ func Int64s(any interface{}) []int64 {
if _ = json.UnmarshalUseNumber(value, &array); array != nil {
return array
}
if bytes.EqualFold([]byte("null"), value) {
return nil
}
}
array = make([]int64, len(value))
for k, v := range value {
@ -365,6 +383,9 @@ func Int64s(any interface{}) []int64 {
if _ = json.UnmarshalUseNumber(byteValue, &array); array != nil {
return array
}
if strings.EqualFold(value, "null") {
return nil
}
}
if value == "" {
return []int64{}

View File

@ -7,8 +7,11 @@
package gconv
import (
"bytes"
"reflect"
"strings"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/internal/reflection"
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
@ -21,7 +24,7 @@ func SliceStr(any interface{}) []string {
// Strings converts `any` to []string.
func Strings(any interface{}) []string {
if any == nil {
if empty.IsNil(any) {
return nil
}
var (
@ -63,6 +66,9 @@ func Strings(any interface{}) []string {
if _ = json.UnmarshalUseNumber(value, &array); array != nil {
return array
}
if bytes.EqualFold([]byte("null"), value) {
return nil
}
}
array = make([]string, len(value))
for k, v := range value {
@ -75,6 +81,9 @@ func Strings(any interface{}) []string {
if _ = json.UnmarshalUseNumber(byteValue, &array); array != nil {
return array
}
if strings.EqualFold(value, "null") {
return nil
}
}
if value == "" {
return []string{}

View File

@ -7,8 +7,11 @@
package gconv
import (
"bytes"
"reflect"
"strings"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/internal/reflection"
"github.com/gogf/gf/v2/internal/utils"
@ -32,7 +35,7 @@ func SliceUint64(any interface{}) []uint64 {
// Uints converts `any` to []uint.
func Uints(any interface{}) []uint {
if any == nil {
if empty.IsNil(any) {
return nil
}
var (
@ -71,6 +74,9 @@ func Uints(any interface{}) []uint {
if _ = json.UnmarshalUseNumber(value, &array); array != nil {
return array
}
if bytes.EqualFold([]byte("null"), value) {
return nil
}
}
array = make([]uint, len(value))
for k, v := range value {
@ -82,6 +88,9 @@ func Uints(any interface{}) []uint {
if _ = json.UnmarshalUseNumber(byteValue, &array); array != nil {
return array
}
if strings.EqualFold(value, "null") {
return nil
}
}
if value == "" {
return []uint{}
@ -169,7 +178,7 @@ func Uints(any interface{}) []uint {
// Uint32s converts `any` to []uint32.
func Uint32s(any interface{}) []uint32 {
if any == nil {
if empty.IsNil(any) {
return nil
}
var (
@ -211,6 +220,9 @@ func Uint32s(any interface{}) []uint32 {
if _ = json.UnmarshalUseNumber(value, &array); array != nil {
return array
}
if bytes.EqualFold([]byte("null"), value) {
return nil
}
}
array = make([]uint32, len(value))
for k, v := range value {
@ -222,6 +234,9 @@ func Uint32s(any interface{}) []uint32 {
if _ = json.UnmarshalUseNumber(byteValue, &array); array != nil {
return array
}
if strings.EqualFold(value, "null") {
return nil
}
}
if value == "" {
return []uint32{}
@ -305,7 +320,7 @@ func Uint32s(any interface{}) []uint32 {
// Uint64s converts `any` to []uint64.
func Uint64s(any interface{}) []uint64 {
if any == nil {
if empty.IsNil(any) {
return nil
}
var (
@ -347,6 +362,9 @@ func Uint64s(any interface{}) []uint64 {
if _ = json.UnmarshalUseNumber(value, &array); array != nil {
return array
}
if bytes.EqualFold([]byte("null"), value) {
return nil
}
}
array = make([]uint64, len(value))
for k, v := range value {
@ -358,6 +376,9 @@ func Uint64s(any interface{}) []uint64 {
if _ = json.UnmarshalUseNumber(byteValue, &array); array != nil {
return array
}
if strings.EqualFold(value, "null") {
return nil
}
}
if value == "" {
return []uint64{}

View File

@ -9,6 +9,7 @@ package gconv
import (
"time"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/internal/utils"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
@ -50,7 +51,7 @@ func Duration(any interface{}) time.Duration {
// If no `format` given, it converts `any` using gtime.NewFromTimeStamp if `any` is numeric,
// or using gtime.StrToTime if `any` is string.
func GTime(any interface{}, format ...string) *gtime.Time {
if any == nil {
if empty.IsNil(any) {
return nil
}
if v, ok := any.(localinterface.IGTime); ok {

View File

@ -14,6 +14,7 @@ import (
"github.com/gogf/gf/v2/encoding/gbinary"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/empty"
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
)
@ -24,7 +25,7 @@ func Uint(any any) uint {
}
func doUint(any any) (uint, error) {
if any == nil {
if empty.IsNil(any) {
return 0, nil
}
if v, ok := any.(uint); ok {
@ -41,7 +42,7 @@ func Uint8(any any) uint8 {
}
func doUint8(any any) (uint8, error) {
if any == nil {
if empty.IsNil(any) {
return 0, nil
}
if v, ok := any.(uint8); ok {
@ -58,7 +59,7 @@ func Uint16(any any) uint16 {
}
func doUint16(any any) (uint16, error) {
if any == nil {
if empty.IsNil(any) {
return 0, nil
}
if v, ok := any.(uint16); ok {
@ -75,7 +76,7 @@ func Uint32(any any) uint32 {
}
func doUint32(any any) (uint32, error) {
if any == nil {
if empty.IsNil(any) {
return 0, nil
}
if v, ok := any.(uint32); ok {
@ -92,7 +93,7 @@ func Uint64(any any) uint64 {
}
func doUint64(any any) (uint64, error) {
if any == nil {
if empty.IsNil(any) {
return 0, nil
}
if v, ok := any.(uint64); ok {