Skip to content

Commit 05167fd

Browse files
emilienkofmanEmilien Kofman
andauthored
fix: use reflect.Append when preloading nested associations (#7014)
Co-authored-by: Emilien Kofman <[email protected]>
1 parent 78c6dfd commit 05167fd

File tree

3 files changed

+81
-4
lines changed

3 files changed

+81
-4
lines changed

callbacks/preload.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,15 @@ func preloadEntryPoint(db *gorm.DB, joins []string, relationships *schema.Relati
125125
case reflect.Slice, reflect.Array:
126126
if rv.Len() > 0 {
127127
reflectValue := rel.FieldSchema.MakeSlice().Elem()
128-
reflectValue.SetLen(rv.Len())
129128
for i := 0; i < rv.Len(); i++ {
130129
frv := rel.Field.ReflectValueOf(db.Statement.Context, rv.Index(i))
131130
if frv.Kind() != reflect.Ptr {
132-
reflectValue.Index(i).Set(frv.Addr())
131+
reflectValue = reflect.Append(reflectValue, frv.Addr())
133132
} else {
134-
reflectValue.Index(i).Set(frv)
133+
if frv.IsNil() {
134+
continue
135+
}
136+
reflectValue = reflect.Append(reflectValue, frv)
135137
}
136138
}
137139

schema/schema.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,10 @@ func (schema Schema) String() string {
6767
}
6868

6969
func (schema Schema) MakeSlice() reflect.Value {
70-
slice := reflect.MakeSlice(reflect.SliceOf(reflect.PtrTo(schema.ModelType)), 0, 20)
70+
slice := reflect.MakeSlice(reflect.SliceOf(reflect.PointerTo(schema.ModelType)), 0, 20)
7171
results := reflect.New(slice.Type())
7272
results.Elem().Set(slice)
73+
7374
return results
7475
}
7576

tests/joins_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package tests_test
22

33
import (
4+
"fmt"
45
"regexp"
56
"sort"
67
"testing"
78

9+
"github.com/stretchr/testify/assert"
810
"gorm.io/gorm"
911
. "gorm.io/gorm/utils/tests"
1012
)
@@ -402,3 +404,75 @@ func TestNestedJoins(t *testing.T) {
402404
CheckPet(t, *user.Manager.NamedPet, *users2[idx].Manager.NamedPet)
403405
}
404406
}
407+
408+
func TestJoinsPreload_Issue7013(t *testing.T) {
409+
manager := &User{Name: "Manager"}
410+
DB.Create(manager)
411+
412+
var userIDs []uint
413+
for i := 0; i < 21; i++ {
414+
user := &User{Name: fmt.Sprintf("User%d", i), ManagerID: &manager.ID}
415+
DB.Create(user)
416+
userIDs = append(userIDs, user.ID)
417+
}
418+
419+
var entries []User
420+
assert.NotPanics(t, func() {
421+
assert.NoError(t,
422+
DB.Debug().Preload("Manager.Team").
423+
Joins("Manager.Company").
424+
Find(&entries).Error)
425+
})
426+
}
427+
428+
func TestJoinsPreload_Issue7013_RelationEmpty(t *testing.T) {
429+
type (
430+
Furniture struct {
431+
gorm.Model
432+
OwnerID *uint
433+
}
434+
435+
Owner struct {
436+
gorm.Model
437+
Furnitures []Furniture
438+
CompanyID *uint
439+
Company Company
440+
}
441+
442+
Building struct {
443+
gorm.Model
444+
Name string
445+
OwnerID *uint
446+
Owner Owner
447+
}
448+
)
449+
450+
DB.Migrator().DropTable(&Building{}, &Owner{}, &Furniture{})
451+
DB.Migrator().AutoMigrate(&Building{}, &Owner{}, &Furniture{})
452+
453+
home := &Building{Name: "relation_empty"}
454+
DB.Create(home)
455+
456+
var entries []Building
457+
assert.NotPanics(t, func() {
458+
assert.NoError(t,
459+
DB.Debug().Preload("Owner.Furnitures").
460+
Joins("Owner.Company").
461+
Find(&entries).Error)
462+
})
463+
464+
AssertEqual(t, entries, []Building{{Model: home.Model, Name: "relation_empty", Owner: Owner{Company: Company{}}}})
465+
}
466+
467+
func TestJoinsPreload_Issue7013_NoEntries(t *testing.T) {
468+
var entries []User
469+
assert.NotPanics(t, func() {
470+
assert.NoError(t,
471+
DB.Debug().Preload("Manager.Team").
472+
Joins("Manager.Company").
473+
Where("false").
474+
Find(&entries).Error)
475+
})
476+
477+
AssertEqual(t, len(entries), 0)
478+
}

0 commit comments

Comments
 (0)