PostgreSQLで異なるスキーマにデータをリストアするには？

# 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``でデータベースを起動している前提です。

```YAML: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
```
リストア先の異なるデータベース、ユーザ、スキーマを作ります。
※だいぶ雑な表現で恐縮ですが。。。

```sql
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``コマンドで、スキーマ定義をプレーンテキストでダンプ
```bash
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``コマンドで、データをプレーンテキストでダンプする
```bash
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``コマンドで、修正したスキーマ定義を投入
```bash
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``をつけてしまうと対話モードになってしまい、リダイレクトで入力を受け付けられなくなるため。

```bash
{ 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
```