DBマイグレーションツールのAtlas使ってみた
まずは、下記のように、MySQL のDB、テーブル定義を HCL形式で作成します。
schema "prd_user" {
charset = "utf8mb4"
collate = "utf8mb4_0900_ai_ci"
comment = "A prd_user schema comment"
}
table "users" {
schema = schema.prd_user
column "id" {
type = int
auto_increment = true
null = false
}
column "user_name" {
type = varchar(64)
null = false
default = ""
}
column "email" {
type = varchar(256)
null = false
default = ""
}
column "prefecture" {
type = tinyint
null = false
default = 0
}
column "birthday" {
type = date
null = false
default = "1000-01-01"
}
column "gender" {
type = tinyint
null = false
default = 0
}
column "created_at" {
type = datetime
null = false
default = sql("CURRENT_TIMESTAMP")
}
column "updated_at" {
type = datetime
null = false
default = sql("CURRENT_TIMESTAMP")
on_update = sql("CURRENT_TIMESTAMP")
}
column "deleted" {
type = bool
null = false
default = false
}
primary_key {
columns = [
column.id
]
}
index "unique_index_user_name_asc" {
columns = [
column.user_name
]
unique = true
}
}
そして、以下のコマンドでマイグレーションしてみます。
atlas schema apply -u "mysql://${DB_USER}:${DB_PASSWORD}@localhost:13306" --to file://./atlas/mysql/user.hcl
すると、以下のように表示されます。
-- Planned Changes:
-- Create "users" table
CREATE TABLE `prd_user`.`users` (
`id` int NOT NULL AUTO_INCREMENT,
`user_name` varchar(64) NOT NULL DEFAULT "",
`email` varchar(256) NOT NULL DEFAULT "",
`prefecture` tinyint NOT NULL DEFAULT 0,
`birthday` date NOT NULL DEFAULT "1000-01-01",
`gender` tinyint NOT NULL DEFAULT 0,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`deleted` bool NOT NULL DEFAULT false,
PRIMARY KEY (`id`),
UNIQUE INDEX `unique_index_user_name_asc` (`user_name`),
);
? Are you sure?:
▸ Apply
Lint and edit (requires login)
Abort
Apply を押すと、DBにスキーマ、テーブルが作成されます。
では、今度は作成したテーブルを Inspect しようと思います。
その際、出力形式に SQL を指定します。
具体的には以下のようなコマンドになります。
atlas schema inspect -u "mysql://${DB_USER}:${DB_PASSWORD}@localhost:13306" --format "{{ sql . }}"
実行すると、以下のようにスキーマがSQL形式で出力されました。
-- Add new schema named "prd_user"
CREATE DATABASE `prd_user` COLLATE utf8mb4_0900_ai_ci;
-- Create "users" table
CREATE TABLE `prd_user`.`users` (
`id` int NOT NULL AUTO_INCREMENT,
`user_name` varchar(64) NOT NULL DEFAULT "",
`email` varchar(256) NOT NULL DEFAULT "",
`prefecture` tinyint NOT NULL DEFAULT 0,
`birthday` date NOT NULL DEFAULT "1000-01-01",
`gender` tinyint NOT NULL DEFAULT 0,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`deleted` bool NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE INDEX `unique_index_user_name_asc` (`user_name`),
) CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
今度はこのSQLスキーマに、hobby
というカラムを追加してApplyしてみます。
-- Add new schema named "prd_user"
CREATE DATABASE IF NOT EXISTS `prd_user` COLLATE utf8mb4_0900_ai_ci;
-- Create "users" table
CREATE TABLE IF NOT EXISTS `prd_user`.`users` (
`id` int NOT NULL AUTO_INCREMENT,
`user_name` varchar(64) NOT NULL DEFAULT "",
`email` varchar(256) NOT NULL DEFAULT "",
`prefecture` tinyint NOT NULL DEFAULT 0,
`birthday` date NOT NULL DEFAULT "1000-01-01",
`gender` tinyint NOT NULL DEFAULT 0,
+ `hobby` text NOT NULL DEFAULT "",
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`deleted` bool NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE INDEX `unique_index_user_name_asc` (`user_name`),
) CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
Applyコマンドは以下です。
(最後のファイル指定が SQLファイルになっています)
atlas schema apply -u "mysql://${DB_USER}:${DB_PASSWORD}@localhost:13306" --to file://./atlas/mysql/user.sql
すると、以下のようなエラーが表示されました。
Error: --dev-url cannot be empty. See: https://atlasgo.io/atlas-schema/sql#dev-database
上記のエラーは、dev-url
というオプションが必須であるという旨を示しています。
Atlas はどうやら、マイグレーションの差分検証と実際のApplyで別々のデータベースを使わないといけない仕様のようです。(参考1, 参考2)
最初の Apply でこのオプションがなくてもエラーが出なかったのは、該当するスキーマ、テーブルが存在せず、差分チェックをすることなくApplyできる状態だったからですね。
ということで、コマンドを修正して以下のようなコマンドを実行します。
atlas schema apply -u "mysql://${DB_USER}:${DB_PASSWORD}@localhost:13306" --to file://./atlas/mysql/user.sql --dev-url "docker://mysql/8"
すると、現状のスキーマ、テーブルとの差分がチェックされ、以下のようにマイグレーション用のSQLが出力されます。
-- Planned Changes:
-- Modify "users" table
ALTER TABLE `prd_user`.`users` ADD COLUMN `hobby` text NOT NULL;
? Are you sure?:
▸ Apply
Lint and edit (requires login)
Abort
これでカラム追加のマイグレーションが完了しました。
理想状態のSQLファイルを更新すれば、自分でマイグレーション用のSQLを書かずに済み、マイグレーションのSQLファイルが増えていくことが無くなるので、とてもいいですね。
最初はHCLで定義しましたが、SQLでも同様に使えるので、SQLファイルで管理するのが良さそうです。