🐹

golang-migrateでのPostgreSQLマイグレーション備忘録

2022/01/24に公開

はじめに

最近 Go での WebAPI 制作に挑戦しはじめて、DB のマイグレーション周りをはじめて触ったので備忘録として書きました。

詳細

マイグレーションツールには golang-migrate を使用しました。
現在 ORM に使っている GORM にもマイグレーションの機能がありますが、機能が乏しそうだったので専用ツールの golang-migrate を使ってみました。
選定基準は検索して一番上に出てきたからです!!!!

DB 立ち上げ

PostgreSQL の公式 Docker イメージを使ってローカルで立ち上げました。
コンテナを一度消したあともデータを持ち越せるよう、名前付きボリュームを設定しています。(匿名でもバインドでもよい)
環境変数のとこはサンプルなので適当に...デフォルトではユーザー名がそのまま DB 名になります。

docker-compose.yml
version: "3"
services:
  db:
    image: postgres:14.1-alpine
    ports:
      - 5432:5432
    volumes:
      - sample:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: admin
      POSTGRES_PASSWORD: admin-password
      LANG: ja_JP.utf8
      TZ: Asia/Tokyo
volumes:
  sample:
    driver: local

起動しときましょう!

$ docker-compose up -d

CLI で試す

最終的にコード内に記述しますが、一旦ローカルにインストール

$ brew install golang-migrate

あとは公式チュートリアルの流れに従って...

$ migrate create -ext sql -dir db/migrations -seq create_bookmarks_table

すると、db/migrations 以下に 000001_create_bookmarks_table.up.sql000001_create_bookmarks_table.down.sql ファイルが生成されています!

数字の部分がバージョン、その後に-seq で指定したファイル名が入り、updown 用がそれぞれ存在する感じですね。up でバージョンを上げ、downで落とすので、downup で書いた内容をそのまま逆に行う SQL を書くってことですね(migration 初心者なのではじめて知りました)。

早速テーブルの作成(up)を書きます。(URL の最大文字数が 50 なのは気にしないでください...)

000001_create_bookmarks_table.up.sql
CREATE TABLE IF NOT EXISTS bookmarks(
   id serial PRIMARY KEY,
   url VARCHAR (50) UNIQUE
);

続いて down の方も書きます、up の逆なので DROP TABLE ですね。

000001_create_bookmarks_table.down.sql
DROP TABLE IF EXISTS bookmarks;

一旦現時点の DB 構成を見ると...

$ export POSTGRESQL_URL=postgres://admin:admin-password@localhost:5432/admin?sslmode=disable
$ psql ${POSTGRESQL_URL}
psql (14.1)
Type "help" for help.

admin=# \d
             List of relations
 Schema |       Name        | Type  | Owner
--------+-------------------+-------+-------
 public | schema_migrations | table | admin
(1 row)

admin=#

schema_migrations テーブルは、バージョン(version)とそのバージョンへの変更が正常に行われているか(dirty)を保存してるテーブルです。

ここで先程書いたバージョン 1 を適用してみましょう。

$ migrate -database ${POSTGRESQL_URL} -path tools/db/migration up 1
1/u create_bookmarks_table (23.938542ms)

もう一度 DB を確認すると...

$ psql ${POSTGRESQL_URL}
psql (14.1)
Type "help" for help.

admin=# \d
               List of relations
 Schema |       Name        |   Type   | Owner
--------+-------------------+----------+-------
 public | bookmarks         | table    | admin
 public | bookmarks_id_seq  | sequence | admin
 public | schema_migrations | table    | admin
(3 rows)

admin=#

テーブルとシーケンスが作成されてます!

今度はバージョンを落として DB 構成を確認します。

$ migrate -database ${POSTGRESQL_URL} -path tools/db/migration down
Are you sure you want to apply all down migrations? [y/N]
y
Applying all down migrations
1/d create_bookmarks_table (35.738583ms)

$ psql $POSTGRESQL_URL
psql (14.1)
Type "help" for help.

admin=# \d
             List of relations
 Schema |       Name        | Type  | Owner
--------+-------------------+-------+-------
 public | schema_migrations | table | admin
(1 row)

admin=#

先ほど作成されたものが削除されていますね。

Go のコードから試す

実際のところは、go runしたときにマイグレーションも実行したいことが多いと思うので、コード内への記述をします。
migrate.New()の引数に、プロジェクトのルートから見たマイグレーションファイルの path と、CLI でも使用した URL を渡します。

package config

import (
	"github.com/golang-migrate/migrate/v4"
	_ "github.com/golang-migrate/migrate/v4/database/postgres"
	_ "github.com/golang-migrate/migrate/v4/source/file"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/postgres"
)

--中略--

m, err := migrate.New(
	"file://tools/db/migration",
	"postgres://admin:admin-password@localhost:5432?sslmode=disable",
)
if err != nil {
	panic(err)
}
if err := m.Up(); err != nil {
	if err != migrate.ErrNoChange {
		panic(err)
	}
}

特にバージョンアップがなかった場合エラーが出てしまったので、自分は ErrNoChange だけ許容するようにしてます。
これでマイグレーションが完了します!

まとめ

仕事ではフロントエンドとインフラ周りしか触ったことがないのでマイグレーション自体はじめてでしたが、コマンドが直感的でわかりやすく、初心者に優しいパッケージでした 😄
他の有名なツールも触っていきたいと思います。

GitHubで編集を提案

Discussion