LoginSignup
10
4

More than 5 years have passed since last update.

DBから取得した値がnilでエラーになったので調べた

Posted at

起きたこと

以下のようなユーザーを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ヶ月くらいの初心者なので、コードでおかしいところ、もっときれいに書けるところなどありましたら、
コメントなどで指摘してもらえると嬉しいです。

10
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
4