gorm icon indicating copy to clipboard operation
gorm copied to clipboard

Joins and Preload Regression with gorm version 1.25.7

Open ldjebran opened this issue 1 year ago • 1 comments

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

ldjebran avatar Feb 09 '24 09:02 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

ldjebran avatar Feb 09 '24 09:02 ldjebran

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.

System-Glitch avatar Feb 23 '24 14:02 System-Glitch

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

VetSoftKeith avatar Feb 24 '24 22:02 VetSoftKeith

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

a631807682 avatar Feb 26 '24 03:02 a631807682

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...

black-06 avatar Feb 26 '24 03:02 black-06

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

DAcodedBEAT avatar Mar 11 '24 14:03 DAcodedBEAT

@DAcodedBEAT looks like it was released in 1.25.8

VetSoftKeith avatar Mar 18 '24 16:03 VetSoftKeith