Skip to content

Preload panics if at least one of parent model field's value is NULL  #6016

@shtrih

Description

@shtrih

GORM Playground Link

go-gorm/playground#537

Description

User and UserProp models are saved independently in different API endpoints so they are not connected by a foreign key. But in another API endpoint they should load together so used Preload("UserProps") with multiple foreign keys to achieve this.

type Company struct {
	ID   int
	Name string
}

type User struct {
	gorm.Model
	Name      string
	CompanyID *int
	Company   Company
	ManagerID *uint
	Manager   *User
	UserProps *UserProp `gorm:"foreignkey:CompanyID,ManagerID;references:CompanyID,ManagerID"`
}

type UserProp struct {
	ID        int
	CompanyID int
	ManagerID uint
	Value     string
}

Expected

UserProp and User connects by CompanyID and ManagerID. Since those fields are nullable in User model it leads to User.UserProps emptiness.

	company := &Company{Name: "ACME"}
	DB.Create(company)

	user := &User{
		Name:      "manager",
		CompanyID: &company.ID,
		// have no manager
		ManagerID: nil,
	}
	DB.Create(user)

	var resultUser User
	DB.Preload("UserProps").First(&resultUser, user.ID)

So, the following SQL is expected to be generated (will return 0 rows, and that's the idea: no panics):

 SELECT * FROM "user_props" WHERE ("user_props"."company_id","user_props"."manager_id") IN ((1,NULL))

Actual

We got panic in case of one of values is NULL:

2022/10/24 09:42:39 testing postgres...
=== RUN   TestGORM

2022/10/24 09:42:39 /mnt/c/Users/crab/Go Projects/gorm-playground/main_test.go:13
[3.820ms] [rows:1] INSERT INTO "companies" ("name") VALUES ('ACME') RETURNING "id"

2022/10/24 09:42:39 /mnt/c/Users/crab/Go Projects/gorm-playground/main_test.go:21
[2.940ms] [rows:1] INSERT INTO "users" ("created_at","updated_at","deleted_at","name","company_id","manager_id") VALUES ('2022-10-24 09:42:39.32','2022-10-24 09:42:39.32',NULL,'manager',1,NULL) RETURNING "id"

2022/10/24 09:42:39 /mnt/c/Users/crab/Go Projects/gorm-playground/main_test.go:28
[2.340ms] [rows:1] INSERT INTO "users" ("created_at","updated_at","deleted_at","name","company_id","manager_id") VALUES ('2022-10-24 09:42:39.324','2022-10-24 09:42:39.324',NULL,'jinzhu',1,1) RETURNING "id"

2022/10/24 09:42:39 /mnt/c/Users/crab/Go Projects/gorm-playground/main_test.go:37
[2.806ms] [rows:1] INSERT INTO "user_props" ("company_id","manager_id","value") VALUES (1,1,'foo') RETURNING "id"
=== RUN   TestGORM/user_has_props

2022/10/24 09:42:39 /mnt/c/Users/crab/Go Projects/gorm-playground/main_test.go:46
[0.578ms] [rows:1] SELECT * FROM "user_props" WHERE ("user_props"."company_id","user_props"."manager_id") IN ((1,1))

2022/10/24 09:42:39 /mnt/c/Users/crab/Go Projects/gorm-playground/main_test.go:46
[1.575ms] [rows:1] SELECT * FROM "users" WHERE "users"."id" = 2 AND "users"."deleted_at" IS NULL ORDER BY "users"."id" LIMIT 1
=== RUN   TestGORM/user_without_props
--- FAIL: TestGORM (0.02s)
    --- PASS: TestGORM/user_has_props (0.00s)
    --- FAIL: TestGORM/user_without_props (0.00s)
panic: reflect: call of reflect.Value.Interface on zero Value [recovered]
        panic: reflect: call of reflect.Value.Interface on zero Value

goroutine 16 [running]:
testing.tRunner.func1.2({0xce5dc0, 0xc0000142b8})
        /usr/local/go/src/testing/testing.go:1209 +0x36c
testing.tRunner.func1()
        /usr/local/go/src/testing/testing.go:1212 +0x3b6
panic({0xce5dc0, 0xc0000142b8})
        /usr/local/go/src/runtime/panic.go:1047 +0x266
reflect.valueInterface({0x0, 0x0, 0x0}, 0x1)
        /usr/local/go/src/reflect/value.go:1356 +0x21e
reflect.Value.Interface(...)
        /usr/local/go/src/reflect/value.go:1351
...
created by testing.(*T).Run
        /usr/local/go/src/testing/testing.go:1306 +0x727
FAIL    gorm.io/playground      0.102s
FAIL

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions