GORM Playground Link
https://github.com/go-gorm/playground/pull/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
Comment From: jasonzbao
Running into the same issue