💾

PlanetScale から Supabase にデータを移行した話(Prisma使用)

2024/03/11に公開

概要

PlanetScale無料プランを廃止したので、 Supabase に移行しようという話です。

TL;DR

Supabase でデータベースを作って Prisma でテーブルを作成し、PGLoader を使ってデータだけを移行する話です。

準備

PGLoader のインストール

まずは移行元の PlanetScale のデータベースURLと移行先の Supabase のデータベースURLを準備します。

次に、移行をしてくれる PGLoader というソフトウェアをインストールします。 Debian 系であれば

sudo apt install pgloader

でインストールすることができます。他の OS の場合は 公式ドキュメントに書いてあるインストール方法を参考にしてください。

Prisma の設定

次に schema.prisma の設定を変更します。 PlanetScale は MySQL だったのですが、 Supabase は PostgreSQL なので、以下のように providor を変更します。

schema.prisma
datasource db {
    ...
-    providor = "mysql"
+    providor = "postgresql"
    ...
}

そして、データベースURLを変更するので url の変更もします。(.env にデータベースURLを保存している場合はそちらを変更します。)

schema.prisma
datasource db {
    ...
-    url = "mysql://..."
+    url = "postgresql://..."
    ...
}

そして、心の準備をしてください。(たまによくわからないエラーが出ます。そういうときは成功するまでコマンドを繰り返すと成功する場合があります。)

移行

では実際に移行をします。

テーブル作成

まず、 Supabase 側にテーブルを作成します。
プロジェクトのディレクトリで以下のコマンドを実行してください。

npx prisma migrate dev

データのインポート

次にデータの移行をします。
どこかに以下の内容で migration.load というファイル(名前はなんでもいい)を作成してください。

migration.load
LOAD DATABASE
    FROM [pscaleDB]?sslmode=require
    INTO [supabaseDB]
ALTER SCHEMA '[schema]' RENAME TO 'public' WITH QUOTE IDENTIFIERS, DATA ONLY, TRUNCATE, CREATE NO TABLES;
  • [pscaleDB] には PlanetScale のデータベース URL を入れます。ただし、最後についている ?sslaccept=strict は消して、?sslmode=require に置き換えてください。
    こんな感じになるはずです。mysql://username:password@aws.connect.psdb.cloud/dbname?sslmode=require
  • [supabaseDB] には Supabase のデータベース URL を入れます。
    こんな感じになるはずです。postgres://postgres.xxxx:password@xxxx.pooler.supabase.com:6543/postgres
  • [schema] には PlanetScale のデータベースの名前(プロジェクトの名前)を入れてください。
  • 全体でセミコロンは一つだけです。

以上のようにファイルを作成することができたら、以下のコマンドを実行してください。

pgloader migration.load

そうすることで、自動的にデータの移行が始まります。

コマンドの実行が終了したらデータベース移行は完了です。引き続き Prisma による開発ライフを送ってください。

詳しい説明

migration.load の中身の詳しい説明をします。うまく行かなかったときの参考などに使ってください。

LOAD DATABASE FROM [db1] TO [db2] という構文を使うことで db1 から db2 への移行をすることができます。
デフォルトではテーブルを新しく作成し、データを入力します。ですが、これだけでは以下の問題が発生します。

  • PlanetScale 側の schema と Supabase 側の schema が一致しない。
  • テーブルの構造が schema.prisma と一致しない。
  • テーブルの名前が schema.prisma と一致しない。(パスカルケースからスネークケースに変えられる)

なので、追加で以下のようなものを追加します。

ALTER SCHEMA '[schema]' RENAME TO 'public' WITH QUOTE IDENTIFIERS, DATA ONLY, TRUNCATE, CREATE NO TABLES

その説明はこのようになります。

  • ALTER SCHEMA '[schema]' RENAME TO 'public'
    PlanetScale 側では schema はデータベースの名前で、 Supabase 側では schema は public が使われるので、それの変更を行います。
  • WITH [option], [option]...
    WITH 構文ではいろいろなオプションを追加できます。カンマで区切ります。
    • QUOTE IDENTIFIERS: テーブル名などをクオートします。つまり、テーブル名をそのままで維持します。
    • DATA ONLY: 移行のときにデータのみを移行します。
    • TRUNCATE: 一度テーブルを TRUNCATE します。
    • CREATE NO TABLES: 新しくテーブルを作成せずに、すでにあるテーブルを変更しないようにします。

WITH に関する細かい使用はこちらを参考にしてください。https://pgloader.readthedocs.io/en/latest/ref/mysql.html?highlight=options with#mysql-database-migration-options-with
※もしかしたらいらないものも有りますが、あっても困らないはずです。

詳しい説明は以上です。ここに書いてある説明はとてもざっくりした説明なので、もしもより詳しく理解したい場合は PGLoader のドキュメントを読んでください。

備考

PostgreSQL と MySQL は相性が悪いので、移行したあとも一度データの不整合などがないかチェックをすることをおすすめします。

まとめ

今回は PlanetScale の無料プランがなくなるということで Supabase に移行しようという記事を書きましたが、ここにたどり着くまでが非常に長かったです(約12時間)。Supabase 公式で紹介されている方法でも何故かエラーは出るし、公式以外の情報も少なく、 PGLoader の公式ドキュメントも活用していろいろな質問の回答なども参考にしてようやくこの方法にたどり着きました。なので、皆さんには同じような失敗をしないでほしいという思いでこの記事を書きました。

後日に、どのように PlanetScale のブランチ機能を用いた開発を Supabase のローカル環境を用いる開発に移行するかに関しても一つ記事を書くつもりです。

ここまで読んでくださり、本当にありがとうございました。

最後に、もし間違っている情報などが有りましたら気軽に言ってください。

参考にした記事など

Supabase 公式

https://supabase.com/docs/guides/database/import-data#option-2-bulk-import-using-pgloader
ここでは

ALTER SCHEMA 'public' OWNER TO 'postgres';
set wal_buffers = '64MB', max_wal_senders = 0, statement_timeout = 0, work_mem to '2GB';

という記述が有りますが、私の場合は OWNER のところで文法エラーが出たので参考にしないことをおすすめします。

pgloader 3.4.1でMySQLからPostgreSQLへスマートに移行しよう(翻訳)

https://techracho.bpsinc.jp/hachi8833/2017_07_20/43380
あまり参考にしなかった記事ですが、もしかしたら最も有益な情報が乗っていたかもしれません。

StackExchange での質問

https://dba.stackexchange.com/questions/191090/how-to-use-pgloader-to-transfer-sqlite-to-postgres-with-table-name-in-uppercase#:~:text=None of your table names,column names to be lowercased.&text=PostgreSQL only takes account of,which is a typing nightmare!
私が WITH という文法を知ったきっかけの StackExchange での質問です。

Discussion