起きたこと
以下のようなユーザーをDBから取得する関数を記述していた。
func (u *User) Find() error {
if err := connector.Postgres.QueryRow(`SELECT id, uuid, name, encrypted_password FROM users where email = $1`, u.Email).Scan(&u.ID, &u.UUID, nullName, &u.EncryptedPassword); err != nil {
return err
}
return nil
}
実行すると、以下のエラーが出た。
unsupported Scan, storing driver.Value type <nil> into type *string
原因
usersテーブルのnameの値がnull
だったことが原因。
Scanメソッドは*Row
から該当するフィールドをコピーする。
このときカラムがnullだと割り当てるポインタがnilになる。
そしてstring型はゼロ値が""(空文字)でnilに対応していない。
DBからの取得結果がnilだったのにu.Name(string型)に値をコピーしようとしたことが原因だった。
解決案
database/sql
パッケージにNullString
があるので、それを使う。
NullStringはScanerインターフェースを実装してるので、スキャンのコピー先として使える。
使い方は、前もってNullString型の変数を定義しておき、
Scanメソッドの引数として渡し、値をコピーする。
nullStringはStringとValidというフィールドを持ち、ValidはStringがNULLでないときにtrueになっている。
これを利用して、条件分岐をする。
Validがtrueなら、文字列を使用する(自分の場合はインスタンスのフィールドに値を代入した)
falseならよしなにできる(自分の場合は空文字を代入した)
func (u *User) Find() error {
nullName := new(sql.NullString)
if err := connector.Postgres.QueryRow(`SELECT id, uuid, name, encrypted_password FROM users where email = $1`, u.Email).Scan(&u.ID, &u.UUID, nullName, &u.EncryptedPassword); err != nil {
return err
}
if nullName.Valid {
u.Name = nullName.String
}
return nil
}
参考
database/sql#NullString
unsupported Scan, storing driver.Value type into type *string
あとがき
SQLでSELECT IF(name IS NULL, "", name)
みたいにしてもいけるのでは?と思った。
Goを始めて2ヶ月くらいの初心者なので、コードでおかしいところ、もっときれいに書けるところなどありましたら、
コメントなどで指摘してもらえると嬉しいです。