fix: Assigning nil value to *uuid.UUID field in Updates
This PR adds handling for the assignment of a nil value to a *uuid.UUID field (resolved as a reflect.Ptr to a reflect.Array), to ensure that the model object reflects the correct value after Updates() has completed. This PR also adds few supporting test cases for Updates() with a map and uuid.UUID column.
This commit is contained in:
parent
a9d27293de
commit
debc5f179e
@ -896,7 +896,9 @@ func (field *Field) setupValuerAndSetter() {
|
|||||||
if !reflectV.IsValid() {
|
if !reflectV.IsValid() {
|
||||||
field.ReflectValueOf(ctx, value).Set(reflect.New(field.FieldType).Elem())
|
field.ReflectValueOf(ctx, value).Set(reflect.New(field.FieldType).Elem())
|
||||||
} else if reflectV.Kind() == reflect.Ptr && reflectV.IsNil() {
|
} else if reflectV.Kind() == reflect.Ptr && reflectV.IsNil() {
|
||||||
return
|
if field.FieldType.Elem().Kind() == reflect.Array && field.OwnerSchema == nil {
|
||||||
|
field.ReflectValueOf(ctx, value).Set(reflectV)
|
||||||
|
}
|
||||||
} else if reflectV.Type().AssignableTo(field.FieldType) {
|
} else if reflectV.Type().AssignableTo(field.FieldType) {
|
||||||
field.ReflectValueOf(ctx, value).Set(reflectV)
|
field.ReflectValueOf(ctx, value).Set(reflectV)
|
||||||
} else if reflectV.Kind() == reflect.Ptr {
|
} else if reflectV.Kind() == reflect.Ptr {
|
||||||
|
@ -101,12 +101,12 @@ func TestConnPoolWrapper(t *testing.T) {
|
|||||||
db: nativeDB,
|
db: nativeDB,
|
||||||
expect: []string{
|
expect: []string{
|
||||||
"SELECT VERSION()",
|
"SELECT VERSION()",
|
||||||
"INSERT INTO `users` (`created_at`,`updated_at`,`deleted_at`,`name`,`age`,`birthday`,`company_id`,`manager_id`,`active`) VALUES (?,?,?,?,?,?,?,?,?)",
|
"INSERT INTO `users` (`created_at`,`updated_at`,`deleted_at`,`name`,`age`,`birthday`,`company_id`,`manager_id`,`active`,`user_uuid`) VALUES (?,?,?,?,?,?,?,?,?,?)",
|
||||||
"SELECT * FROM `users` WHERE name = ? AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT ?",
|
"SELECT * FROM `users` WHERE name = ? AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT ?",
|
||||||
"INSERT INTO `users` (`created_at`,`updated_at`,`deleted_at`,`name`,`age`,`birthday`,`company_id`,`manager_id`,`active`) VALUES (?,?,?,?,?,?,?,?,?)",
|
"INSERT INTO `users` (`created_at`,`updated_at`,`deleted_at`,`name`,`age`,`birthday`,`company_id`,`manager_id`,`active`,`user_uuid`) VALUES (?,?,?,?,?,?,?,?,?,?)",
|
||||||
"SELECT * FROM `users` WHERE name = ? AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT ?",
|
"SELECT * FROM `users` WHERE name = ? AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT ?",
|
||||||
"SELECT * FROM `users` WHERE name = ? AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT ?",
|
"SELECT * FROM `users` WHERE name = ? AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT ?",
|
||||||
"INSERT INTO `users` (`created_at`,`updated_at`,`deleted_at`,`name`,`age`,`birthday`,`company_id`,`manager_id`,`active`) VALUES (?,?,?,?,?,?,?,?,?)",
|
"INSERT INTO `users` (`created_at`,`updated_at`,`deleted_at`,`name`,`age`,`birthday`,`company_id`,`manager_id`,`active`,`user_uuid`) VALUES (?,?,?,?,?,?,?,?,?,?)",
|
||||||
"SELECT * FROM `users` WHERE name = ? AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT ?",
|
"SELECT * FROM `users` WHERE name = ? AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT ?",
|
||||||
"SELECT * FROM `users` WHERE name = ? AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT ?",
|
"SELECT * FROM `users` WHERE name = ? AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT ?",
|
||||||
},
|
},
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
. "gorm.io/gorm/utils/tests"
|
. "gorm.io/gorm/utils/tests"
|
||||||
)
|
)
|
||||||
@ -114,6 +115,7 @@ func TestEmbeddedPointerTypeStruct(t *testing.T) {
|
|||||||
ContentPtr *Content
|
ContentPtr *Content
|
||||||
Birthday time.Time
|
Birthday time.Time
|
||||||
BirthdayPtr *time.Time
|
BirthdayPtr *time.Time
|
||||||
|
AuthorUUID *uuid.UUID
|
||||||
}
|
}
|
||||||
|
|
||||||
type HNPost struct {
|
type HNPost struct {
|
||||||
|
@ -7,6 +7,7 @@ require (
|
|||||||
github.com/jinzhu/now v1.1.5
|
github.com/jinzhu/now v1.1.5
|
||||||
github.com/lib/pq v1.10.9
|
github.com/lib/pq v1.10.9
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.9.0
|
||||||
|
gorm.io/datatypes v1.2.2
|
||||||
gorm.io/driver/mysql v1.5.7
|
gorm.io/driver/mysql v1.5.7
|
||||||
gorm.io/driver/postgres v1.5.10
|
gorm.io/driver/postgres v1.5.10
|
||||||
gorm.io/driver/sqlite v1.5.6
|
gorm.io/driver/sqlite v1.5.6
|
||||||
|
@ -158,7 +158,7 @@ func TestDryRun(t *testing.T) {
|
|||||||
dryRunDB := DB.Session(&gorm.Session{DryRun: true})
|
dryRunDB := DB.Session(&gorm.Session{DryRun: true})
|
||||||
|
|
||||||
stmt := dryRunDB.Create(&user).Statement
|
stmt := dryRunDB.Create(&user).Statement
|
||||||
if stmt.SQL.String() == "" || len(stmt.Vars) != 9 {
|
if stmt.SQL.String() == "" || len(stmt.Vars) != 10 {
|
||||||
t.Errorf("Failed to generate sql, got %v", stmt.SQL.String())
|
t.Errorf("Failed to generate sql, got %v", stmt.SQL.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -403,7 +403,7 @@ func TestToSQL(t *testing.T) {
|
|||||||
sql = DB.ToSQL(func(tx *gorm.DB) *gorm.DB {
|
sql = DB.ToSQL(func(tx *gorm.DB) *gorm.DB {
|
||||||
return tx.Model(&User{}).Create(user)
|
return tx.Model(&User{}).Create(user)
|
||||||
})
|
})
|
||||||
assertEqualSQL(t, `INSERT INTO "users" ("created_at","updated_at","deleted_at","name","age","birthday","company_id","manager_id","active") VALUES ('2021-10-18 00:00:00','2021-10-18 00:00:00',NULL,'foo',20,NULL,NULL,NULL,false) RETURNING "id"`, sql)
|
assertEqualSQL(t, `INSERT INTO "users" ("created_at","updated_at","deleted_at","name","age","birthday","company_id","manager_id","active","user_uuid") VALUES ('2021-10-18 00:00:00','2021-10-18 00:00:00',NULL,'foo',20,NULL,NULL,NULL,false,NULL) RETURNING "id"`, sql)
|
||||||
|
|
||||||
// save
|
// save
|
||||||
user = &User{Name: "foo", Age: 20}
|
user = &User{Name: "foo", Age: 20}
|
||||||
@ -412,7 +412,7 @@ func TestToSQL(t *testing.T) {
|
|||||||
sql = DB.ToSQL(func(tx *gorm.DB) *gorm.DB {
|
sql = DB.ToSQL(func(tx *gorm.DB) *gorm.DB {
|
||||||
return tx.Model(&User{}).Save(user)
|
return tx.Model(&User{}).Save(user)
|
||||||
})
|
})
|
||||||
assertEqualSQL(t, `INSERT INTO "users" ("created_at","updated_at","deleted_at","name","age","birthday","company_id","manager_id","active") VALUES ('2021-10-18 00:00:00','2021-10-18 00:00:00',NULL,'foo',20,NULL,NULL,NULL,false) RETURNING "id"`, sql)
|
assertEqualSQL(t, `INSERT INTO "users" ("created_at","updated_at","deleted_at","name","age","birthday","company_id","manager_id","active","user_uuid") VALUES ('2021-10-18 00:00:00','2021-10-18 00:00:00',NULL,'foo',20,NULL,NULL,NULL,false,NULL) RETURNING "id"`, sql)
|
||||||
|
|
||||||
// updates
|
// updates
|
||||||
user = &User{Name: "bar", Age: 22}
|
user = &User{Name: "bar", Age: 22}
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/datatypes"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"gorm.io/gorm/clause"
|
"gorm.io/gorm/clause"
|
||||||
"gorm.io/gorm/utils"
|
"gorm.io/gorm/utils"
|
||||||
@ -183,6 +184,38 @@ func TestUpdates(t *testing.T) {
|
|||||||
|
|
||||||
user3.Age += 100
|
user3.Age += 100
|
||||||
AssertObjEqual(t, user4, user3, "UpdatedAt", "Age")
|
AssertObjEqual(t, user4, user3, "UpdatedAt", "Age")
|
||||||
|
|
||||||
|
// Updates() with map and datatypes.UUID - Case 1 - Update with UUID value
|
||||||
|
uuidVal := datatypes.NewUUIDv4()
|
||||||
|
tx := DB.Model(&user4)
|
||||||
|
uuidErr := tx.Updates(map[string]interface{}{"user_uuid": uuidVal}).Error
|
||||||
|
if uuidErr != nil {
|
||||||
|
t.Errorf("No error should occur while updating with UUID value, but got %v", uuidErr)
|
||||||
|
}
|
||||||
|
// Expecting the model object (user4) to reflect the UUID value assignment.
|
||||||
|
AssertEqual(t, user4.UserUUID, uuidVal)
|
||||||
|
|
||||||
|
// Updates() with map and datatypes.UUID - Case 2 - Update with UUID nil pointer
|
||||||
|
var nilUUIDPtr *datatypes.UUID = nil
|
||||||
|
uuidErr = tx.Updates(map[string]interface{}{"user_uuid": nilUUIDPtr}).Error
|
||||||
|
if uuidErr != nil {
|
||||||
|
t.Errorf("No error should occur while updating with nil UUID pointer, but got %v", uuidErr)
|
||||||
|
}
|
||||||
|
// Expecting the model object (user4) to reflect the UUID nil pointer assignment.
|
||||||
|
AssertEqual(t, user4.UserUUID, nilUUIDPtr)
|
||||||
|
|
||||||
|
// Updates() with map and datatypes.UUID - Case 3 - Update with a non-nil UUID pointer
|
||||||
|
uuidVal2 := datatypes.NewUUIDv1()
|
||||||
|
if uuidErr != nil {
|
||||||
|
t.Errorf("No error should occur while generating UUID, but got %v", uuidErr)
|
||||||
|
}
|
||||||
|
var nonNilUUIDPtr *datatypes.UUID = &uuidVal2
|
||||||
|
uuidErr = tx.Updates(map[string]interface{}{"user_uuid": nonNilUUIDPtr}).Error
|
||||||
|
if uuidErr != nil {
|
||||||
|
t.Errorf("No error should occur while updating with non-nil UUID pointer, but got %v", uuidErr)
|
||||||
|
}
|
||||||
|
// Expecting the model object (user4) to reflect the non-nil UUID pointer assignment.
|
||||||
|
AssertEqual(t, user4.UserUUID, nonNilUUIDPtr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpdateColumn(t *testing.T) {
|
func TestUpdateColumn(t *testing.T) {
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/datatypes"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -30,6 +31,7 @@ type User struct {
|
|||||||
Languages []Language `gorm:"many2many:UserSpeak;"`
|
Languages []Language `gorm:"many2many:UserSpeak;"`
|
||||||
Friends []*User `gorm:"many2many:user_friends;"`
|
Friends []*User `gorm:"many2many:user_friends;"`
|
||||||
Active bool
|
Active bool
|
||||||
|
UserUUID *datatypes.UUID
|
||||||
}
|
}
|
||||||
|
|
||||||
type Account struct {
|
type Account struct {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user