PostgreSQLで異なるスキーマにデータをリストアするには?
異なるデータベースにリストアするのは簡単ですが、異なるスキーマにリストアするのに結構はまったので、以下覚書。psqlなどのコマンドをローカルにインストールするのが嫌だったのでdocker
を使ってます。
シナリオは、
データベース名:my_db
スキーマ名:public
をダンプし、
データベース名:my_db2
スキーマ名:my_schema2
にリストアするというものです。pg_restore
コマンドでは異なるスキーマにリストアすることはできなかったです。うまいやり方ご存じのかた教えてほしいです(スキーマ名をリネームするのは無しで)。
方針
pg_restore
コマンドはあきらめて、pg_dump
でプレーンテキストを出力し、適宜プレーンテキストを文字列置換して、psql
コマンドでスキーマ定義、データを反映します。
-
pg_dump
コマンドで、スキーマ定義をプレーンテキストでダンプ。 -
pg_dump
コマンドで、データをプレーンテキストでダンプする。 - ダンプしたスキーマ定義をスキーマ名とユーザ名をエディタでリストア先のスキーマ名、ユーザ名に置き換える。
-
psql
コマンドで、修正したスキーマ定義を投入。 -
psql
コマンドで、データを投入の際に、スキーマ名をリストア先のスキーマ名に変更してからpsql
コマンドを実行。
かなりメンドクサイ。
事前準備
次のdocker-compose.yml
でデータベースを起動している前提です。
version: '3.7'
services:
my-posgre-db:
image: postgres:latest
volumes:
- ./data:/var/lib/postgresql/data
environment:
- TZ=Asia/Tokyo
- POSTGRES_PASSWORD=postgres
networks:
- my-posgre-network
ports:
- 5432:5432
networks:
my-posgre-network:
driver: bridge
リストア先の異なるデータベース、ユーザ、スキーマを作ります。
※だいぶ雑な表現で恐縮ですが。。。
CREATE USER "my_user2" WITH PASSWORD 'my_user2';
CREATE DATABASE "my_db2";
\c "my_db2"
CREATE SCHEMA "my_schema2";
ALTER SCHEMA "my_schema2" OWNER TO "my_user2";
GRANT ALL PRIVILEGES ON SCHEMA "my_schema2" TO "my_user2";
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA "my_schema2" TO "my_user2";
pg_dump
コマンドで、スキーマ定義をプレーンテキストでダンプ
docker run -it --rm -v ./work:/tmp --network postgresql_my-posgre-network postgres:latest pg_dump -h my-posgre-db -d my_db -U my_user -n public -s -F p -f /tmp/public_schema.sql
pg_dump
コマンドで、データをプレーンテキストでダンプする
docker run -it --rm -v ./work:/tmp --network postgresql_my-posgre-network postgres:latest pg_dump -h my-posgre-db -d my_db -U my_user -n public -F p -a | gzip > ./work/public_schema.dump.gz
わかりにくいですが、このコマンドを打つと、裏で「Password:」のプロンプトが出ています。gzipにリダイレクトしているので、プロンプトが標準出力されないです。
なので、パスワードを入力するとダンプが始まります。
残念なことに、「Password:」の文字列がgzipにリダイレクトされているので、ダンプファイルの先頭に書き込まれてしまいます。
この後でリストアする際には除外するようにしなければならないです。
ダンプしたスキーマ定義をスキーマ名とユーザ名をエディタでリストア先のスキーマ名、ユーザ名に置き換える
sed
やvi
などのエディタでスキーマ名とOWNERユーザ名を書き換える。sed
コマンドで置換した結果を標準入力を食わせても良いです。以下でデータを投入するところででは標準入力で食わせてます。
あと、先頭の方にCREATE SCHEMA
文があるので、削除すること。
psql
コマンドで、修正したスキーマ定義を投入
docker run -it --rm -v ./work:/tmp --network postgresql_my-posgre-network postgres:latest psql -h my-posgre-db -d my_db2 -U my_user2 -f /tmp/public_schema.sql
psql
コマンドで、データを投入の際に、スキーマ名をリストア先のスキーマ名に変更してからpsql
コマンドを実行
コマンドの解説。
コマンドの先頭で{ echo -e ~
をしているのは、docker run
に食わせる標準入力を組み立てています。
まず、echo -e "my_user2\n"
の部分はパスワードをdocker run
に食わせる標準入力の先頭行に入れています。
次に、gzip -d -c ./work/public_schema.dump.gz
でダンプファイルを解凍。
sed 's/COPY public/COPY my_schema2/g'
でスキーマ名を変更。ここが肝。
sed 's/^Password://g'
でダンプした際の「Password:」を消しています。
あと、docker run -i
の-i
の部分。いつもは-it
なのに、今回は-i
としているのは、{ echo -e ~
の標準入力を受け付けるため。t
をつけてしまうと対話モードになってしまい、リダイレクトで入力を受け付けられなくなるため。
{ echo -e "my_user2\n"; gzip -d -c ./work/public_schema.dump.gz | sed 's/COPY public/COPY my_schema2/g' | sed 's/^Password://g';} | docker run -i --rm -v ./work:/tmp --network postgresql_my-posgre-network postgres:latest psql -h my-posgre-db -d my_db2 -U my_user2