sqldefのメリットと使い方

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

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

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↻ 2024/12/18
中馬崇尋
Chuma Takahiro