fix: many2many association with duplicate belongs to elem (#6206)

This commit is contained in:
bsmith-auth0 2023-04-10 20:06:13 -07:00 committed by GitHub
parent 05bb9d6106
commit ccc3cb758a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 51 additions and 6 deletions

View File

@ -51,25 +51,40 @@ func SaveBeforeAssociations(create bool) func(db *gorm.DB) {
} }
elems := reflect.MakeSlice(reflect.SliceOf(fieldType), 0, 10) elems := reflect.MakeSlice(reflect.SliceOf(fieldType), 0, 10)
distinctElems := reflect.MakeSlice(reflect.SliceOf(fieldType), 0, 10)
identityMap := map[string]bool{}
for i := 0; i < rValLen; i++ { for i := 0; i < rValLen; i++ {
obj := db.Statement.ReflectValue.Index(i) obj := db.Statement.ReflectValue.Index(i)
if reflect.Indirect(obj).Kind() != reflect.Struct { if reflect.Indirect(obj).Kind() != reflect.Struct {
break break
} }
if _, zero := rel.Field.ValueOf(db.Statement.Context, obj); !zero { // check belongs to relation value if _, zero := rel.Field.ValueOf(db.Statement.Context, obj); !zero { // check belongs to relation value
rv := rel.Field.ReflectValueOf(db.Statement.Context, obj) // relation reflect value rv := rel.Field.ReflectValueOf(db.Statement.Context, obj) // relation reflect value
if !isPtr {
rv = rv.Addr()
}
objs = append(objs, obj) objs = append(objs, obj)
if isPtr { elems = reflect.Append(elems, rv)
elems = reflect.Append(elems, rv)
} else { relPrimaryValues := make([]interface{}, 0, len(rel.FieldSchema.PrimaryFields))
elems = reflect.Append(elems, rv.Addr()) for _, pf := range rel.FieldSchema.PrimaryFields {
if pfv, ok := pf.ValueOf(db.Statement.Context, rv); !ok {
relPrimaryValues = append(relPrimaryValues, pfv)
}
}
cacheKey := utils.ToStringKey(relPrimaryValues...)
if len(relPrimaryValues) != len(rel.FieldSchema.PrimaryFields) || !identityMap[cacheKey] {
if cacheKey != "" { // has primary fields
identityMap[cacheKey] = true
}
distinctElems = reflect.Append(distinctElems, rv)
} }
} }
} }
if elems.Len() > 0 { if elems.Len() > 0 {
if saveAssociations(db, rel, elems, selectColumns, restricted, nil) == nil { if saveAssociations(db, rel, distinctElems, selectColumns, restricted, nil) == nil {
for i := 0; i < elems.Len(); i++ { for i := 0; i < elems.Len(); i++ {
setupReferences(objs[i], elems.Index(i)) setupReferences(objs[i], elems.Index(i))
} }

View File

@ -393,3 +393,33 @@ func TestConcurrentMany2ManyAssociation(t *testing.T) {
AssertEqual(t, err, nil) AssertEqual(t, err, nil)
AssertAssociationCount(t, find, "Languages", int64(count), "after concurrent append") AssertAssociationCount(t, find, "Languages", int64(count), "after concurrent append")
} }
func TestMany2ManyDuplicateBelongsToAssociation(t *testing.T) {
user1 := User{Name: "TestMany2ManyDuplicateBelongsToAssociation-1", Friends: []*User{
{Name: "TestMany2ManyDuplicateBelongsToAssociation-friend-1", Company: Company{
ID: 1,
Name: "Test-company-1",
}},
}}
user2 := User{Name: "TestMany2ManyDuplicateBelongsToAssociation-2", Friends: []*User{
{Name: "TestMany2ManyDuplicateBelongsToAssociation-friend-2", Company: Company{
ID: 1,
Name: "Test-company-1",
}},
}}
users := []*User{&user1, &user2}
var err error
err = DB.Session(&gorm.Session{FullSaveAssociations: true}).Save(users).Error
AssertEqual(t, nil, err)
var findUser1 User
err = DB.Preload("Friends.Company").Where("id = ?", user1.ID).First(&findUser1).Error
AssertEqual(t, nil, err)
AssertEqual(t, user1, findUser1)
var findUser2 User
err = DB.Preload("Friends.Company").Where("id = ?", user2.ID).First(&findUser2).Error
AssertEqual(t, nil, err)
AssertEqual(t, user2, findUser2)
}