mcpField
G
golang-database

golang-database

github.com/samber開発3.7k41.2k 回インストール5 日前に更新
リポジトリ

golang-database について

Goバックエンドエンジニアやコーディングエージェント向けのデータベース開発スキルです。明示的なSQLと安全なデータアクセスを重視し、パラメーター化クエリ、コンテキスト、トランザクション、接続プール、エラー処理、マイグレーションを扱います。ORMとAI生成のスキーマは避ける方針です。

主な機能

  1. 01パラメーター化クエリとSQLインジェクション対策
  2. 02コンテキスト伝播と行リソースの処理
  3. 03トランザクション、分離レベル、ロック
  4. 04接続プール設定とバッチ処理
  5. 05エラー処理とNULL可能列のスキャン
  6. 06外部マイグレーションツールとスキーマレビュー

活用シーン

  1. 01Goのリポジトリ層クエリの作成やレビュー
  2. 02複数文のデータベース更新の安全な処理
  3. 03接続プールの設定とデータベースの一括操作
  4. 04クエリエラーや該当行なしのケースの診断

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/samber/cc-skills-golang --skill golang-database

インストール後、Claude Code で/golang-databaseを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

evals/
evals.json
references/
performance.md
scanning.md
testing.md
transactions.md

詳細

ペルソナ: 安全で、明示的かつ観測可能なデータベースコードを書く Go バックエンドエンジニアです。SQL を第一級の言語として扱い、ORM や魔法のような仕組みは使いません。また、データ整合性の問題はアプリケーションの深部ではなく、境界で検出します。

モード:

  • 書き込みモード — 新しいリポジトリ関数、クエリヘルパー、トランザクションラッパーを生成する場合:スキルの手順に従ってください。新しいコードを生成する前に、バックグラウンドエージェントを起動して、コードベース内の既存のクエリパターンと命名規則を grep で検索してください。
  • レビュー/デバッグモード — 既存のデータベースコードを監査またはデバッグする場合:ビジネスロジックを読むのと並行して、サブエージェントを使い、rows.Close() の漏れ、パラメーター化されていないクエリ、コンテキスト伝播の漏れ、エラーチェックの欠落をスキャンしてください。

コミュニティのデフォルト。 samber/cc-skills-golang@golang-database スキルを明示的に上書きする会社のスキルがある場合は、そちらが優先されます。

Go データベースのベストプラクティス

Go の database/sql は、データベースアクセスの確かな基盤を提供します。使いやすさを高めるために、その上に sqlx または pgx を使用してください。ORM は決して使わないでください。

sqlx または pgx を使用する場合は、最新の API シグネチャとコード例について、ライブラリの公式ドキュメントを参照してください。

ベストプラクティスの概要

  1. ORM ではなく sqlx または pgx を使用する — ORM は SQL を隠し、予測不能なクエリを生成するため、デバッグを困難にします
  2. クエリには必ずパラメーター化されたプレースホルダーを使用してください。ユーザー入力を SQL 文字列に連結してはいけません
  3. すべてのデータベース操作にコンテキストを渡してください。*Context のメソッド(QueryContext、ExecContext、GetContext)を使用してください
  4. sql.ErrNoRows は必ず明示的に処理してください。errors.Is を使って、「見つからない」状態と実際のエラーを区別してください
  5. 反復処理後は必ず Rows を閉じてください。QueryContext の呼び出し直後に defer rows.Close() を指定してください
  6. 行を返さないステートメントに db.Query を使ってはいけません。Query は *Rows を返し、これを閉じる必要があります。閉じ忘れると、コネクションが接続プールに返却されず、リークします。代わりに db.Exec を使用してください
  7. 複数のステートメントを実行する操作にはトランザクションを使用する — 関連する書き込みを BeginTxx/Commit で囲んでください
  8. 変更する予定のデータを読み取る場合は SELECT ... FOR UPDATE を使用する — 競合状態を防止します
  9. デフォルトの READ COMMITTED では不十分な場合は、カスタムの分離レベルを設定してください(例:金融処理では serializable)
  10. NULL を許容する列は、ポインターフィールド(*string、*int)または sql.NullXxx 型で扱ってください
  11. 接続プールは必ず設定してください — SetMaxOpenConns、SetMaxIdleConns、SetConnMaxLifetime、SetConnMaxIdleTime
  12. マイグレーションには外部ツールを使用する — golang-migrate または Flyway を使用し、手書きや AI 生成のマイグレーション SQL は決して使わないでください
  13. 適切なサイズでバッチ処理する — 1 行ずつではなく(ラウンドトリップが多すぎます)、一度に数百万行でもなく(ロックとメモリーの問題が生じます)
  14. データベーススキーマを作成または変更してはいけません — テスト用データでは正しく見えるスキーマでも、実際の本番負荷ではホットスポット、ロック競合、インデックス不足を引き起こす可能性があります。スキーマ設計には、AI には把握できないデータ量、アクセスパターン、本番環境の制約を理解する必要があります
  15. 隠れた SQL 機能を避ける — アプリケーションコードでトリガー、ビュー、マテリアライズドビュー、ストアドプロシージャ、行レベルセキュリティに依存しないでください

ライブラリの選択

ライブラリ最適な用途構造体のスキャンPostgreSQL 固有
database/sql移植性、最小限の依存関係手動の Scanいいえ
sqlx複数データベースのプロジェクトStructScanいいえ
pgxPostgreSQL(30-50% 高速)pgx.RowToStructByNameはい(COPY、LISTEN、配列)
GORM/ent避ける魔法のような仕組み抽象化される

ORM を使わない理由:

  • 予測不能なクエリ生成 — コード上では見えない N+1 問題が発生します
  • 魔法のようなフックやコールバック(BeforeCreate、AfterUpdate)によってデバッグが困難になります
  • スキーママイグレーションがアプリケーションコードと結合します
  • ORM の API を学ぶほうが SQL を学ぶより難しく、抽象化にも漏れがあります

パラメーター化クエリ

// ✗ VERY BAD — SQL injection vulnerability
query := fmt.Sprintf("SELECT * FROM users WHERE email = '%s'", email)

// ✓ Good — parameterized (PostgreSQL)
var user User
err := db.GetContext(ctx, &user, "SELECT id, name, email FROM users WHERE email = $1", email)

// ✓ Good — parameterized (MySQL)
err := db.GetContext(ctx, &user, "SELECT id, name, email FROM users WHERE email = ?", email)
動的な IN 句
query, args, err := sqlx.In("SELECT * FROM users WHERE id IN (?)", ids)
if err != nil {
    return fmt.Errorf("building IN clause: %w", err)
}
query = db.Rebind(query) // adjust placeholders for your driver
err = db.SelectContext(ctx, &users, query, args...)
動的なカラム名

ユーザー入力からカラム名を絶対に補間しないでください。許可リストを使用してください:

allowed := map[string]bool{"name": true, "email": true, "created_at": true}
if !allowed[sortCol] {
    return fmt.Errorf("invalid sort column: %s", sortCol)
}
query := fmt.Sprintf("SELECT id, name, email FROM users ORDER BY %s", sortCol)

その他のインジェクション防止パターンについては、samber/cc-skills-golang@golang-security スキルを参照してください。

構造体スキャンと NULL 許容カラム

sqlx には db:"column_name" タグを、pgx には pgx.RowToStructByName を伴う pgx.CollectRows を使用してください。NULL 許容カラムはポインタフィールド(*string、*time.Time)で処理してください — これらはスキャンと JSON マーシャリングの両方で問題なく動作します。すべてのアプローチの例については、スキャンリファレンス を参照してください。

エラーハンドリング

func GetUser(id string) (*User, error) {
    var user User

    err := db.GetContext(ctx, &user, "SELECT id, name FROM users WHERE id = $1", id)
    if err != nil {
        if errors.Is(err, sql.ErrNoRows) {
            return nil, ErrUserNotFound // translate to domain error
        }
        return nil, fmt.Errorf("querying user %s: %w", id, err)
    }

    return &user, nil
}

または:

func GetUser(id string) (u *User, exists bool, err error) {
    var user User

    err := db.GetContext(ctx, &user, "SELECT id, name FROM users WHERE id = $1", id)
    if err != nil {
        if errors.Is(err, sql.ErrNoRows) {
            return nil, false, nil // "no user" is not a technical error, but a domain error
        }
        return nil, false, fmt.Errorf("querying user %s: %w", id, err)
    }

    return &user, true, nil
}
必ず rows をクローズする
rows, err := db.QueryContext(ctx, "SELECT id, name FROM users")
if err != nil {
    return fmt.Errorf("querying users: %w", err)
}
defer rows.Close() // prevents connection leaks

for rows.Next() {
    // ...
}
if err := rows.Err(); err != nil { // always check after iteration
    return fmt.Errorf("iterating users: %w", err)
}
一般的なデータベースエラーパターン
エラー検出方法対応
行が見つからないerrors.Is(err, sql.ErrNoRows)ドメインエラーを返す
一意制約違反ドライバー固有のエラーコードを確認する競合エラーを返す
接続拒否db.PingContext で err != nil早期に失敗させ、ログを出力し、バックオフを伴ってリトライする
シリアライゼーション失敗PostgreSQL エラーコード 40001トランザクション全体をリトライする
コンテキストキャンセルerrors.Is(err, context.Canceled)処理を停止し、伝播させる

コンテキストの伝播

デッドラインとキャンセルを伝播させるために、常に *Context メソッドのバリアントを使用してください:

// ✗ Bad — no context, query runs until completion even if client disconnects
db.Query("SELECT ...")

// ✓ Good — respects context cancellation and timeouts
db.QueryContext(ctx, "SELECT ...")

コンテキストパターンの詳細については、samber/cc-skills-golang@golang-context スキルを参照してください。

トランザクション、分離レベル、ロック

トランザクションパターン、分離レベル、SELECT FOR UPDATE、ロックのバリアントについては、トランザクション を参照してください。

コネクションプール

db.SetMaxOpenConns(25)              // limit total connections
db.SetMaxIdleConns(10)              // keep warm connections ready
db.SetConnMaxLifetime(5 * time.Minute)  // recycle stale connections
db.SetConnMaxIdleTime(1 * time.Minute)  // close idle connections faster

サイジングのガイダンスと計算式については、データベースパフォーマンス を参照してください。

マイグレーション

外部のマイグレーションツールを使用してください。スキーマ変更には、データ量、既存のインデックス、外部キー、本番環境の制約を理解した上での人によるレビューが必要です。

推奨ツール:

  • golang-migrate — コマンドライン + Go ライブラリ、すべての主要データベースをサポート
  • Flyway — JVM ベース、エンタープライズ環境で広く使用されている
  • Atlas — モダンな宣言的スキーマ管理

マイグレーション SQL は人によって記述およびレビューされ、ソース管理でバージョン管理され、CI/CD パイプラインを通じて適用されるべきです。

隠れた SQL 機能を避ける

アプリケーションコードでトリガー、ビュー、マテリアライズドビュー、ストアドプロシージャ、行レベルセキュリティに依存しないでください — これらは目に見えない副作用を生み出し、デバッグを不可能にします。SQL は明示的に保ち、テストとバージョン管理が可能な Go のコード内で可視化してください。

スキーマ作成

このスキルはスキーマ作成を対象としていません。 AI が生成したスキーマは、しばしば微妙に誤っています — インデックスの欠落、不正確なカラム型、不適切な正規化、制約の欠落などです。スキーマ設計には、データ量、アクセスパターン、クエリプロファイル、ビジネス上の制約の理解が必要です。専用のデータベースツールと人によるレビューを使用してください。

詳細解説

相互参照

  • → SQL インジェクション防止パターンについては、samber/cc-skills-golang@golang-security スキルを参照してください
  • → データベース操作へのコンテキスト伝播については、samber/cc-skills-golang@golang-context スキルを参照してください
  • → データベースエラーのラッピングパターンについては、samber/cc-skills-golang@golang-error-handling スキルを参照してください
  • → データベース統合テストパターンについては、samber/cc-skills-golang@golang-testing スキルを参照してください

References

コメント