Golangの標準sqlパッケージ

Contents
  1. Fetching Data from the Database
  2. How Scan() Works
  3. Preparing Queries
  4. Single-Row Queries
  5. Statements that Modify Data
  6. Working with Transactions
  7. Prepared Statements And Connections
  8. Avoiding Prepared Statements
  9. Prepared Statements in Transactions
  10. Parameter Placeholder Syntax
  11. Errors From Iterating Resultsets
  12. Errors From Closing Resultsets
  13. Errors From QueryRow()
  14. Identifying Specific Database Errors
  15. Handling Connection Errors
  16. Resource Exhaustion
  17. Large uint64 Values
  18. Connection State Mismatch
  19. Database-Specific Syntax
  20. Multiple Result Sets
  21. Invoking Stored Procedures
  22. 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