sqldef のメリット・デメリットと使い方

sqldefは RDBMS のスキーマを config に沿って更新する CLI ツールです。
先行実装 ridgepoleの Go クローンと言えます。

Web フレームワークで標準的なマイグレーションと異なり、スキーマバージョン間の差分ではなく要求スキーマを一括で定義するものです。
マイグレーションは仕上がりのスキーマを DB に実装しないと確認できませんが、ridgepole や sqldef は config が仕様であり、この点が決定的に異なります。

production 運用のネック

sqldef は現状、構造的なネックがあります。

多くの場合、パーサの解釈がデータベースと異なることから来るエラーです。
sqldef はまず初動で実データベースのオブジェクトを幅広くスキャンして解釈しますが、その過程でパーサが解釈できない部分があると異常終了します。

典型的には、関数など非互換の大きいオブジェクトを読むと、それがじっさいに機能している SQL であっても syntax error でマイグレーション不能になります。
インプットの定義スキーマをテーブルだけに限定したとしても防げません。

これは production など複雑度が高い環境ほど動作しない傾向がある、ということです。動作しないので危険性は低いとも言えますが、現時点では明らかに用途の限界がセットされている状況です。

sqldef と ridgepole の違い

sqldef と ridgepole は目的が同じであるためユーザーは競合するのですが、config の言語が異なるため決定的に優劣が付くことはないでしょう。プロジェクトが重視する点に応じて選択します。

  • sqldef のスキーマ定義は SQL。SQL は完全互換でないこともあり、DBMS ごとに mysqldef/psqldef/sqlite3def/mssqldef があり挙動も異なる
  • ridgepole のスキーマ定義は Rails マイグレーション DSL。原則として DBMS 間で記述が共通

両者の違いは外から機能比較しても理解できません。
いずれのツールも主要機能として--exportオプションを備えているため、運用中の DB スキーマを config にダンプして見比べることが有効です。

最後は目視検証

マイグレーションや sqldef, ridgepole は DB スキーマの品質維持を強力に支援します。
ただし、いずれのツールを採用したとしても、スキーマを正確に反映できていることや目的に沿った挙動になっていることには別途検証が必要です。

DB を含めた挙動確認にはユニットテストは無力で、インテグレーションテストが必要になります。
そしてインテグレーションテストを積み上げたとしても、矛盾がないことの保証は得られないため、最終的にはスキーマを手作業で目視検証することが重要であるように思います。

ridgepole や sqldef のスキーマファイルは DB 内の全テーブルを見渡せるため、論理的な検証をしやすい点がポイントと言えます。
行きがかり上、定義と実装が乖離する展開もあるのですが、その場合にも素朴に見比べやすいメリットがあります。

psqldef の運用

既述のとおり sqldef は DBMS ごとの CLI を提供しており、PostgreSQL 向けにはpsqldefを PATH の有効ディレクトリに置いて使います。

# エクスポート
$ psqldef -U<DB user> --export [--skip-view] <DB name> [> <schema filename>]

# 適用
$ cat <schema filename> | psqldef -U<DB user> [--dry-run] [--skip-view] <DB name>

ユーザー指定のみ記載しましたが、ホストやパスワードのオプションは標準のpsqlコマンドに準じて指定します。

なお、想定どおりに動作しない場合、カラムがデータごと消える挙動などが起きうることをあらかじめ理解しておきましょう。
カラム名変更のつもりで削除&追加の挙動になるといった致命的な凡ミスがありえるため、マイグレーションとはメンタルモデルを切り替えなくてはなりません。

適用する前には、実 DB の最新バックアップを取得しておくことが最重要です
また、予行演習として--dry-runの出力を確認しておくことも有効です。

psql との併用

psqldef はテーブルに加えて View や MaterializedView なども追跡するのですが、View は複雑な SELECT 文をサポートすることから、エラーになることがあります。
--skip-viewオプションをつけると、これらを管理対象から除外します。この挙動の違いは--exportすると確認できます。

ただし冒頭のとおり、スキップできないもの多くあります。

View を含めCREATE OR REPLACEをサポートしている DB オブジェクトについては、もとより標準のpsqlコマンドで適切に更新できるため、psqlpsqldefを併用することである程度複雑な DB も管理できます。

⁋ 2023/11/16↻ 2026/03/18
中馬崇尋
Chuma Takahiro