GORM Playground Link

https://github.com/go-gorm/playground/pull/688

Description

suppose we have the following data structures

type Repo struct {
    ID     uint   `gorm:"primarykey"`
    URL    string `json:"RepoURL"`
    Status string `json:"RepoStatus"`
}

type DispatchRecord struct {
    ID                   uint `gorm:"primarykey"`
    PlaybookURL          string
    PlaybookDispatcherID string
}

type UpdateTransaction struct {
    ID              uint `gorm:"primarykey"`
    RepoID          *uint
    Repo            *Repo
    DispatchRecords []DispatchRecord `gorm:"many2many:updatetransaction_dispatchrecords"`
}

type DeviceUpdate struct {
    ID       uint `gorm:"primarykey"`
    Name     string
    UpdateID *uint
    Update   *UpdateTransaction
}

Panic Occurs when Mixing Joins and Preload with the same field eg query like

DB.Joins("Update.Repo").Joins("Update").Preload("Update.DispatchRecords").Find(&deviceUpdates)

will panic with

panic: reflect: call of reflect.Value.Field on slice Value [recovered]
        panic: reflect: call of reflect.Value.Field on slice Value

goroutine 15 [running]:
testing.tRunner.func1.2({0xc50dc0, 0xc000013e78})
        /home/djlezzou/bin/go/src/testing/testing.go:1526 +0x24e
testing.tRunner.func1()
        /home/djlezzou/bin/go/src/testing/testing.go:1529 +0x39f
panic({0xc50dc0, 0xc000013e78})
        /home/djlezzou/bin/go/src/runtime/panic.go:884 +0x213
reflect.Value.Field({0xc1db00?, 0xc000012798?, 0xc0004e81a8?}, 0xc00001f970?)
        /home/djlezzou/bin/go/src/reflect/value.go:1268 +0xe5
gorm.io/gorm/schema.(*Field).setupValuerAndSetter.func4({0xd3e90f?, 0x6?}, {0xc1db00?, 0xc000012798?, 0x10?})
        /home/djlezzou/projects/gorm/playground/gorm/schema/field.go:503 +0x65
gorm.io/gorm/callbacks.preloadEntryPoint(0xc0001845d0, {0xc00001f970, 0x1, 0x1}, 0xc0004b7748, 0xc000012798?, {0x0, 0x0, 0x0})
        /home/djlezzou/projects/gorm/playground/gorm/callbacks/preload.go:124 +0x429
gorm.io/gorm/callbacks.Preload(0xc000023b90)
        /home/djlezzou/projects/gorm/playground/gorm/callbacks/query.go:283 +0x2e5
gorm.io/gorm.(*processor).Execute(0xc0002508c0, 0xc00025c360?)
        /home/djlezzou/projects/gorm/playground/gorm/callbacks.go:130 +0x3ce
gorm.io/gorm.(*DB).Find(0x1221520?, {0xc04c60?, 0xc000012798}, {0x0, 0x0, 0x0})
        /home/djlezzou/projects/gorm/playground/gorm/finisher_api.go:170 +0x137
gorm.io/playground.TestGORM1(0x0?)
        /home/djlezzou/projects/gorm/playground/main_test.go:72 +0x4ad
testing.tRunner(0xc000583380, 0xd97258)
        /home/djlezzou/bin/go/src/testing/testing.go:1576 +0x10b
created by testing.(*T).Run
        /home/djlezzou/bin/go/src/testing/testing.go:1629 +0x3ea

with version v1.25.6 all the tests passed

this has been discovered with dependency PR in our project https://github.com/RedHatInsights/edge-api/pull/2406

Comment From: ldjebran

I think I found the problem

Used the same test used in unit tests in PR https://github.com/go-gorm/gorm/pull/6771 The unit tests are only using First with var find1 Value

But when using Find with type var find1 []Value it will panic eg :

type (
    Preload struct {
        ID       uint
        Value    string
        NestedID uint
    }
    Join struct {
        ID       uint
        Value    string
        NestedID uint
    }
    Nested struct {
        ID       uint
        Preloads []*Preload
        Join     Join
        ValueID  uint
    }
    Value struct {
        ID     uint
        Name   string
        Nested Nested
    }
)

func TestJoinsPreloads(t *testing.T) {
    _ = DB.AutoMigrate(&Preload{}, &Join{}, &Nested{}, &Value{})

    value := Value{
        Name: "value",
        Nested: Nested{
            Preloads: []*Preload{
                {Value: "p1"}, {Value: "p2"},
            },
            Join: Join{Value: "j1"},
        },
    }
    if err := DB.Create(&value).Error; err != nil {
        t.Errorf("failed to create value, got err: %v", err)
    }

    var err error

    // this will succeed
    var find Value
    err = DB.Joins("Nested").Joins("Nested.Join").Preload("Nested.Preloads").First(&find).Error
    if err != nil {
        t.Errorf("Failed, got error: %v", err)
    }

    // this will panic
    var values []Value
    err = DB.Joins("Nested").Joins("Nested.Join").Preload("Nested.Preloads").Find(&values).Error
    if err != nil {
        t.Errorf("Failed, got error: %v", err)
    }
}

the second query will panic

var values []Value
    err = DB.Joins("Nested").Joins("Nested.Join").Preload("Nested.Preloads").Find(&values).Error
    if err != nil {
        t.Errorf("Failed, got error: %v", err)
    }

The unit tests in that PR was not using Find with slice , but only First with pointer to a struct

Comment From: System-Glitch

Experiencing the same issue. Debugging revealed that the db.Statement.ReflectValue here appears to be of Kind reflect.Slice despite my relationship being a simple struct pointer.

Comment From: VetSoftKeith

I have this exact same issue. Upgrading to 1.25.7 completely breaks my entire application consisting of a dozen+ microservices all using gorm.

Comment From: a631807682

I haven't investigated it, but it seems from the description that it may have something to do with https://github.com/go-gorm/gorm/pull/6771. Do you have time to take a look? @black-06

Comment From: black-06

I haven't investigated it, but it seems from the description that it may have something to do with #6771. Do you have time to take a look? @black-06

Okay...

Comment From: DAcodedBEAT

Hello @go-gorm maintainers 👋 do you know when this fix will be released?

I cannot test this with https://github.com/go-gorm/postgres/releases/tag/v1.5.7 because m.GuessConstraintInterfaceAndTable undefined in <= v1.25.6

Comment From: VetSoftKeith

@DAcodedBEAT looks like it was released in 1.25.8