VillageSQL is a drop-in replacement for MySQL with extensions.
All examples on this page work on VillageSQL. Install Now →
posts テーブルを用意し、MySQL 向けに sqlc でコードを生成して、稼働中のサーバーに対して実行します。
sqlc のインストール
sqlc は Homebrew でインストールします。Go のツールとして管理したい場合はgo install を使います。
プロジェクトの構成
Go モジュールと 3 つのファイルを作成します。スキーマ、クエリファイル、および sqlc の設定ファイルです。schema.sql は、sqlc が型を生成する元になるテーブルを定義します。body には NOT NULL 制約がなく、これが後で重要になります。
注釈付きクエリを書く
sqlc はただの SQL ファイルを読み、各ステートメントの上にある-- name: コメントを、生成される Go のメソッドに変換します。クエリ名の後の単語が戻り値の形を決めます。:one は 1 行、:many はスライス、:exec は戻り値なし、:execresult は生の sql.Result、:execrows は影響を受けた行数です。
MySQL の INSERT ステートメントは RETURNING をサポートしていないため、新しい行の ID を受け取るには専用の注釈が必要です。:execlastid は sql.Result.LastInsertId() を代わりに呼び出し、その値を直接返します。
sqlc.yaml の設定
sqlc.yaml は、スキーマファイルとクエリファイルの場所を sqlc に示し、MySQL エンジンを設定します。パスは、sqlc を実行したディレクトリではなく、sqlc.yaml の場所からの相対パスとして解決されます。
package と out は、生成される Go パッケージの名前と、その書き出し先ディレクトリを指定します。sql_package: "database/sql" は、標準ライブラリの database/sql に対するコードを生成するよう sqlc に指示します。go-sql-driver/mysql が実装しているのはこのインターフェースです。
コードの生成
posts/ に 3 つのファイルが生成されます。db.go(Queries 構造体とそのコンストラクタ)、models.go(Post 構造体)、および query.sql.go(注釈付きクエリごとに 1 つのメソッド)です。NULL 許容の body 列が現れるのは models.go です。
NULL 許容列はポインタではなく sql.NullString になる
body TEXT には NOT NULL 制約がないため、sqlc はこの列に対して、ただの string や *string ではなく sql.NullString を生成しました。同じルールは、INSERT ステートメントのパラメータリストから生成される CreatePostParams にも適用されます。
sql.NullString を明示的に組み立てる必要があります。
Valid: false を設定します。空の sql.NullString{} ゼロ値はすでに Valid: false を持つため、sql.NullString{Valid: false} と明示的に書くのと同じです。
.String を信頼する前に .Valid を確認する必要があります。
NULL を許容させる必要がある場合は、それを意図的なスキーマ上の決定にしてください。schema.sql の body に NOT NULL を追加して sqlc generate を再実行すると、生成されるフィールドは現れるすべての場所で sql.NullString からただの string に変わり、すべての呼び出し元から .Valid のチェックがなくなります。
接続とマイグレーションの実行
データベースと、実際のパスワードを持ち、その 1 つのデータベースに範囲を限定したアプリケーションユーザーを作成します。mysql クライアントで schema.sql を適用します。
parseTime=true を付けて TCP で接続します。この設定は、MySQL の TIMESTAMP 列と DATETIME 列を []byte ではなく Go の time.Time にスキャンするために必要です。
CreatePost は int64 を返し、GetPost は uint64 を受け取る
id BIGINT UNSIGNED は、sqlc が列として生成するすべての場所で uint64 に対応し、GetPost の id パラメータも同様です。しかし、:execlastid で生成された CreatePost はただの int64 を返します。これは sql.Result.LastInsertId() に由来し、標準ライブラリは列の実際の符号の有無にかかわらず、この戻り値を int64 と定義しています。作成したばかりの ID を GetPost に渡すには、明示的な変換が必要です。
接続プールの調整
database/sql は sql.Open の下で独自の接続プールを管理し、sqlc が生成する Queries 構造体は、渡された *sql.DB がすでに持っているプールをそのまま使います。MySQL バックエンドでは、特に 2 つの設定が重要です。
SetConnMaxLifetime は MySQL の wait_timeout より小さい値に設定します。このサーバーでのデフォルトは 28800 秒(8 時間)です。この設定がないと、MySQL がサーバー側ですでに閉じた接続が、そのいずれかに対するクエリが失敗するまで、Go のプールにアイドルの項目として残り続けます。
よくある質問
sqlc は稼働中のデータベースに対して実行されますか?
明示的に有効にした場合のみです。sqlc generate は schema.sql と query.sql を静的に解析し、稼働中のサーバーを必要としません。sqlc vet と sqlc push は追加のチェックのために実際のデータベースに接続できますが、このガイドの基本的な generate ワークフローは接続を一切開きません。
query.sql のクエリに構文エラーがあるとどうなりますか?
sqlc generate は、何かを生成する前に、ファイルと行を示す解析エラーで失敗します。失敗時には出力ディレクトリに何も書き込まれないため、壊れたクエリが古い、または部分的な生成コードを生むことはありません。
sqlc は INSERT … ON DUPLICATE KEY UPDATE のコードを生成できますか?
はい。sqlc はこれを通常のINSERT ステートメントとして解析します。他の挿入と同じように、結果のどの部分が必要かに応じて :exec、:execresult、:execrows、または :execlastid の注釈を付けます。
トラブルシューティング
関連項目
- MySQL と ORM — sqlc がその代替となる、実行時にクエリを組み立てる手法
- MySQL のスキーママイグレーション — テーブルが本番環境に入った後、
schema.sqlの変更を安全にバージョン管理する - MySQL の接続プーリング —
database/sqlのデフォルトを超えたプールのサイズ設定と永続接続