メインコンテンツへスキップ

VillageSQL is a drop-in replacement for MySQL with extensions.

All examples on this page work on VillageSQL. Install Now →
sqlc は SQL から直接 Go の構造体とクエリメソッドを生成するため、アプリケーションコードは実行時にクエリを組み立てるのではなく、ただの Go 関数を呼び出します。コードと自分で書いた SQL の間に ORM の層はありません。このガイドでは、小さな 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} と明示的に書くのと同じです。
NULL 許容列を読み戻すときは、.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 の注釈を付けます。

トラブルシューティング

関連項目