Open4

DBマイグレーションツールのAtlas使ってみた

hott0mott0hott0mott0

まずは、下記のように、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にスキーマ、テーブルが作成されます。

hott0mott0hott0mott0

では、今度は作成したテーブルを 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;
hott0mott0hott0mott0

今度はこの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
hott0mott0hott0mott0

上記のエラーは、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ファイルで管理するのが良さそうです。