Skip to content

Commit

Permalink
fix: use reflect.Append when preloading nested associations (#7014)
Browse files Browse the repository at this point in the history
Co-authored-by: Emilien Kofman <emilien.kofman@miimosa.com>
  • Loading branch information
emilienkofman and Emilien Kofman authored Jun 12, 2024
1 parent 78c6dfd commit 05167fd
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 4 deletions.
8 changes: 5 additions & 3 deletions callbacks/preload.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}

Expand Down
3 changes: 2 additions & 1 deletion schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
74 changes: 74 additions & 0 deletions tests/joins_test.go
Original file line number Diff line number Diff line change
@@ -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"
)
Expand Down Expand Up @@ -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)
}

0 comments on commit 05167fd

Please sign in to comment.