sql.NullString(or NullXXX)を使いましょう
背景
sqlパッケージのAPIドキュメントのサンプルを参考に値を取ってくるとします。例えばこのようなコードですね。
age := 27
rows, err := db.Query("SELECT id, name, address FROM users WHERE age=?", age)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var id string
var name string
var address string
if err := rows.Scan(&id, &name, &address); err != nil {
// もしレコードのどこかにNULL値があれば、errを吐いて終了する。
log.Fatal(err)
}
fmt.Printf("%s %s %s\n", id, name, address)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
ここでもしnameがNULL値だった場合、errを吐いてこけます。だからNULL値を許容するカラムにはstringではなくsql.NullStringを使いましょうというだけの話なんですが。。。ここでもしrows.Scan()のエラーチェックを省略すると、見かけ上処理が正しく終わったようになってしまうため、ハマってしまうことがあるかと思います。
age := 27
rows, err := db.Query("SELECT id, name, address FROM users WHERE age=?", age)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var id string
var name string
var address string
// 例えば id:"1", name:NULL, address:"Tokyo"というレコードが合った場合、
// errorチェックを省くと・・・
// id=1, name=, address=\n
// が標準出力される。見かけ上、name, addressが空文字だっただけに見える。(実際はaddressにはデータがあるが、Scan()が中断されて取得できていない)
/*
if err := rows.Scan(&id, &name, &address); err != nil {
log.Fatal(err)
*/
rows.Scan(&id, &name, &address)
fmt.Printf("id=%s name=%s address=%s\n", id, name, address)
}
if err := rows.Err(); err != nil {
log.Fatal(err)
}
まとめ
- sql.NullString(NullXXX)使いましょう
- エラーチェックサボらないようにしましょう