Golangの標準sqlパッケージ
Contents
- Overview
- Importing a Database Driver
- Accessing the Database
- Retrieving Result Sets
- Fetching Data from the Database
- How Scan() Works
- Preparing Queries
- Single-Row Queries
- Modifying Data and Using Transactions
- Statements that Modify Data
- Working with Transactions
- Using Prepared Statements
- Prepared Statements And Connections
- Avoiding Prepared Statements
- Prepared Statements in Transactions
- Parameter Placeholder Syntax
- Handling Errors
- Errors From Iterating Resultsets
- Errors From Closing Resultsets
- Errors From QueryRow()
- Identifying Specific Database Errors
- Handling Connection Errors
- Working with NULLs
- Working with Unknown Columns
- The Connection Pool
- Surprises, Antipatterns and Limitations
- Resource Exhaustion
- Large uint64 Values
- Connection State Mismatch
- Database-Specific Syntax
- Multiple Result Sets
- Invoking Stored Procedures
- 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)`はうまく調整する