Search code examples
postgresqlgogo-gorm

How to set pgtype.Null as a default when querying a null field?


I am handling postgresql db with Gorm in Golang. And I'm using the pgtype package to handle nullable fields. To insert a null value into a field, I have to set the status to pgtype.null as follows.

type Person struct {
  ID string
  Age int
  Nickname pgtype.Text
}

func CreatePerson(){
  john := &Person{
    ID: "John",
    Age: 30,
    Nickname: pgtype.Text{Status: pgtype.Null},
  }
  DB.Create(john) // ID: John, Age: 30, Nickname: NULL
}

However, When I load the data from the DB, the null field will be converted to Zero value for pgtypte.Text, that is, its status is Undefined.

func GetPerson(){
  john := &Person{}
  DB.Where("id = ?", "John").First(john) // ID: John, Nickname: NUL

  /* john = {
    ID: "John",
    Age: 30,
    Nickname: pgtype.Text{Status: pgtype.Undefined},
  }
  */
}

Here, it is unnatural that the gorm struct that used to save in the DB is different from the gorm struct loaded from the DB.

Also, when I modify some of the fields of the struct and save it again, an error occurs due to the status of undefined.

func GetAndSave(){
  john := &Person{}
  DB.Where("id = ?", "John").First(john) // ID: John, Nickname: NUL

  /* john = {
    ID: "John",
    Age: 30,
    Nickname: pgtype.Text{Status: pgtype.Undefined},
  }
  */
  john.Age = 31
  DB.Select("*").Update(john) // Error since its nickname has undefined status
}

Therefore, I want to make pgtype fields automatically have null status for null fields when I load data with the null field from the db. How can I do this?


Solution

  • You can use BeforeCreate hook to set the default value for fields.

    try

    func (person *Person) BeforeCreate(_ *gorm.DB) error {
        if person.Nickname.Status == pgtype.Undefined {
            person.Nickname.Status = pgtype.Null
        }
        return nil
    }
    

    This will check the pgtype field is Undefined and set that as Null for Person struct before creating the entry in the database. And you will be able to omit the Nickname field while creating

    john := &Person{
        ID:  "John",
        Age: 30,
    }
    DB.Create(john) // ID: John, Age: 30, Nickname: NULL    
    

    Same way, you can use AfterFind hook to update the resulting response fields

    func (person *Person) AfterFind(_ *gorm.DB) error {
        if person.Nickname.Status == pgtype.Undefined {
            person.Nickname.Status = pgtype.Null
        }
        return nil
    }
    

    Hope this helps