仕事で携わっているウェブアプリケーションでは、DBのマイグレーションにgolang-migrateを使っています。
DBをマイグレーションするだけのツールとしてはこれで十分なのですが、現在のスキーマダンプ、Railsでいうところのdb/structure.sqlをダンプする機能がなくて困っていたので、それ相当のファイルを作成する方法を考えました。
スキーマダンプには
- DBスキーマの信頼できる情報源となり、現在のスキーマを概観できる
- ダンプにスキーマのバージョンが記録されていて、バージョン管理システムの異なるブランチで同時に作業したとき、両方をマージしようとするとコンフリクトするので不整合に気づきやすい
などの利点があり*1、Railsのようなフレームワークを使っていなくとも、これに相当するファイルがあると便利です。
ファイルを作成する手順としては、DBからスキーマとそのバージョンを取得し、ファイルに書き出すだけです。
仕事ではMySQLを使っているので、bashとMySQLのコマンドラインツールを使った例を示します。ツールは例示したものである必要はなく、スキーマとそのバージョンが取得できればやり方はなんでも構いません。他の言語・DBでも同じ考え方でスキーマダンプを作成できるでしょう。
DBからスキーマのバージョンを取得してファイルに書き出す
まずはスキーマのバージョンを取得します。golang-migrateはDBがMySQLの場合、デフォルトではschema_migrationsテーブルにバージョンを記録するので、それをmysqlコマンドで取得します*2。
$ echo "/* Schema version: $( mysql \ --host 127.0.0.1 \ --port 13306 \ --user root \ --password=root \ --skip-column-names \ --silent \ --execute "SELECT version FROM database.schema_migrations" ) */" > structure.sql
mysqlコマンドは単に --execute
オプションを指定するだけだと、罫線を使った表を出力しますが、今回はカラム名は必要なく、SELECTした値だけが欲しいので、 --silent
も指定しています。
なお、パスワードを直接指定しているのはあくまで例示のためで、実際には環境変数などを介して渡すのが良いでしょう(次に紹介するmysqldumpも同様です)。
DBからスキーマを取得してファイルに追記する
次にDBスキーマをダンプします。mysqldumpを使って、先ほどバージョンを出力したファイルにスキーマを追記します。
mysqldump \ --host 127.0.0.1 \ --port 13306 \ --user root \ --password=root \ --compact \ --no-data \ --single-transaction \ --skip-dump-date \ database \ >> structure.sql
database
はダンプしたいデータベースの名前です。
mysqldumpはデフォルトだとテーブルの行や様々なコメントなどを出力しますが、今回はスキーマだけ出力できればいいので、 --compact
--no-data
オプションを使って CREATE TABLE
文だけが出力されるようにします。
また、--single-transaction
を指定してトランザクション内でダンプすることで、仮にダンプ中にスキーマが変更されても出力結果が変わらないようにします。
--skip-dump-date
オプションは、スキーマの変更以外でダンプファイルが変更されないように、ダンプした日付の出力をスキップする設定です。
これらのコマンドをシェルスクリプトにしておけば、いつでも誰でも手元のスキーマをファイルにダンプできるようになります。
さらに、ダンプをソースコードリポジトリにコミットしておいて、CIでもスキーマダンプを生成し、コミット済みのものと差分があったら失敗するようにしておけば、マイグレーションの適用漏れなどのも不整合も発生しにくくなるでしょう。
*1:Active Record マイグレーション - Railsガイド
*2:migrateコマンドでも取得できるのですが、ダンプを出力する環境でmigrateとMySQLコマンドラインツールを両方インストールするのが面倒だったので、mysqlコマンドを使います