From a9753f5948fa2f575709d5c7a20662aabe2c3730 Mon Sep 17 00:00:00 2001 From: The-night-elves Date: Sat, 15 Nov 2025 12:46:58 +0800 Subject: [PATCH] feat(contrib/drivers/pgsql): add array type numeric[] and decimal[] converting to Go []float64 support #4457 --- contrib/drivers/pgsql/pgsql_convert.go | 9 +++ .../drivers/pgsql/pgsql_z_unit_init_test.go | 2 + .../drivers/pgsql/pgsql_z_unit_model_test.go | 65 +++++++++++++++++++ database/gdb/gdb.go | 45 ++++++------- 4 files changed, 99 insertions(+), 22 deletions(-) diff --git a/contrib/drivers/pgsql/pgsql_convert.go b/contrib/drivers/pgsql/pgsql_convert.go index 00e1e92fa40..f308d95df26 100644 --- a/contrib/drivers/pgsql/pgsql_convert.go +++ b/contrib/drivers/pgsql/pgsql_convert.go @@ -77,6 +77,8 @@ func (d *Driver) CheckLocalTypeForField(ctx context.Context, fieldType string, f case "_varchar", "_text": return gdb.LocalTypeStringSlice, nil + case "_numeric", "_decimal": + return gdb.LocalTypeFloat64Slice, nil default: return d.Core.CheckLocalTypeForField(ctx, fieldType, fieldValue) @@ -130,6 +132,13 @@ func (d *Driver) ConvertValueForLocal(ctx context.Context, fieldType string, fie } return []string(result), nil + // Float64 slice. + case "_numeric", "_decimal": + var result pq.Float64Array + if err := result.Scan(fieldValue); err != nil { + return nil, err + } + return []float64(result), nil default: return d.Core.ConvertValueForLocal(ctx, fieldType, fieldValue) } diff --git a/contrib/drivers/pgsql/pgsql_z_unit_init_test.go b/contrib/drivers/pgsql/pgsql_z_unit_init_test.go index 1c71042c272..f1a0034f377 100644 --- a/contrib/drivers/pgsql/pgsql_z_unit_init_test.go +++ b/contrib/drivers/pgsql/pgsql_z_unit_init_test.go @@ -85,6 +85,8 @@ func createTableWithDb(db gdb.DB, table ...string) (name string) { create_time timestamp NOT NULL, favorite_movie varchar[], favorite_music text[], + numeric_values numeric[], + decimal_values decimal[], PRIMARY KEY (id) ) ;`, name, )); err != nil { diff --git a/contrib/drivers/pgsql/pgsql_z_unit_model_test.go b/contrib/drivers/pgsql/pgsql_z_unit_model_test.go index 3a51ba264dc..56db22a85b1 100644 --- a/contrib/drivers/pgsql/pgsql_z_unit_model_test.go +++ b/contrib/drivers/pgsql/pgsql_z_unit_model_test.go @@ -692,3 +692,68 @@ func Test_ConvertSliceString(t *testing.T) { t.Assert(len(user2.FavoriteMovie), 0) }) } + +func Test_ConvertSliceFloat64(t *testing.T) { + table := createTable() + defer dropTable(table) + + type Args struct { + NumericValues []float64 `orm:"numeric_values"` + DecimalValues []float64 `orm:"decimal_values"` + } + type User struct { + Id int `orm:"id"` + Passport string `orm:"passport"` + Password string `json:"password"` + NickName string `json:"nickname"` + CreateTime *gtime.Time `json:"create_time"` + Args + } + + tests := []struct { + name string + args Args + }{ + { + name: "nil", + args: Args{ + NumericValues: nil, + DecimalValues: nil, + }, + }, + { + name: "not nil", + args: Args{ + NumericValues: []float64{1.1, 2.2, 3.3}, + DecimalValues: []float64{1.1, 2.2, 3.3}, + }, + }, + { + name: "not empty", + args: Args{ + NumericValues: []float64{}, + DecimalValues: []float64{}, + }, + }, + } + now := gtime.New(CreateTime) + for i, tt := range tests { + gtest.C(t, func(t *gtest.T) { + user := User{ + Id: i + 1, + Passport: "", + Password: "", + NickName: "", + CreateTime: now, + Args: tt.args, + } + + _, err := db.Model(table).OmitNilData().Insert(user) + t.AssertNil(err) + var got Args + err = db.Model(table).Where("id", user.Id).Limit(1).Scan(&got) + t.AssertNil(err) + t.AssertEQ(tt.args, got) + }) + } +} diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index d5925efd2c8..f4260b0525c 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -787,28 +787,29 @@ const ( type LocalType string const ( - LocalTypeUndefined LocalType = "" - LocalTypeString LocalType = "string" - LocalTypeTime LocalType = "time" - LocalTypeDate LocalType = "date" - LocalTypeDatetime LocalType = "datetime" - LocalTypeInt LocalType = "int" - LocalTypeUint LocalType = "uint" - LocalTypeInt64 LocalType = "int64" - LocalTypeUint64 LocalType = "uint64" - LocalTypeBigInt LocalType = "bigint" - LocalTypeIntSlice LocalType = "[]int" - LocalTypeInt64Slice LocalType = "[]int64" - LocalTypeUint64Slice LocalType = "[]uint64" - LocalTypeStringSlice LocalType = "[]string" - LocalTypeInt64Bytes LocalType = "int64-bytes" - LocalTypeUint64Bytes LocalType = "uint64-bytes" - LocalTypeFloat32 LocalType = "float32" - LocalTypeFloat64 LocalType = "float64" - LocalTypeBytes LocalType = "[]byte" - LocalTypeBool LocalType = "bool" - LocalTypeJson LocalType = "json" - LocalTypeJsonb LocalType = "jsonb" + LocalTypeUndefined LocalType = "" + LocalTypeString LocalType = "string" + LocalTypeTime LocalType = "time" + LocalTypeDate LocalType = "date" + LocalTypeDatetime LocalType = "datetime" + LocalTypeInt LocalType = "int" + LocalTypeUint LocalType = "uint" + LocalTypeInt64 LocalType = "int64" + LocalTypeUint64 LocalType = "uint64" + LocalTypeBigInt LocalType = "bigint" + LocalTypeIntSlice LocalType = "[]int" + LocalTypeInt64Slice LocalType = "[]int64" + LocalTypeUint64Slice LocalType = "[]uint64" + LocalTypeStringSlice LocalType = "[]string" + LocalTypeFloat64Slice LocalType = "[]float64" + LocalTypeInt64Bytes LocalType = "int64-bytes" + LocalTypeUint64Bytes LocalType = "uint64-bytes" + LocalTypeFloat32 LocalType = "float32" + LocalTypeFloat64 LocalType = "float64" + LocalTypeBytes LocalType = "[]byte" + LocalTypeBool LocalType = "bool" + LocalTypeJson LocalType = "json" + LocalTypeJsonb LocalType = "jsonb" ) const (