golang-migrateを使ってみた(CLI編)

dockerで環境を作成しています
```
使用イメージ : postgres:16
os : Debian GNU/Linux 12 (bookworm)
postgresql : psql (PostgreSQL) 16.4 (Debian 16.4-1.pgdg120+2)
```

### golang-migrateをインストール
```
# apt-get update && apt-get upgrade -y
# apt-get install tar gzip curl vim
# curl -L https://github.com/golang-migrate/migrate/releases/download/v4.18.0/migrate.linux-amd64.tar.gz | tar xvz
# mv ./migrate /usr/bin/migrate
```
利用できるかチェック
```
$ migrate -help
```
### 公式のPostgreSQLのチュートリアルを参考に進めていく

データベースの作成
```
psql -h localhost -U postgres -w -c "create database example;"
```
パスワードの変更をする場合は以下で行う
```
$ psql -h localhost -U postgres
postgres=# \password
Enter new password for user "postgres":
Enter it again:
```

テーブルの作成
```
$ migrate create -ext sql -dir db/migrations -seq create_users_table
/var/lib/postgresql/db/migrations/000001_create_users_table.up.sql
/var/lib/postgresql/db/migrations/000001_create_users_table.down.sql
```
CREATE文の作成
```
$ vim /var/lib/postgresql/db/migrations/000001_create_users_table.up.sql

CREATE TABLE IF NOT EXISTS users(
   id serial PRIMARY KEY,
   username VARCHAR (50) UNIQUE NOT NULL,
   password VARCHAR (50) NOT NULL,
   email VARCHAR (300) UNIQUE NOT NULL,
   created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```
DROP文の作成
```
vim /var/lib/postgresql/db/migrations/000001_create_users_table.down.sql

DROP TABLE IF EXISTS users;
```

マイグレーションの実行前にテーブルの確認
```
$ psql example -c "\d users"

Did not find any relation named "users".
```
データベースURLが必要とのことなのでエクスポートを行う
```
$ export POSTGRESQL_URL='postgres://postgres:password@localhost:5432/example?sslmode=disable'
```
マイグレーションの実行
```
$ migrate -database ${POSTGRESQL_URL} -path db/migrations up

1/u create_users_table (12.606125ms)
```
テーブルの確認
```
$ sql example -c "\d users"

                                      Table "public.users"
  Column  |          Type          | Collation | Nullable |                Default
----------+------------------------+-----------+----------+----------------------------------------
 user_id  | integer                |           | not null | nextval('users_user_id_seq'::regclass)
 username | character varying(50)  |           | not null |
 password | character varying(50)  |           | not null |
 email    | character varying(300) |           | not null |
Indexes:
    "users_pkey" PRIMARY KEY, btree (user_id)
    "users_email_key" UNIQUE CONSTRAINT, btree (email)
    "users_username_key" UNIQUE CONSTRAINT, btree (username)
```
マイグレーションを行った内容を戻す
```
$ migrate -database ${POSTGRESQL_URL} -path db/migrations down

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

マイグレーションの管理はschema_migrationsテーブルで行っているようです  
https://github.com/golang-migrate/migrate/blob/master/database/postgres/README.md
```
upを実行後
$ psql ${POSTGRESQL_URL} -c "select * from schema_migrations;"
 version | dirty
---------+-------
       1 | f
(1 row)

downを実行後
$ psql ${POSTGRESQL_URL} -c "select * from schema_migrations;"
 version | dirty 
---------+-------
(0 rows)
```

2回目のマイグレーションファイルを作成する
```
$ migrate create -ext sql -dir db/migrations -seq create_todo_table

$ vim /var/lib/postgresql/db/migrations/000002_create_todo_table.up.sql

CREATE TABLE todo (
    id SERIAL PRIMARY KEY,
    user_id INT NOT NULL,
    title VARCHAR(255) NOT NULL,
    description TEXT,
    due_date DATE,
    is_completed BOOLEAN DEFAULT FALSE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

$ vim /var/lib/postgresql/db/migrations/000002_create_todo_table.down.sql

DROP TABLE IF EXISTS todo;
```

２回目のマイグレーションを実行
```
$ migrate -database ${POSTGRESQL_URL} -path db/migrations up
```
```
$ psql example -c "\d todo"
                                         Table "public.todo"
    Column    |            Type             | Collation | Nullable |             Default
--------------+-----------------------------+-----------+----------+----------------------------------
 id           | integer                     |           | not null | nextval('todo_id_seq'::regclass)
 user_id      | integer                     |           | not null |
 title        | character varying(255)      |           | not null |
 description  | text                        |           |          |
 due_date     | date                        |           |          |
 is_completed | boolean                     |           |          | false
 created_at   | timestamp without time zone |           |          | CURRENT_TIMESTAMP
Indexes:
    "todo_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
    "todo_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
```
問題なくテーブルの作成されています。

チュートリアルにあるenum_moodを作成します。
```
$ migrate create -ext sql -dir db/migrations -seq add_mood_to_users
```
```
$ vim /var/lib/postgresql/db/migrations/000003_add_mood_to_users.up.sql
$ vim /var/lib/postgresql/db/migrations/000003_add_mood_to_users.down.sql
```
```
$ migrate -database ${POSTGRESQL_URL} -path db/migrations up
```
gotoで特定バージョンまでdownする
```
$migrate -database ${POSTGRESQL_URL} -path db/migrations goto 1
```
バージョン2,3で作成したtodoテーブルとenum_moodが削除されていることを確認できました。
```
$ psql example -c "\d users"
                                        Table "public.users"
   Column   |            Type             | Collation | Nullable |              Default
------------+-----------------------------+-----------+----------+-----------------------------------
 id         | integer                     |           | not null | nextval('users_id_seq'::regclass)
 username   | character varying(50)       |           | not null |
 password   | character varying(50)       |           | not null |
 email      | character varying(300)      |           | not null |
 created_at | timestamp without time zone |           |          | CURRENT_TIMESTAMP
Indexes:
    "users_pkey" PRIMARY KEY, btree (id)
    "users_email_key" UNIQUE CONSTRAINT, btree (email)
    "users_username_key" UNIQUE CONSTRAINT, btree (username)
```
```
$ psql example -c "\d"
                List of relations
 Schema |       Name        |   Type   |  Owner
--------+-------------------+----------+----------
 public | schema_migrations | table    | postgres
 public | users             | table    | postgres
 public | users_id_seq      | sequence | postgres
(3 rows)
```

### まとめ
チュートリアルを参考にgolang-migrateを動かしてみました。使い方に関しては特に複雑なことはなく基本的な使い方は、時間をかからず理解できるのかなと感じました。  
今回はCLIで動かしてみましたが、goアプリからも使うことができるようなので次回はgoアプリ内で動かしてみようと思います。