Golangの標準sqlパッケージ

Contents
  1. Overview
  2. Importing a Database Driver
  3. Accessing the Database
  4. Retrieving Result Sets
  5. Fetching Data from the Database
  6. How Scan() Works
  7. Preparing Queries
  8. Single-Row Queries
  9. Modifying Data and Using Transactions
  10. Statements that Modify Data
  11. Working with Transactions
  12. Using Prepared Statements
  13. Prepared Statements And Connections
  14. Avoiding Prepared Statements
  15. Prepared Statements in Transactions
  16. Parameter Placeholder Syntax
  17. Handling Errors
  18. Errors From Iterating Resultsets
  19. Errors From Closing Resultsets
  20. Errors From QueryRow()
  21. Identifying Specific Database Errors
  22. Handling Connection Errors
  23. Working with NULLs
  24. Working with Unknown Columns
  25. The Connection Pool
  26. Surprises, Antipatterns and Limitations
  27. Resource Exhaustion
  28. Large uint64 Values
  29. Connection State Mismatch
  30. Database-Specific Syntax
  31. Multiple Result Sets
  32. Invoking Stored Procedures
  33. Multiple Statement Support

http://go-database-sql.org/index.html

を読んだ。のでまとめてみた 😃

内容はちょっと古いっぽい。

Overview

  • sql.DB は DB connection ではない
  • sql.DB はインターフェイスの抽象であり、DB の存在である
  • sql.DB は次のことをやってくれる
    • コネクションの管理
    • コネクションプールの管理
  • コネクションはちゃんと閉じよう

Importing a Database Driver

  • driver は直接使うな

Accessing the Database

  • sql.Open() は *sql.DB を返す
  • sql.Open() ではコネクションは確立しない
  • 実際に接続が必要になったとき、遅延的に接続される
  • sql.DB は長生きするようにデザインされている
  • 頻繁に Open/Close するな

Retrieving Result Sets

  • database/sql の関数名は大きな意味を持つ
  • 例えば Query って単語を含んでいれば、DB に問い合わせて行のセットを返す

Fetching Data from the Database

  • 結果を受け取るには適切な型の変数のポインタを rows.Scan() に渡す
  • db.Query() で返る rows はコネクションを占領し続けるので適切に rows.Close() する
  • rows.Next() は最終的には EOF になって rows.Close() 呼ぶけど、ループ途中で抜けたりしたときのために
  • rows.Close() は既にクローズ済でも無害
  • とりあえず defer rows.Close() しときましょう
  • ループの中でクエリして結果を受け取るような場合は、ループの中で defer を使わず明示的に rows.Close() しましょう

How Scan() Works

  • Scan() 使えばクエリ結果をよろしく型変換してくれる

Preparing Queries

  • 何回も同じクエリを実行するときは db.Prepare() しましょう

Single-Row Queries

  • err = db.QueryRow().Scan() でエラーが起きると、Scan() まで defer される

Modifying Data and Using Transactions

Statements that Modify Data

  • 行を返さない insert とか delete には Exec() を使う
  • Query() は行を返さない SQL でも sql.Rows を返し、それは実行後もコネクションを確保している

Working with Transactions

  • db.Begin() から Commint() か Rollback() までが 1 つのトランザクションとなる
  • トランザクションの中で Db を操作しちゃダメ。Tx を使う。Tx はトランザクション内にいるけど、Db はトランザクション外
  • コネクションの状態を変えるような複数の Statement を使う時はトランザクションが必要なくても Tx が必要
    • temporary table
    • SET @var := value
    • charset, timeout みたいな接続オプションとか
  • シングルコネクションでごにょごにょする方法が Go には Tx しかない

Using Prepared Statements

Prepared Statements And Connections

  • Prepared Statement は、特定のコネクションと紐づく
  • database/sql では、statement がどのコネクションと紐付いているかを Stmt オブジェクトが記憶している
  • Stmt を実行するとき、Stmt がそのコネクションを使おうとする
    • もしコネクションが閉じてたり、busy だった場合は他のコネクションに再度 prepare し直す
  • こうすることで high-concurrency を実現してる

Avoiding Prepared Statements

  • db.Query(sql, param) でも背後で Prepared Statement してる
  • Prepared Statement が好ましくないときもある
    • RDBMS がサポートしてないとき
    • パフォーマンスが厳しいとき(セキュリティは別の方法でなんとかする)
  • Prepared Statement したくないときは、fmt.Sprint() とかで整形して、db.Query() とかに渡す

Prepared Statements in Transactions

  • Tx で作られた Prepared Statements はその Tx にのみ紐づく
  • 同様に DB 上で作られた Prepared Statement はトランザクション内では使えない
  • トランザクション外で準備された Prepared Statement を使うには Tx.Stmt() を使う
  • あんま使わない方がいい (今も?)

Parameter Placeholder Syntax

Handling Errors

  • database/sql のほぼすべての機能は返り値でエラーを返すから無視すんなよ

Errors From Iterating Resultsets

  • ループ後 rows.Err() で確認する
  • 異常終了した時自動で rows.Close() される

Errors From Closing Resultsets

  • rows.Next() が最後までいくと勝手に rows.Close() されるけど、途中で抜けた時のために明示的にループ後で rows.Close() する
  • rows.Close() によるエラーは、何をすべきか明確でないので、ログとったり panic したりする

Errors From QueryRow()

  • QueryRow() で結果が空のとき sql.ErrNoRows というエラーを返す
  • 結果が空なのはエラーじゃないことがほとんどなので、適切にハンドルする必要がある

Identifying Specific Database Errors

  • エラーのハンドルはエラーメッセージじゃなくエラー番号で
  • ドライバごとに方法は異なる
  • エラー番号はデータベースによる
  • 数字をそのまま使うのは臭うので、定数を使おう

Handling Connection Errors

  • 10 回を上限に自動で再接続する

Working with NULLs

  • NULLABLE な値を扱う時、database/sql に用意されている特別な型を使う
  • トリッキーかつ将来性ないので、あんま使わない方がいい
  • デフォルト値を設定した方がいい
  • どうしても NULL を避けられなかったら、SQL で COALESCE() を使うのもあり

Working with Unknown Columns

  • Scan() は渡す変数の数がクエリ結果の列数と一致してないといけない
  • クエリ結果の列数がわからないときは、Columns() が使える
  • なんもわからんときは sql.RawBytes を使う

The Connection Pool

  • デフォルトで接続数に上限はない
  • db.SetMaxOpenConns(N) で接続数の上限を設定できる
  • db.SetMaxIdleConns(N)`はうまく調整する

Surprises, Antipatterns and Limitations

Resource Exhaustion

Large uint64 Values

Connection State Mismatch

Database-Specific Syntax

Multiple Result Sets

Invoking Stored Procedures

Multiple Statement Support