Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
shtrih opened this issue Feb 1, 2023 · 1 comment
Open
Assignees
Labels
type:with reproduction steps with reproduction steps

Comments

@shtrih
Copy link
Contributor

shtrih commented Feb 1, 2023

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
@github-actions github-actions bot added the type:with reproduction steps with reproduction steps label Feb 1, 2023
@jasonzbao
Copy link

Running into the same issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type:with reproduction steps with reproduction steps
Projects
None yet
Development

No branches or pull requests

3 participants