先々月くらいにPrismaを知り、関連の記事をいろいろ書いていました。
- prisma-client-pythonでpythonからORMを楽にやる
- prisma client pythonが実行しているNodeはどこにあるのか
- prismaでfactoryboyみたいなやつが作りたい
ただ、最近Rustを触ることが多くてPrismaの使いどころがマイグレーションぐらいしかないのでちょっとしょんぼりしてたんですが、関連してちょっと気になるツールを見つけたので勉強がてら紹介します。
GitHub - k0kubun/sqldef: Idempotent schema management for MySQL, PostgreSQL, and more
このツールを使うと、Prismaでいうschema.prisma
をSQLファイルで管理できるようになるとのこと!
すごい柔軟性!!!!!
つまり、1つのSQLファイルと現在のDBを比較して、追加・変更・削除されたテーブルや列などの差分情報を読み取り、マイグレーションを行ってくれるという感じです。
ちなみに、sqlx関連で調べて見ていた参考にしていたブログはこちら。
[Rust] sqlxを使ってみる #Rust - Qiita
インストール
まずは使用しているUbuntuにダウンロードしてみます。
私は普段使いがPostgresなので、以下のコマンドでpsqldefをインストールします。
curl -OL https://github.com/k0kubun/sqldef/releases/download/v0.16.9/psqldef_linux_amd64.tar.gz
sudo tar xf psqldef_linux_amd64.tar.gz -C /usr/local/bin/
インストールできたか確認してみます。
% psqldef --help
Usage:
psqldef [option...] db_name
Application Options:
-U, --user=username PostgreSQL user name (default: postgres)
-W, --password=password PostgreSQL user password, overridden by $PGPASSWORD
-h, --host=hostname Host or socket directory to connect to the PostgreSQL server (default: 127.0.0.1)
-p, --port=port Port used for the connection (default: 5432)
--password-prompt Force PostgreSQL user password prompt
-f, --file=filename Read schema SQL from the file, rather than stdin (default: -)
--dry-run Don't run DDLs but just show them
--export Just dump the current schema to stdout
--enable-drop-table Enable destructive changes such as DROP (enable only table drops)
--before-apply= Execute the given string before applying the regular DDLs
--config= YAML file to specify: target_tables, skip_tables
--help Show this help
--version Show this version
ヘルプコマンドが表示されたので、ちゃんとインストールできましたね。
使い方
適当にdocker-composeでDBを起動しておきます。
以下のdocker-composeファイルを作成しておきます。
version: '3'
services:
db:
image: postgres:15
container_name: postgres
ports:
- 5432:5432
environment:
- POSTGRES_PASSWORD=example
これを起動して、今回使用するDBも適当に作っておきましょう。
(今回は「psqldef」という名前で作りました)
% psql -h localhost -p 5432 -U postgres
Password for user postgres:
psql (14.9 (Ubuntu 14.9-0ubuntu0.22.04.1), server 15.4 (Debian 15.4-2.pgdg120+1))
WARNING: psql major version 14, server major version 15.
Some psql features might not work.
Type "help" for help.
postgres=# create database psqldef;
CREATE DATABASE
まずはDBの管理に使用する「schema.sql」を作成します。
touch schema.sql
作成したファイルにUserテーブルを作成するクエリを書いておきます。
CREATE TABLE public.user (
id bigint NOT NULL,
name text,
age integer
);
ではこれを先程作成したデータベースに適用していきます。
% psqldef --password=example -h localhost -p 5432 -U postgres psqldef < schema.sql
-- Apply --
CREATE TABLE public.user (
id bigint NOT NULL,
name text,
age integer
);
さて、これでユーザーテーブルが作成されました。
psqldef=# \dt
List of relations
Schema | Name | Type | Owner
--------+------+-------+----------
public | user | table | postgres
(1 row)
次に、差分が変更されるかも確認してみましょう。
IDにUUIDが使えるようにしたのと、名前に制約を追加しましょう。
CREATE EXTENSION "uuid-ossp";
CREATE TABLE public.user (
id uuid NOT NULL DEFAULT uuid_generate_v4(),
name varchar(255),
age integer
);
この状態で再度マイグレーションを実行してみましょう。
% psqldef -h localhost -U postgres --password=example psqldef < schema.sql
-- Apply --
CREATE EXTENSION "uuid-ossp";
ALTER TABLE "public"."user" ALTER COLUMN "id" TYPE uuid;
2023/10/07 17:09:37 pq: column "id" cannot be cast automatically to type uuid
自動でALTER TABLE
が実行されていますが、失敗してしまいました。
もとのデータをキャストすることができないので、以下のようなALTER TABLE
が実行されないとダメですね。
ALTER TABLE "public"."user" ALTER COLUMN "id" SET DATA TYPE UUID USING (uuid_generate_v4());
これを手動で実行してあげて、再度マイグレーションを実行します。
% psqldef -h localhost -U postgres --password=example psqldef < schema.sql
-- Apply --
ALTER TABLE "public"."user" ALTER COLUMN "id" SET DEFAULT uuid_generate_v4();
ALTER TABLE "public"."user" ALTER COLUMN "name" TYPE varchar(255);
残りのマイグレーションが実行されました。
まとめ
もちろん、上記のようにIDをintから途中でUUIDにするようなDB設計はあまりいいとは言えません。
しかし、複数環境を運用する場合でこういったエラーが出た場合はどうすればよいでしょうか?
一応--before-apply
というオプションを使用すれば事前に適用可能なSQLの指定ができるので、エラーが起こったらそのたびに--before-apply
として適用できるSQLを用意するという対応策は考えられますね。
先の例であれば、以下のような差分SQLを用意する必要があります。
CREATE EXTENSION "uuid-ossp";
ALTER TABLE "public"."user" ALTER COLUMN "id" SET DATA TYPE UUID USING (uuid_generate_v4());
その上で、これを以下のように--before-apply
で指定してあげます。
# --before-applyにはSQL文をそのまま書く必要がある
psqldef -h localhost -U postgres --password=example --before-apply="$(cat diff.sql)" psqldef < schema.sql
また、差異SQLの管理はこちら側で行わなければいけませんが、これは必要であれば行うでよいでしょう。
Prismaと違ってPostgresのテーブルポリシーが書けるのは結構嬉しいです。
さくっとマイグレーションをしたいならこれ、ちゃんとやりたいならPrismaみたいな使い分けがいいかも?