fix: use reflect.Append when preloading nested associations (#7014)

Co-authored-by: Emilien Kofman <emilien.kofman@miimosa.com>
This commit is contained in:
Emilien 2024-06-12 12:52:33 +02:00 committed by GitHub
parent 78c6dfd712
commit 05167fd591
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 81 additions and 4 deletions

View File

@ -125,13 +125,15 @@ func preloadEntryPoint(db *gorm.DB, joins []string, relationships *schema.Relati
case reflect.Slice, reflect.Array: case reflect.Slice, reflect.Array:
if rv.Len() > 0 { if rv.Len() > 0 {
reflectValue := rel.FieldSchema.MakeSlice().Elem() reflectValue := rel.FieldSchema.MakeSlice().Elem()
reflectValue.SetLen(rv.Len())
for i := 0; i < rv.Len(); i++ { for i := 0; i < rv.Len(); i++ {
frv := rel.Field.ReflectValueOf(db.Statement.Context, rv.Index(i)) frv := rel.Field.ReflectValueOf(db.Statement.Context, rv.Index(i))
if frv.Kind() != reflect.Ptr { if frv.Kind() != reflect.Ptr {
reflectValue.Index(i).Set(frv.Addr()) reflectValue = reflect.Append(reflectValue, frv.Addr())
} else { } else {
reflectValue.Index(i).Set(frv) if frv.IsNil() {
continue
}
reflectValue = reflect.Append(reflectValue, frv)
} }
} }

View File

@ -67,9 +67,10 @@ func (schema Schema) String() string {
} }
func (schema Schema) MakeSlice() reflect.Value { func (schema Schema) MakeSlice() reflect.Value {
slice := reflect.MakeSlice(reflect.SliceOf(reflect.PtrTo(schema.ModelType)), 0, 20) slice := reflect.MakeSlice(reflect.SliceOf(reflect.PointerTo(schema.ModelType)), 0, 20)
results := reflect.New(slice.Type()) results := reflect.New(slice.Type())
results.Elem().Set(slice) results.Elem().Set(slice)
return results return results
} }

View File

@ -1,10 +1,12 @@
package tests_test package tests_test
import ( import (
"fmt"
"regexp" "regexp"
"sort" "sort"
"testing" "testing"
"github.com/stretchr/testify/assert"
"gorm.io/gorm" "gorm.io/gorm"
. "gorm.io/gorm/utils/tests" . "gorm.io/gorm/utils/tests"
) )
@ -402,3 +404,75 @@ func TestNestedJoins(t *testing.T) {
CheckPet(t, *user.Manager.NamedPet, *users2[idx].Manager.NamedPet) CheckPet(t, *user.Manager.NamedPet, *users2[idx].Manager.NamedPet)
} }
} }
func TestJoinsPreload_Issue7013(t *testing.T) {
manager := &User{Name: "Manager"}
DB.Create(manager)
var userIDs []uint
for i := 0; i < 21; i++ {
user := &User{Name: fmt.Sprintf("User%d", i), ManagerID: &manager.ID}
DB.Create(user)
userIDs = append(userIDs, user.ID)
}
var entries []User
assert.NotPanics(t, func() {
assert.NoError(t,
DB.Debug().Preload("Manager.Team").
Joins("Manager.Company").
Find(&entries).Error)
})
}
func TestJoinsPreload_Issue7013_RelationEmpty(t *testing.T) {
type (
Furniture struct {
gorm.Model
OwnerID *uint
}
Owner struct {
gorm.Model
Furnitures []Furniture
CompanyID *uint
Company Company
}
Building struct {
gorm.Model
Name string
OwnerID *uint
Owner Owner
}
)
DB.Migrator().DropTable(&Building{}, &Owner{}, &Furniture{})
DB.Migrator().AutoMigrate(&Building{}, &Owner{}, &Furniture{})
home := &Building{Name: "relation_empty"}
DB.Create(home)
var entries []Building
assert.NotPanics(t, func() {
assert.NoError(t,
DB.Debug().Preload("Owner.Furnitures").
Joins("Owner.Company").
Find(&entries).Error)
})
AssertEqual(t, entries, []Building{{Model: home.Model, Name: "relation_empty", Owner: Owner{Company: Company{}}}})
}
func TestJoinsPreload_Issue7013_NoEntries(t *testing.T) {
var entries []User
assert.NotPanics(t, func() {
assert.NoError(t,
DB.Debug().Preload("Manager.Team").
Joins("Manager.Company").
Where("false").
Find(&entries).Error)
})
AssertEqual(t, len(entries), 0)
}