diff --git a/callbacks/preload.go b/callbacks/preload.go index 3dd0dea3e..fd8214bb2 100644 --- a/callbacks/preload.go +++ b/callbacks/preload.go @@ -125,13 +125,15 @@ func preloadEntryPoint(db *gorm.DB, joins []string, relationships *schema.Relati case reflect.Slice, reflect.Array: if rv.Len() > 0 { reflectValue := rel.FieldSchema.MakeSlice().Elem() - reflectValue.SetLen(rv.Len()) for i := 0; i < rv.Len(); i++ { frv := rel.Field.ReflectValueOf(db.Statement.Context, rv.Index(i)) if frv.Kind() != reflect.Ptr { - reflectValue.Index(i).Set(frv.Addr()) + reflectValue = reflect.Append(reflectValue, frv.Addr()) } else { - reflectValue.Index(i).Set(frv) + if frv.IsNil() { + continue + } + reflectValue = reflect.Append(reflectValue, frv) } } diff --git a/schema/schema.go b/schema/schema.go index 3e7459ce7..57bab7fd1 100644 --- a/schema/schema.go +++ b/schema/schema.go @@ -67,9 +67,10 @@ func (schema Schema) String() string { } 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.Elem().Set(slice) + return results } diff --git a/tests/joins_test.go b/tests/joins_test.go index bcb60c88f..836e25732 100644 --- a/tests/joins_test.go +++ b/tests/joins_test.go @@ -1,10 +1,12 @@ package tests_test import ( + "fmt" "regexp" "sort" "testing" + "github.com/stretchr/testify/assert" "gorm.io/gorm" . "gorm.io/gorm/utils/tests" ) @@ -402,3 +404,75 @@ func TestNestedJoins(t *testing.T) { 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) +}