オフラインスキーマ管理:堅牢なRustアプリケーションのためのsqlx-cliとdiesel-cliの活用
Wenhao Wang
Dev Intern · Leapcell

はじめに
現代のソフトウェア開発の世界では、データベースはほぼすべてのアプリケーションの基盤です。アプリケーションが進化するにつれて、そのデータ構造も進化します。データベースマイグレーションとして知られるこれらの変更を管理することは、アプリケーションの安定性、保守性、およびデプロイ可能性に直接影響を与える重要なタスクです。Rustのエコシステムでは、強力な型付けとコンパイル時の保証が重視されているため、アプリケーションのデータモデルとデータベーススキーマを同期させることが最も重要です。マイグレーションを管理するためのツールは数多く存在しますが、ライブデータベース接続なしでオフラインスキーマチェックを実行し、マイグレーションを管理できる機能は、特にCI/CDパイプラインや厳格なネットワークアクセスを持つローカル開発環境において、大きな利点を提供します。この記事では、Rust開発者がsqlx-cli
やdiesel-cli
のような強力なコマンドラインツールを活用して、データベースマイグレーションとスキーマを効果的に管理する方法について、特にその貴重なオフライン機能に焦点を当てて掘り下げます。
環境の理解
sqlx-cli
とdiesel-cli
の詳細に入る前に、Rustにおけるデータベーススキーマ管理の根底にあるいくつかのコアコンセプトを明確にしましょう。
- データベースマイグレーション: これらはデータベーススキーマに対するプログラムによる変更です。通常、データベースをある状態から別の状態に進化させる方法(新しいテーブルの追加、列の変更、インデックスの作成など)を定義するバージョン管理されたファイル(例:SQLスクリプトまたはRustコード)です。
- スキーマ: テーブル、列、リレーションシップ、インデックス、およびデータベースのその他の要素の正式な記述です。
- ORM(オブジェクトリレーショナルマッパー): オブジェクト指向プログラミング言語を使用して、互換性のない型システム間のデータを変換するプログラミング技術です。Rustでは、
Diesel
が著名なORMです。 - クエリビルダー: 開発者がプログラムでSQLクエリを構築できるライブラリで、多くの場合、型安全性を提供し、生のSQL文字列を回避します。
SQLx
は主にクエリビルダーであり、非同期データベースドライバーです。 - オフラインスキーマチェック: ライブデータベース接続を必要とせずに、アプリケーションのデータモデル(例:Rustの構造体)とデータベーススキーマとの間の不一致を検証したり、マイグレーションファイルを生成したりする機能です。これにより、開発が大幅に高速化され、CI/CDの効率が向上します。
SQLx
とDiesel
はどちらもRustでデータベースと対話するための堅牢なソリューションを提供しており、それぞれのCLIツールはスキーマ管理にまでその機能性を拡張しています。Diesel
はRustコード内での型安全なクエリとスキーマ定義に焦点を当てたORMである一方、SQLx
は型安全な生のSQLクエリと、スキーマ定義に対するよりハンズオフなアプローチを重視しています。しかし、どちらも最新の開発に不可欠な強力なマイグレーションユーティリティを提供しています。
sqlx-cli:型安全なSQLとオフラインチェック
sqlx-cli
は、SQLx
データベースクレートのコマンドラインインターフェースです。SQLx
は、生のSQLクエリのコンパイル時チェックで知られており、アプリケーションを実行する前にクエリが構文的に正しく、データベーススキーマと一致していることを保証します。ここでsqlx-cli
が、特にオフラインのsqlx database diff
およびsqlx migrate add
機能で輝きます。
sqlx-cli
は、基本的にSQLスクリプトファイルを通じてマイグレーションを管理します。sqlx-cli
を使用した典型的なマイグレーションワークフローには、以下が含まれます。
-
新しいマイグレーションの作成:
sqlx migrate add create_users_table
このコマンドは、指定されたマイグレーションディレクトリ(通常は
migrations/
)に、マイグレーションの適用と元に戻すための_up.sql
および_down.sql
サフィックスを持つ新しいマイグレーションファイルを生成します。 -
マイグレーションSQLの記述: 次に、これらのファイルにSQL DDLステートメントを記述します。
-- migrations/20231027100000_create_users_table.up.sql CREATE TABLE users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), username VARCHAR(255) NOT NULL UNIQUE, email VARCHAR(255) NOT NULL UNIQUE, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP );
-- migrations/20231027100000_create_users_table.down.sql DROP TABLE users;
-
マイグレーションの適用: 通常、以下を実行します。
sqlx migrate run
これにより、保留中のすべてのマイグレーションがデータベースに適用されます。
オフラインチェックの文脈におけるsqlx-cli
のキラー機能は、アプリケーションのクエリをマイグレーション履歴に対して検証する機能です。SQLx
は、以前のデータベース操作またはsqlx prepare
によって生成された.sqlx
メタデータファイルを保存します。これらのファイルは、期待されるスキーマとクエリの型をキャプチャします。新しいクエリや変更されたスキーマがある場合、sqlx prepare --check
を実行すると、SQLx
DATABASE_URL
環境変数がライブデータベースではなくスキーマファイルを指している場合、ライブデータベース接続なしでデータベースマイグレーションを検証し、不一致を特定できます。
または、sqlx-cli
自体は、DATABASE_URL
にスキーマファイルを指す必要なしに強力なオフラインチェックを提供します。Rustアプリケーションと一連のマイグレーションがあるプロジェクトを考えてみてください。CI実行ごとにデータベースを起動して、アプリケーションのクエリが期待されるスキーマに対してまだ有効であるかを確認する代わりに、sqlx-cli
はマイグレーションファイルからデータベーススキーマを合成できます。
# このコマンドはオフラインで実行して、マイグレーションが有効なSQLであり、 # 明らかな構文エラーがないことを検証できます。 sqlx migrate info
sqlx-cli
は主にマイグレーションの実行に焦点を当てていますが、sqlx database diff
コマンドは、Rust構造体から直接オフラインスキーマ生成と比較、またはスキーマファイルで表される2つの異なるデータベース状態、またはライブデータベースの比較を目的とした強力な実験的な機能です。Rust構造体からのほとんどのオフラインスキーマ生成にはまだ成熟していませんが、その開発は、より洗練されたオフラインスキーマ管理への明確な道筋を示しています。現時点では、sqlx
ユーザーにとって主な「オフラインチェック」は、.sqlx
メタデータファイルによって可能になるコンパイル時チェックから得られることが多く、これはSQLX_OFFLINE
がtrue
に設定されている場合(またはsqlx prepare --check
によって)cargo check
によって暗黙的にチェックされます。
// src/main.rs #[macro_use] extern crate sqlx; #[tokio::main] async fn main() -> Result<(), sqlx::Error> { // 本番アプリケーションでは、DATABASE_URLは環境変数からロードされます let database_url = std::env::var("DATABASE_URL") .expect("DATABASE_URL must be set"); let pool = sqlx::PgPool::connect(&database_url).await?; let row: (i64,) = sqlx::query_as("SELECT $1 FROM users") // `users`テーブルが存在しない場合、ここでエラー .bind(150_i64) .fetch_one(&pool) .await?; println!({}) .0; Ok(()) }
SQLX_OFFLINE=true
が設定されている場合、sqlx
はキャッシュされた情報を使用してクエリを検証し、cargo check
がクエリのオフラインスキーマ検証ツールとして効果的に機能します。これはCI/CDにとって信じられないほど強力な機能です。
diesel-cli:ORM主導のマイグレーションとスキーマ生成
diesel-cli
は、Diesel
ORMのコマンドラインツールです。Diesel
はデータベース操作に異なるアプローチを取り、マクロを使用してRustコード内で直接スキーマを定義することに焦点を当てています。これにより、クエリと操作に対して高いレベルの型安全性を提供できます。
diesel-cli
のスキーマ管理の中核は、schema.rs
ファイルとマイグレーションシステムを中心に展開されます。
-
Dieselの初期化:
diesel setup
このコマンドは、必要なフォルダと
diesel.toml
設定ファイルをセットアップします。 -
schema.rs
の生成: ここでdiesel-cli
はオフライン作業で真価を発揮します。diesel-cli
は、ライブデータベース接続を検査して、データベーステーブルと列のRust表現を含むsrc/schema.rs
ファイルを生成できます。diesel print-schema > src/schema.rs
この
schema.rs
ファイルは、Rustアプリケーション内のデータベーススキーマの単一の真実の源として機能します。しかし、この特定のコマンドはライブデータベース接続を必要とします。diesel-cli
のオフラインパワーは、マイグレーションシステムと、schema.rs
が生成されると、アプリケーションはそのファイルに対して型チェックされ、ライブデータベースではないという事実から生まれます。schema.rs
をマイグレーションと最新の状態に保つことを手動で確認できます。 -
新しいマイグレーションの作成:
diesel migration generate create_posts_table
sqlx-cli
と同様に、これによりマイグレーション用のup.sql
およびdown.sql
ファイルが作成されます。 -
マイグレーションSQLの記述:
-- migrations/20231027100000_create_posts_table/up.sql CREATE TABLE posts ( id SERIAL PRIMARY KEY, title VARCHAR NOT NULL, body TEXT NOT NULL, published BOOLEAN NOT NULL DEFAULT FALSE );
-- migrations/20231027100000_create_posts_table/down.sql DROP TABLE posts;
-
マイグレーションの適用:
diesel migration run
これにより、マイグレーションがデータベースに適用されます。
diesel-cli
でのオフラインチェックの主なユースケースは、src/schema.rs
がマイグレーションの状態を、ライブデータベースに接続せずに正確に反映していることを確認する必要がある場合に発生します。diesel print-schema
は接続を必要としますが、一時的なインメモリデータベース(SQLiteのような選択したバックエンドで利用可能なら)またはCIで起動および破棄されるDocker化されたテストデータベースに統合できます。PostgreSQLまたはMySQLの場合、これは通常、テストデータベースのセットアップを伴います。
ただし、真のオフラインパワーは、Rustコードの型安全性がschema.rs
によって保証されているという事実から生まれます。schema.rs
を一度生成してしまい、その後アプリケーションをビルドするだけで、Rustコンパイラはschema.rs
がRust構造体と一致しなくなった場合にエラーをキャッチします。
簡単なDiesel
アプリケーションを考えてみましょう。
// src/schema.rs (diesel print-schemaで生成、またはマイグレーションごとに手動で維持) diesel::table! { posts (id) { id -> Int4, title -> Varchar, body -> Text, published -> Bool, } } // src/models.rs use crate::schema::posts; use diesel::Queryable; #[derive(Queryable)] pub struct Post { pub id: i32, pub title: String, pub body: String, pub published: bool, } // src/main.rs use diesel::prelude::*; use diesel::pg::PgConnection; fn main() { let database_url = std::env::var("DATABASE_URL") .expect("DATABASE_URL must be set"); let mut connection = PgConnection::establish(&database_url) .expect("Error connecting to database"); // このクエリは `src/schema.rs` に対して型チェックされます let results: Vec<crate::models::Post> = posts::table .filter(posts::published.eq(true)) .limit(5) .load(&mut connection) .expect("Error loading posts"); println!("Found {} published posts.", results.len()); }
マイグレーションを変更した場合(例:posts
テーブルに新しい列を追加した場合)、src/schema.rs
を更新しないと、Rustコードが古いschema.rs
に依存している場合、cargo build
またはcargo check
は失敗する可能性が高いです。その後、一時的なデータベースでマイグレーションを実行し、src/schema.rs
を再生成することでスキーマ変更を再評価できます。このプロセスにより、ライブで運用可能なデータベースインスタントが常に利用可能である必要なしに、整合性が保証されます。
CI/CDの場合、diesel migration check
コマンドは、schema.rs
ファイルを、すべてのマイグレーションが実行された場合に生成されるものと比較し、不一致をフラグ付けできます。
# このコマンドは、マイグレーションを実行してスキーマを比較するために # データベース接続を必要としますが、インメモリまたは一時的なDBを指すことができます。 diesel migration check
真のオフラインチェックは、schema.rs
が目的のスキーマを反映して生成された後のcargo-check
から得られます。schema.rs
によって記述されたデータベースとRust構造体の相互作用の方法からの逸脱は、コンパイルエラーにつながります。
結論
sqlx-cli
とdiesel-cli
はどちらもRustでのデータベースマイグレーションとスキーマ管理のための堅牢なソリューションを提供します。sqlx-cli
は、既存のデータベースまたは独自のメタデータから派生したスキーマに対して生のSQLクエリのコンパイル時チェックを行うのに優れており、SQLxユーザーに強力な「オフライン」クエリ検証を提供します。一方、diesel-cli
はORMアプローチを活用してschema.rs
ファイルを生成し、そのファイルに対してRustコードが型チェックされます。ライブデータベース接続が永続的に利用できないシナリオであっても、これらのツールでスキーマとマイグレーションを管理および検証できる機能は、開発ワークフローを大幅に合理化し、CI/CDパイプラインを強化し、最終的により信頼性の高いRustアプリケーションにつながります。これらのCLIツールを習得することは、データベース駆動型アプリケーションを構築するすべてのRust開発者にとって不可欠です。