PlanetScaleからSupabaseに移行する（Prisma）

PlanetScaleは、2024年4月8日に無料枠を廃止します。

https://planetscale.com/blog/planetscale-forever

そのため、私はとあるWebサービスのDBをPlanetScaleからSupabaseに移行することにしました。

Supabaseは、無料で利用することもできるPostgreSQLベースのDBです。

https://supabase.com

Supabaseは既に利用したことがあり、今回選定しました。大変使いやすく、またphpMyAdminのようにWeb上でデータを直接編集できるところも便利でおすすめです。

## 移行手順

:::note
今回は少量のデータだったため試していませんが、大量のデータがある場合は[pgloader](https://pgloader.io)を使うほうが良いかもしれません。
:::

前提として、今回移行するサービスは、以下の記事に書かれたような構成で作られています。

https://qiita.com/namosuke/items/6ab960cc8e3496f71db1

DBとしてPlanetScaleを利用し、ORMとしてPrismaを利用しています。

### Supabaseのテーブル作成

SupabaseとPrismaを接続します。Prismaでスキーマが定義されているため、接続するだけで自動的にDBの準備は完了します。

SupabaseでPrismaを使う方法は、下記の公式ドキュメントに詳しく書かれています。

https://supabase.com/partners/integrations/prisma

https://www.prisma.io/docs/orm/overview/databases/supabase

重要な部分は、`.env`でDATABASE_URLの他にDIRECT_URLが必要になる点、そしてDATABASE_URLの末尾には`?pgbouncer=true`が必要な点です。

Supabaseは、PostgreSQLの接続にあたりコネクションプーリング機能を利用しています。これは、接続や切断を繰り返した際にメモリが枯渇したり性能が劣化したりするのを防ぐ機能です。

![](https://supabase.com/docs/_next/image?url=%2Fdocs%2Fimg%2Fguides%2Fdatabase%2Fconnecting-to-postgres%2Fhow-connection-pooling-works--light.png&w=2048&q=75)

https://supabase.com/docs/guides/database/connecting-to-postgres#connection-pooler

https://e-words.jp/w/%E3%82%B3%E3%83%8D%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%97%E3%83%BC%E3%83%AA%E3%83%B3%E3%82%B0.html

Supabaseではこの機能のために、Session modeとTransaction modeという２種類の接続先が用意されており、操作の性質によって接続先を変えることでパフォーマンスが最大化できるようになっています。

モード毎の接続先は、Supabaseの「Project Settings」ページから「Database Settings」を開き、ページ最上部の「Connection string」という項目でそれぞれ確認できます。

`.env`のDATABASE_URLにはTransaction modeの接続先を、DIRECT_URLにはSession modeの接続先を指定してください。指定の際に、DATABASE_URLの末尾に`?pgbouncer=true`を追加するのを忘れないようにしてください。

URLは、パスワードの部分のみ`[YOUR-PASSWORD]`となっていますので作成時に指定したDBのパスワードで置き換えてください。

続いて、`schema.prisma`を以下のように置き換えてください。

```diff_shell:schema.prisma
datasource db {
-  provider     = "mysql"
-  url          = env("DATABASE_URL")
-  relationMode = "prisma"
+  provider  = "postgresql"
+  url       = env("DATABASE_URL")
+  directUrl = env("DIRECT_URL")
}
```

ここまでできたら、ターミナルで`prisma db push`すればDBテーブルが作成できるはずです。

### PlanetScaleのデータをエクスポート

GUIからエクスポートすることは現状できないので、PlanetScale公式のCLIであるpscaleを利用します。

https://github.com/planetscale/cli

ターミナルで`pscale auth login`してから`pscale database dump [database] [branch]`します。branch名はだいたい`main`だと思います。

https://planetscale.com/docs/reference/database

すると、カレントディレクトリに`pscale_dump_*`で始まるディレクトリが作成され、その中にsql形式のファイルが出力されます。

`*-schema.sql`みたいなファイルにスキーマが定義されており、`*.00001.sql`みたいなファイルにデータが定義されています。前者は使わないです。

### Supabaseにデータをインポート

いかにもそのままインポートに使えそうなSQLが生成されましたが、MySQLとPostgreSQLでクォーテーションの扱いが異なるため、そのままでは使えません。

「"」を「'」に、「\`」を「"」に、「`\'`」を「''」に変換する必要があります。

簡単なやり方として、VSCodeの検索機能でファイルを絞り込んで置換する方法があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/218499/bea1ff90-6dc1-2b60-6c40-841cf6bd196b.png)

「含めるファイル」に`*00001.sql`を指定し、すべて置換を実行すると置き換えることができます。

:::note warn
この方法では、元の文字列にダブルクォーテーションが含まれている場合などに、データが破損する可能性があります。データの性質に合わせて、適宜やり方を変更してください。
:::

置き換えることができたら、SupabaseのSQL EditorにSQLを貼り付けて実行していきましょう。

なお、よく出るエラーとして、外部キー制約によるエラー、真偽値が数値で書かれていることによるエラーがあります。

#### 外部キー制約によるエラー

`ERROR:  23503: insert or update on table "*" violates foreign key constraint "*"`

一時的に無効化できるようです。

https://blog1.mammb.com/entry/2023/08/01/090000

#### 真偽値が数値で書かれていることによるエラー

`ERROR:  42804: column "*" is of type boolean but expression is of type integer`

こちらは例えば「0」を「'0'」に置き換えることで、PostgreSQLに真偽値だと解釈させることができます。

まあ大体「Debug with Supabase AI」でなんとかなります。