GORM Playground Link
https://github.com/go-gorm/playground/pull/707
Description
Related to #4990
The SetColumn
updates the wrong column when used inside a BeforeUpdate
hook. With the code below one would expect that the price_area
column would be updated to "before hook" however the next column gets updated to before hook
. When running the code below, the output is:
While updating the before hook to tx.Statement.SetColumn("address", &test)
it outputs this:
var id = uuid.New()
func setupDb() (*gorm.DB, error) {
dbSource := "user=local-user password=local-pwd host=test-local-db dbname=testdb port=5434 sslmode=disable TimeZone=Europe/Oslo"
return gorm.Open(postgres.Open(dbSource), &gorm.Config{TranslateError: true})
}
type Home struct {
Id uuid.UUID `json:"id" gorm:"primaryKey"`
Address *string `json:"address"`
Zip *int `json:"zip"`
PriceArea *string `json:"price_area"`
Type *string `json:"type"`
CreatedAt time.Time
UpdatedAt time.Time
}
type HomeUpdate struct {
Zip *int `json:"zip"`
Address *string `json:"address"`
PriceArea *string `json:"price_area"`
Type *string `json:"type"`
}
func (home *Home) BeforeUpdate(tx *gorm.DB) error {
test := "before hook"
tx.Statement.SetColumn("price_area", &test)
return nil
}
func main() {
db, err := setupDb()
if err != nil {
fmt.Println(err.Error())
return
}
db.AutoMigrate(&Home{})
home := Home{
Id: id,
}
dbErr := db.Create(&home).Error
if dbErr != nil {
fmt.Println(dbErr.Error())
return
}
tmpString := "test"
updatePayload := HomeUpdate{
Address: &tmpString,
}
dbErr = db.Model(&home).Updates(&updatePayload).Error
if dbErr != nil {
fmt.Println(dbErr.Error())
return
}
}
If it turns out to be a bug I am happy to help looking more into the problem and possible find a solution 🔍
Update: Some further research
After som searching it seems like the problem arises from the use fieldIndex in field.ReflectValueOf
as it assumes the index of the two structs are the same. So when this line runs
https://github.com/go-gorm/gorm/blob/1b48aa072d1210c2ba315aeea18b57fddb634875/schema/field.go#L501-L503
the value is updated using the index in the Home
to update the HomeUpdate
struct which is the one being used for creating the update. So when this runs
https://github.com/go-gorm/gorm/blob/1b48aa072d1210c2ba315aeea18b57fddb634875/statement.go#L584
the 3rd index is updated to "before hook" which is the Type
field. If Id
is moved under Type
in Home
everything would work as expected.
Proposed Fix
Moving from updating fields by index to reflect.Indirect(value).FieldsByName
in field.Set(stmt.Context, destValue, value)
would fix the problem, however the field.ReflectValueOf
must points to the new FieldsByName function which I am not sure how to do or it may not be what we want to do.
Comment From: ksn-ark
Gorm Playground Link
https://github.com/ksn-ark/playground
Description
Hey this has been resolved, I'm entirely unsure as to how but I forked your fork and merged it with the original master because the dependency resolutions were failing. The tests pass on every dialect. only alteration is avoiding an unsafe de-reference of a null pointer. This issue should be closed.