データベースマイグレーション管理ツール dbmate

目的

  • 未公開記事の発掘
  • upとdownを同じファイルに書けるのは無駄なくてよさそう

amacneil/dbmate でデータベースマイグレーションを始めるときのガイドです。

依存関係は少ないし、凝った動作もしないのでなんかよさそう。

仕組み

  • 管理対象データベースに schema_migrations というテーブルを作成し、マイグレーションスクリプトの適用状況を追跡します
  • データベースのローカルオブジェクト (テーブルやビューやシーケンスなど) を管理します
  • データベースのグローバルオブジェクト (ユーザーや権限など) は管理しません
  • データベーススキーマをダンプするときはデータベースソフトウェア固有のコマンドを利用します

準備

amacneil/dbmate のインストール

Go が入ってるなら go install するだけ。

そうでなければリリースアセットから対象OSの実行可能ファイルをダウンロードします。

go install github.com/amacneil/dbmate@latest

サンプルリポジトリのダウンロード

Sakila データベースMySQL (8.0.21) と PostgreSQL (13) のコンテナにロードする docker-compose.yml ファイルを用意しました。

dbmate は 8.x 以上の MySQL に対応しています (5.x には対応してません)

$ git clone https://bitbucket.org/yujiorama/db-migration-base-202009
$ cd db-migration-base-202009
$ docker-compose up -d
$ docker-compose ps
        Name                       Command              State                 Ports
-------------------------------------------------------------------------------------------------
mysql-demo_mysql_1      docker-entrypoint.sh mysqld     Up      0.0.0.0:3306->3306/tcp, 33060/tcp
mysql-demo_postgres_1   docker-entrypoint.sh postgres   Up      0.0.0.0:5432->5432/tcp

後始末するときはこちらを。

# ボリュームも一緒に削除します
docker-compose down -v

ガイド

1 新しい DDL/DML を用意する

new サブコマンドを実行すると、新しい DDL/DML を記述する SQL ファイルを作成します。

引数は SQL ファイル名の一部になります。

$ dbmate --url "mysql://user:password@host:port/db" new create-todo-table
Creating migration: db/migrations/yyyymmddHHMMSS_create-todo-table.sql

SQL ファイルはコメントで 2 つの部分に分かれています。

-- migrate:up


-- migrate:down
  • migrate:up には up サブコマンドで実行する SQL を記述します
  • migrate:down には rollback サブコマンドで実行する SQL を記述します
  • それぞれの部分には何行でも SQL を記述できます

ファイルを作成するディレクトリ名は --migrations-dir オプション、あるいは、環境変数 DBMATE_MIGRATIONS_DIR で指定できます。

環境別にディレクトリを分けるやり方はたぶん事故ります。

ブランチを別にするか、リポジトリを別にしたほうがいいでしょう。

$ dbmate --url "mysql://user:password@host:port/db" --migrations-dir mydb/migrations new create-todo-table
Creating migration: mydb/migrations/20200928093003_create-todo-table.sql

$ DBMATE_MIGRATIONS_DIR=otherdb/migrations dbmate --url "mysql://user:password@host:port/db" new create-todo-table
Creating migration: otherdb/migrations/20200928093003_create-todo-table.sql

2 用意した DDL/DML を適用する

up サブコマンドは SQL ファイルの migrate:up 部分を実行できます。

schema_migrations テーブルを検索して適用済みかどうかを判断するようです。

$ dbmate --url mysql://user:pass1word@minikube:3306/sakila up
Applying: 20200928090841_create-todo-table.sql

status サブコマンドを実行すると適用済みかどうかを確認できます。

$ dbmate --url mysql://user:pass1word@minikube:3306/sakila status
[X] 20200928090841_create-todo-table.sql

Applied: 1
Pending: 0

なお、複数 Pending がある状態で up すると順番に実行していきます。

$ dbmate --url mysql://user:pass1word@minikube:3306/sakila up
Applying: 20200928093936_create-todo-table.sql
Applying: 20200928094214_create-todo-table.sql

$ dbmate --url mysql://user:pass1word@minikube:3306/sakila status
[X] 20200928090841_create-todo-table.sql
[X] 20200928093936_create-todo-table.sql
[X] 20200928094214_create-todo-table.sql

Applied: 3
Pending: 0

3 適用した DDL/DMLロールバックする

rollback サブコマンドは SQL ファイルの migrate:down 部分を実行できます。

最後に適用した SQL ファイル、つまり、最後に Applied になった SQL ファイルを対象にするようです。

$ dbmate --url mysql://user:pass1word@minikube:3306/sakila status
[X] 20200928090841_create-todo-table.sql
[X] 20200928093936_create-todo-table.sql
[ ] 20200928094214_create-todo-table.sql

Applied: 2
Pending: 1

$ dbmate --url mysql://user:pass1word@minikube:3306/sakila rollback
Rolling back: 20200928093936_create-todo-table.sql

$ dbmate --url mysql://user:pass1word@minikube:3306/sakila status
[X] 20200928090841_create-todo-table.sql
[ ] 20200928093936_create-todo-table.sql
[ ] 20200928094214_create-todo-table.sql

Applied: 1
Pending: 2

なお、 migrate:down の部分に構文エラーがあると実行時エラーになります。

たぶん実行時エラーの場合もですが、ロールバックしたことにはなりません。

$ dbmate --url mysql://user:pass1word@minikube:3306/sakila status
[X] 20200928090841_create-todo-table.sql
[X] 20200928093936_create-todo-table.sql
[X] 20200928094214_create-todo-table.sql

Applied: 3
Pending: 0

$ echo '1;' >> db/migrations/20200928094214_create-todo-table.sql

$ dbmate --url mysql://user:pass1word@minikube:3306/sakila rollback
Rolling back: 20200928094214_create-todo-table.sql
Error: Error 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '1' at line 3

$ dbmate --url mysql://user:pass1word@minikube:3306/sakila status
[X] 20200928090841_create-todo-table.sql
[X] 20200928093936_create-todo-table.sql
[X] 20200928094214_create-todo-table.sql

Applied: 3
Pending: 0

4 schema_migrations テーブル

createuprollback を最初に実行するとき、接続先URLで指定したデータベースへ作成します。

$ mysql --user user -p -h minikube sakila
Enter password: *********
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 33
Server version: 8.0.21 MySQL Community Server - GPL

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> desc schema_migrations;
+---------+--------------+------+-----+---------+-------+
| Field   | Type         | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| version | varchar(255) | NO   | PRI | NULL    |       |
+---------+--------------+------+-----+---------+-------+
1 row in set (0.00 sec)

mysql> select * from schema_migrations;
+----------------+
| version        |
+----------------+
| 20200928090841 |
| 20200928093936 |
| 20200928094214 |
+----------------+
3 rows in set (0.00 sec)