From e9f25c73ee6afd560880db4537edf9ca24f2bc4a Mon Sep 17 00:00:00 2001 From: Cr <631807682@qq.com> Date: Fri, 10 Mar 2023 16:35:26 +0800 Subject: [PATCH 1/2] fix: on confilct with default null (#6129) * fix: on confilct with default null * Update create.go --------- Co-authored-by: Jinzhu --- callbacks/create.go | 4 +++- tests/create_test.go | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/callbacks/create.go b/callbacks/create.go index 0fe1dc93..f0b78139 100644 --- a/callbacks/create.go +++ b/callbacks/create.go @@ -3,6 +3,7 @@ package callbacks import ( "fmt" "reflect" + "strings" "gorm.io/gorm" "gorm.io/gorm/clause" @@ -302,7 +303,8 @@ func ConvertToCreateValues(stmt *gorm.Statement) (values clause.Values) { for _, column := range values.Columns { if field := stmt.Schema.LookUpField(column.Name); field != nil { if v, ok := selectColumns[field.DBName]; (ok && v) || (!ok && !restricted) { - if !field.PrimaryKey && (!field.HasDefaultValue || field.DefaultValueInterface != nil) && field.AutoCreateTime == 0 { + if !field.PrimaryKey && (!field.HasDefaultValue || field.DefaultValueInterface != nil || + strings.EqualFold(field.DefaultValue, "NULL")) && field.AutoCreateTime == 0 { if field.AutoUpdateTime > 0 { assignment := clause.Assignment{Column: clause.Column{Name: field.DBName}, Value: curTime} switch field.AutoUpdateTime { diff --git a/tests/create_test.go b/tests/create_test.go index 274a7f48..e8da91ff 100644 --- a/tests/create_test.go +++ b/tests/create_test.go @@ -547,3 +547,39 @@ func TestFirstOrCreateRowsAffected(t *testing.T) { t.Fatalf("first or create rows affect err:%v rows:%d", res.Error, res.RowsAffected) } } + +func TestCreateOnConfilctWithDefalutNull(t *testing.T) { + type OnConfilctUser struct { + ID string + Name string `gorm:"default:null"` + Email string + Mobile string `gorm:"default:'133xxxx'"` + } + + err := DB.Migrator().DropTable(&OnConfilctUser{}) + AssertEqual(t, err, nil) + err = DB.AutoMigrate(&OnConfilctUser{}) + AssertEqual(t, err, nil) + + u := OnConfilctUser{ + ID: "on-confilct-user-id", + Name: "on-confilct-user-name", + Email: "on-confilct-user-email", + Mobile: "on-confilct-user-mobile", + } + err = DB.Create(&u).Error + AssertEqual(t, err, nil) + + u.Name = "on-confilct-user-name-2" + u.Email = "on-confilct-user-email-2" + u.Mobile = "" + err = DB.Clauses(clause.OnConflict{UpdateAll: true}).Create(&u).Error + AssertEqual(t, err, nil) + + var u2 OnConfilctUser + err = DB.Where("id = ?", u.ID).First(&u2).Error + AssertEqual(t, err, nil) + AssertEqual(t, u2.Name, "on-confilct-user-name-2") + AssertEqual(t, u2.Email, "on-confilct-user-email-2") + AssertEqual(t, u2.Mobile, "133xxxx") +} From 1643a36260cbc5bcc6e4abab6489325b64c57e7a Mon Sep 17 00:00:00 2001 From: Jinzhu Date: Fri, 10 Mar 2023 10:48:14 +0800 Subject: [PATCH 2/2] Fix possible concurrency problem for serializer --- schema/field.go | 13 ++++++++++--- tests/go.mod | 3 ++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/schema/field.go b/schema/field.go index 59151878..00beb067 100644 --- a/schema/field.go +++ b/schema/field.go @@ -916,6 +916,8 @@ func (field *Field) setupValuerAndSetter() { sameElemType = field.FieldType == reflect.ValueOf(field.Serializer).Type().Elem() } + serializerValue := reflect.Indirect(reflect.ValueOf(field.Serializer)) + serializerType := serializerValue.Type() field.Set = func(ctx context.Context, value reflect.Value, v interface{}) (err error) { if s, ok := v.(*serializer); ok { if s.fieldValue != nil { @@ -923,11 +925,12 @@ func (field *Field) setupValuerAndSetter() { } else if err = s.Serializer.Scan(ctx, field, value, s.value); err == nil { if sameElemType { field.ReflectValueOf(ctx, value).Set(reflect.ValueOf(s.Serializer).Elem()) - s.Serializer = reflect.New(reflect.Indirect(reflect.ValueOf(field.Serializer)).Type()).Interface().(SerializerInterface) } else if sameType { field.ReflectValueOf(ctx, value).Set(reflect.ValueOf(s.Serializer)) - s.Serializer = reflect.New(reflect.Indirect(reflect.ValueOf(field.Serializer)).Type()).Interface().(SerializerInterface) } + si := reflect.New(serializerType) + si.Elem().Set(serializerValue) + s.Serializer = si.Interface().(SerializerInterface) } } else { err = oldFieldSetter(ctx, value, v) @@ -939,11 +942,15 @@ func (field *Field) setupValuerAndSetter() { func (field *Field) setupNewValuePool() { if field.Serializer != nil { + serializerValue := reflect.Indirect(reflect.ValueOf(field.Serializer)) + serializerType := serializerValue.Type() field.NewValuePool = &sync.Pool{ New: func() interface{} { + si := reflect.New(serializerType) + si.Elem().Set(serializerValue) return &serializer{ Field: field, - Serializer: field.Serializer, + Serializer: si.Interface().(SerializerInterface), } }, } diff --git a/tests/go.mod b/tests/go.mod index b2d5ca97..e970c9f5 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -9,11 +9,12 @@ require ( github.com/lib/pq v1.10.7 github.com/mattn/go-sqlite3 v1.14.16 // indirect github.com/microsoft/go-mssqldb v0.20.0 // indirect + golang.org/x/crypto v0.7.0 // indirect gorm.io/driver/mysql v1.4.7 gorm.io/driver/postgres v1.4.8 gorm.io/driver/sqlite v1.4.4 gorm.io/driver/sqlserver v1.4.2 - gorm.io/gorm v1.24.5 + gorm.io/gorm v1.24.6 ) replace gorm.io/gorm => ../