PostgreSQL 15がやってくる(3) psqlの改善

# はじめに
にゃーん。
今回は、みんな大好き``psql``のPostgreSQL 15改善項目を調べてみた。
注：まだbeat1時点での調査内容なので、今後変更される可能性はあり。

# 改善項目概要
PostgreSQL 15でも色々と``psql``への改善項目が入っている。

* psqlの \copyコマンドの性能向上
* psqlコマンド \getenv の追加
* psqlの\dl/, lo_listの'+'オプションによるオブジェクト権限表示の追加
* \dconfig コマンドの追加
* \weatch コマンドがページャに対応
* クエリ内のダブルハイフンのコメントをサーバに送信するようになった
* ダブルハイフンのコメントマーカーを挿入するようにpsqlのreadline機能を改善
* 複数問い合わせ結果をpsqlが全て表示するようになった
* タブ補完を改良
* psqlのサポートをPostgreSQL 9.2以降が動作するサーバに限定
* psql の \password コマンドを改善する（リリースノート未記載）
* psqlの絵文字の幅の計算の問題を改善（リリースノート未記載）

# 改善項目の詳細
以下、改善項目の詳細を見ていく。
PostgreSQL 14と比較したほうがわかりやすいものは、両方のバージョンでの挙動を記載している。

## psqlの \copyコマンドの性能向上
psqlには`copy`というメタコマンドが用意されている。
これは名前のとおり、一括ロードを行う`COPY`コマンドをクライアント(psql)から認識できる場所に置いたファイル/プログラムから読み取り可能にするものである。

どのくらい\copyコマンドによる一括ロードやダンプが高速になったのか、手元の環境で比較してみた。

### 測定用のテーブル
以下のようにUNLOGGED TABLE（WAL書き込み影響を排除したかったので）を定義する。

```sql
CREATE UNLOGGED TABLE test (id int, num_data numeric, txt_data text);
```

このテーブルに以下のようにテキトーな値を1000000件挿入する。

```sql
INSERT INTO test VALUES (generate_series(1, 1000000), random(), md5(CLOCK_TIMESTAMP()::text));
```

ロードしたテーブルを`COPY TO`コマンドでテキトーな場所にダンプしておく。

### 測定
で、このダンプしたファイルを、
* psqlの`\copy`
* SQLコマンドの`COPY`
を使って、TRUNCATEして空にしたテーブルにロードしてみる。ロード後にはまたTRUNCATEして空にしておく。
ロード処理の時間はpsqlの`\timing`をONにしておいて取得する。
これを3回測定して平均値をとってみる。

### 結果
![Improve performance of psql's yen-copy command.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32626/c3113c61-a116-ba4b-3096-b3664dbee05c.png)

たしかに`\psql`の処理時間をみると有意にPostgreSQL 15beta1のほうが早くなっている。
なお、SQLコマンド`COPY`についても、PostgreSQL 15beta1では少し早くなっているが、その差分は小さい。
この結果からも、`\copy`自体の性能が向上していると判断できる。

### 参考情報
この改善は、Commitfest 2021-07でコミットされた。
[In psql \copy from, send data to server in larger chunks](https://commitfest.postgresql.org/33/2973/)

## psqlコマンド \getenv の追加
``psql``を実行する環境の環境変数をpsql変数に設定するpsqlコマンド``\getenv``が追加された。
PostgreSQL 15のpsqlを起動してヘルプ`\?`を表示すると以下のようなコマンドがヘルプに表示されるようになる。

```sql
test-# \?
General
  \copyright             show PostgreSQL usage and distribution terms
（中略）
Operating System
  \cd [DIR]              change the current working directory
  \getenv PSQLVAR ENVVAR fetch environment variable
  \setenv NAME [VALUE]   set or unset environment variable
  \timing [on|off]       toggle timing of commands (currently off)
  \! [COMMAND]           execute command in shell or start interactive shell
```

``\getenv``コマンドは2つの引数をもつ。

* 最初の引数``PSQLVAR``は任意のpsql変数を指定する。
* 2番目の引数``ENVVAR``にはpsqlを起動した環境に設定されている環境変数（実装は見ていないけど``getenv``システムコールで取得できるもの？）を指定する。

例えばbash上に以下のような環境変数が設定されているとする。
```bash
$ export VOICE="にゃーん"
$ echo $VOICE
にゃーん
$
```
この環境上でpsqlを起動して、以下のように``\getenv``コマンドでpsql変数``voice``に環境変数の値を設定してみる。
設定したpsql変数は、クエリ内でも使うことができる。

```sql
test=# \getenv voice VOICE
test=# SELECT :'voice';
 ?column?
----------
 にゃーん
(1 row)

test=#
```
もうちょい実用的な使い方としては、環境変数にテーブル名をセットしてから、``\getenv``を使ったスクリプトを実行するとか、条件分岐(`\if`等)で評価する変数を環境変数から取得するようにして、環境変数設定値によって動作を変えるとか、そんな用途に使えるのかもしれない。

### 参考情報
この改善は、Commitfestリストからは見つからなかった。
MLを探すと、pgsql-commitersのこのメールが該当するものっぽい。
[pgsql: Add a \getenv command to psql.](https://www.postgresql.org/message-id/E1mzO8j-0000Yy-WE@gemulon.postgresql.org)

## psqlの\dl, \lo_listの'+'オプションによるオブジェクト権限表示の追加
``\dl``,``lo_list``はラージオブジェクトの一覧を表示するpsqlコマンドである。
PostgreSQL 15では、``+``オプションを追加することで、各ラージオブジェクトへのアクセス権限(SELECT/UPDATE)をもつユーザの情報を表示可能になった。

PostgreSQL 15

```sql
postgres=# \dl
         Large objects
  ID   |  Owner   | Description
-------+----------+-------------
 10001 | postgres |
(1 row)

postgres=# \dl+
                     Large objects
  ID   |  Owner   |  Access privileges   | Description
-------+----------+----------------------+-------------
 10001 | postgres | postgres=rw/postgres+|
       |          | user_a=w/postgres   +|
       |          | user_b=r/postgres    |
(1 row)

postgres=# \lo_list
         Large objects
  ID   |  Owner   | Description
-------+----------+-------------
 10001 | postgres |
(1 row)

postgres=# \lo_list+
                     Large objects
  ID   |  Owner   |  Access privileges   | Description
-------+----------+----------------------+-------------
 10001 | postgres | postgres=rw/postgres+|
       |          | user_a=w/postgres   +|
       |          | user_b=r/postgres    |
(1 row)

postgres=#
```

なお、PostgreSQL 14のpsqlでも`\dl+`コマンドは受け付けるが、表示される情報は`\dl`と同じものになる。

### 参考情報
この改善は、Commitfest 2021-03でコミットされた。
[psql: \dl+ to list large objects privileges](https://commitfest.postgresql.org/36/3313/)

## \dconfig コマンドの追加
個人的に今回の改善項目で一番気に入っているもの。
``\dconfig``コマンドはサーバ変数を表示するpsqlのコマンド。
サーバ変数名と設定値を表示する機能として、古くからPostgreSQLコマンド``SHOW``があるが、``SHOW``と``\dconfig``の大きな違いは、``\dconfig``はパターンマッチをしてくれることだ。

たとえば、``max_parallel_workers_per_gather``というパラメータ（長い＆覚えにくい）を表示したい場合、以下のようなパターンマッチ文字で引くことができる。

```sql
test=# \dconfig max*para*gather
    List of configuration parameters
            Parameter            | Value
---------------------------------+-------
 max_parallel_workers_per_gather | 2
(1 row)

test=#
```
なお、`SHOW`コマンドでは、上記のようなパターンマッチは使えない。にゃーん。
```sql
test=# SHOW max*para*gather;
ERROR:  syntax error at or near "*"
LINE 1: SHOW max*para*gather;
                ^
```

パラメータ名をきちんと覚えられない自分にとっては実にありがたい。``SHOW``で個別のパラメータ値を表示するときに、長いパラメータ名を入れるの面倒だし。
ただ、これによってますますパラメータ名がうろ覚えになってしまいそう・・・ｗ

ただ個人的にちょい謎だと思ったのは、なんでこのpsqlコマンド名は``\show``ではなく``\dconfig``という名前になったんだろう、ということ。

### 参考情報
この改善項目は、[Commitfest](https://commitfest.postgresql.org/)のリストには載っていない。
hackers-mlを探すと2021-04-07のメールが初出のようだ。
[pgsql: psql: add \dconfig command to show server's configuration parame](https://www.postgresql.org/message-id/E1ncZPB-000n2a-R2@gemulon.postgresql.org)
このため、Commitfest 2022-03のリストにもないのだろう。
もしかすると、このCommitfest 2022-03のこのコミットが関係しているのかもしれない。
[fix psql pattern handling](https://commitfest.postgresql.org/37/3367/)


## \weatch コマンドがページャに対応
psqlには`\watch`コマンドという機能がある。これは直前にpsqlから実行されたコマンドを、引数に指定した秒数ごとに繰り返し実行する、というもので、SQLを使ったアドホックな監視をするときに良く使っている。
このコマンドは従来はページャに対応していなかったが、PostgreSQL 15からはPAGERに指定したコマンドによるページング制御が可能になった。

`\watch`コマンドの結果をページャ用のコマンドで制御するには、環境変数``PSQL_WATCH_PAGER``にページャ用のコマンド（例えばless）を設定し、その状態psqlを実行すると使用できるようになる。

例えば、bash上で``export PSQL_WATCH_PAGER="less"``を設定してpsqlを起動し、20行の結果を生成するSELECT文を実行する。

```sql
$ export PSQL_WATCH_PAGER="less"
$ ~/pgsql/pgsql-15b1/bin/psql -p 10015 postgres
psql (15beta1)
Type "help" for help.

postgres=# SELECT generate_series(1,20), clock_timestamp();
 generate_series |        clock_timestamp
-----------------+-------------------------------
               1 | 2022-05-28 15:32:19.803739+09
               2 | 2022-05-28 15:32:19.803751+09
               3 | 2022-05-28 15:32:19.803752+09
（中略）
              13 | 2022-05-28 15:32:19.80376+09
              14 | 2022-05-28 15:32:19.803761+09
--More--
```
この状態で、`\watch`コマンドを実行すると、`\watch`コマンドの出力結果は``less``コマンドによってページ制御される(lessのプロンプト``:``になっていることに注目）。

```sql
postgres=# \watch 1


   Sat 28 May 2022 03:34:35 PM JST (every 1s)

 generate_series |        clock_timestamp
-----------------+-------------------------------
               1 | 2022-05-28 15:34:35.048501+09
               2 | 2022-05-28 15:34:35.048508+09
               3 | 2022-05-28 15:34:35.048509+09
（中略）
              10 | 2022-05-28 15:34:35.048515+09
              11 | 2022-05-28 15:34:35.048516+09
              12 | 2022-05-28 15:34:35.048517+09
:
```

### 参考情報
この改善は、Commitfest 2021-07でコミットされた。
[allow to set a pager for psql's watch command](https://commitfest.postgresql.org/33/2539/)

## クエリ内のダブルハイフンのコメントをサーバに送信するようになった
PostgreSQLのクエリはコメントとしてダブルハイフン(``--``)も使うことができる。
ダブルハイフン以降、行末(改行コード)までは全てコメントとみなされる。

```sql
SELECT 1 -- 1を返す``
```
PostgreSQL 14まではこのダブルハイフンによるコメントはサーバ送信前にpsql内で除去されていた。
PostgreSQL 15からはダブルハイフンによるコメント文字列もサーバへ送信されるようになった。

たとえば、``log_statement='all'``が設定されているときに、PostgreSQL 14までは
```sql
test=# SELECT 'nuko', -- にゃーん
2;
 ?column? | ?column?
----------+----------
 nuko     |        2
(1 row)

test=#
```
というクエリを実行しても、ダブルハイフンコメント中の``にゃーん``はサーバログには記録されなかった。

PostgreSQL 14でのサーバログ
```sql
2022-05-28 10:17:42.067 JST [20709] LOG:  statement: SELECT 'nuko',
        2;
```

しかし、PostgreSQL 15からは、ダブルハイフンコメント中の``にゃーん``もサーバログに記録されるようになる。
(pgaudit等の監査拡張機能を使う場合も同様である）

PostgreSQL 15でのサーバログ
```sql
2022-05-28 10:10:49.932 JST [20606] LOG:  statement: SELECT 'nuko', -- にゃーん
        2;
```
なので、PostgreSQL 15以降のpsqlを使う場合には、ダブルハイフンコメントの中に変なコメントを書かないように気をつけねばならない。にゃーん。

### 参考情報
この改善項目を直接指しているCommitfestの項目は見つけられなかった。
この項目が関係指定そうだが、深追いはしていない。
[Correct handling of blank/commented lines in PSQL interactive-mode history](https://commitfest.postgresql.org/36/3318/)


## ダブルハイフンのコメントマーカーを挿入するようにpsqlのreadline機能を改善
**現状、改善内容を把握できず。**
リリースノートを読むと
> Adjust psql's readline meta-# to insert a double-hyphen comment marker (Tom Lane)
> Previously an unhelpful pound marker was inserted.

と書かれているのだが、これはどういう状態での問題を解決したのかリリースノート記述からだけだとよくわからない。MLも追いきれなかった。
PostgreSQL 14.3とPostgreSQL 15 beta1でダブルハイフンつき、かつ複数行にまたがるクエリを実行して、その後にreadlineによるコマンド履歴を表示させたけど、違いがよくわからず・・・。

### 参考情報
この改善は、Commitfest 2022-01でコミットされたと思われる。
[Correct handling of blank/commented lines in PSQL interactive-mode history](https://commitfest.postgresql.org/36/3318/)

## 複数問い合わせ結果をpsqlが全て表示するようになった
PostgreSQL 14までは、複数の問い合わせをpsqlから実行した場合、最後の問い合わせの結果のみを表示していた。
ここでいう「複数の問い合わせ」というのがなかなか理解できなかったが、どうやら、以下のように、`\;`で区切られたクエリのことを指すらしい。
``SELECT 1 \; SELECT 2 \; SELECT 3;``

上記のクエリをPostgreSQL 14で実行した場合、最後の``SELECT 3``の結果のみが表示される。

```sql
test=# SELECT 1 \; SELECT 2 \; SELECT 3;
 ?column?
----------
        3
(1 row)

test=#
```

PostgreSQL 15では以下のように全ての問い合わせの結果が表示されるようになった。

```sql
postgres=# SELECT 1 \; SELECT 2 \; SELECT 3;
 ?column?
----------
        1
(1 row)

 ?column?
----------
        2
(1 row)

 ?column?
----------
        3
(1 row)

postgres=#
```
なお、この機能はpsql変数``SHOW_ALL_RESULTS``(これもPostgreSQL 15から導入されたpsql変数)を無効化することでPostgreSQL 14までの挙動（最後の問い合わせのみを表示）にすることもできる。

```sql
postgres=# \set SHOW_ALL_RESULTS off
postgres=# SELECT 1 \; SELECT 2 \; SELECT 3;
 ?column?
----------
        3
(1 row)

postgres=#
```

### 参考情報
この改善は、Commitfest 2022-03でコミットされた。
[psql - add SHOW_ALL_RESULTS option](https://commitfest.postgresql.org/37/2096/)

## タブ補完を改良
psqlの使いやすい点の一つとして、SQL構文のタブ補完、データベースオブジェクトのタブ補完が豊富なことが挙げられる。これはreadlineの機能を使っている。
（逆にいうと、readlineに対応していない、psqlというのは正直言ってとても使いづらい。Windows版PostgreSQLについているpsqlのことだよっ！）

リリースノートでは「タブ補完を改良」としか書かれておらず、具体的にどんな改良がされたのかは、リリースノートから読み取ることはできない・・・。
しかたないので、Commitfestでコミットされたpsqlのタブ補完に関する項目を洗い出していく。

今回それっぽいTAB補完改善の項目は以下。
* CREATE PULICATION 
* ALTER TABLE .. VALIDATE CONSTRAINT
* 大文字(upper charcter)入力時のTAB補完に対応

### CREATE PULICATION
対応するCommitfest項目（Commitfest 2021-07）は以下。
[psql tab auto-complete for CREATE PUBLICATION](https://commitfest.postgresql.org/34/3249/)

このメールにあった幾つかのケースを試してみたが、PostgreSQL 14.3とPostgreSQL 15 beta1でTAB補完の挙動に差がなかった。
PostgreSQL 14.3だと、既にバックパッチされている？

### ALTER TABLE .. VALIDATE CONSTRAINT
対応するCommitfest項目（Commitfest 2022-01）は以下。
[psql: exclude valid constraints in tab-complete for ALTER TABLE .. VALIDATE CONSTRAINT](https://commitfest.postgresql.org/36/3113/)

これもPostgreSQL 14.3とPostgreSQL 15 beta1でTAB補完の挙動に差がなかった。
PostgreSQL 14.3だと、既にバックパッチされている？

### 大文字(upper charcter)入力時のTAB補完に対応
対応するCommitfest項目（Commitfest 2022-01）は以下。
[Support tab completion for upper character inputs in psql](https://commitfest.postgresql.org/36/2979/)

議論メールは追いかけきれていないけど、``SET``/``RESET``/``SHOW``の3コマンドのコマンド名をが小文字であっても、大文字であってもTAB補完してくれる、という解釈でいいのかな。

ただ、これもPostgreSQL 14.3とPostgreSQL 15 beta1でTAB補完の挙動に差がなかった（展開されるパラメータそのものは15で増えているので、その違いはあるけれど）。
PostgreSQL 14.3だと、既にバックパッチされている？

### 参考情報
この改善は、以下のCommitfest契機でコミットされた。
* [psql tab auto-complete for CREATE PUBLICATION](https://commitfest.postgresql.org/34/3249/)
* [psql: exclude valid constraints in tab-complete for ALTER TABLE .. VALIDATE CONSTRAINT](https://commitfest.postgresql.org/36/3113/)
* [Support tab completion for upper character inputs in psql](https://commitfest.postgresql.org/36/2979/)

## psqlのサポートをPostgreSQL 9.2以降が動作するサーバに限定
この修正については、Commitfestのリストからは見つけられなかった。
MLを探すと、pgsql-commiters MLのこの内容が該当するものっぽい。
[pgsql: Remove psql support for server versions preceding 9.2.](https://www.postgresql.org/message-id/E1mxw1V-00069g-Tm@gemulon.postgresql.org)

PostgreSQL 9.1自体はとうの昔にEOLになっているので、これで困るケースはそれほどなさそうには思うが、PostgreSQL 15からは古すぎるバージョンのサーバに接続しようとすると警告される、ということか。

実際にPostgreSQL 9.1.24のサーバを立てて、PostgreSQL 15 beta1で接続すると、``Some psql features might not work.``といった警告が表示されるようになった。
ただ警告は表示されるのみで接続自体が拒否されるというわけではない。

```sh
$ /home/ec2-user/pgsql/pgsql-9.1/bin/pg_ctl -D /tmp/pg091 -l logfile start
server starting
[ec2-user@ip-10-0-1-10 postgresql-9.1.24]$ ~/pgsql/pgsql-15b1/bin/psql -p 10091 postgres
psql (15beta1, server 9.1.24)
WARNING: psql major version 15, server major version 9.1.
         Some psql features might not work.
Type "help" for help.

postgres=# SELECT version();
                                                    version
----------------------------------------------------------------------------------------------------------------
 PostgreSQL 9.1.24 on x86_64-unknown-linux-gnu, compiled by gcc (GCC) 7.3.1 20180712 (Red Hat 7.3.1-13), 64-bit
(1 row)

postgres=#
```

## psql の \password コマンドを改善する
この改善は、Commitfest 2021-11でコミットされた。
[Improving psql's \password command](https://commitfest.postgresql.org/35/3387/)
理由は良くわからないが、PostgreSQL 15 beta1のリリースノートには掲載されていない項目だ。

自分の理解（手元で試した感じ）だと、
* PostgreSQL 14までは、``\password``でパスワード変更を試みている最中には、`CTRL+C`での中断はできない。
* PostgreSQL 15では、``\password``でパスワード変更を試みている最中に、`CTRL+C`での中断が可能になった

と解釈しているけど、それであっているのかな・・・（MLの議論は追いきれていない・・・）

## psqlの絵文字の幅の計算の問題を改善
**これも改善内容をまだ確認できず・・・**
リリースノートには記載されていなかったが、Commitfestでコミットされていた項目には入っていた。

とりあえず、絵文字を含むクエリをpsqlで実行（psqlｎ実行環境は[Putty日本語版+独自パッチ版？](https://ice.hotmint.com/putty/index.html)を使った）してみたが、PostgreSQL 14.3とPostgreSQL 15 beta1での違いは分からなかった。（どっちも微妙にずれている）

PostgreSQL 14.3
```sql
$ ~/pgsql/pgsql-15b1/bin/psql -U postgres -p 10014 test -e -f emoji.sql
CREATE TEMP TABLE hoge (n text, i text, v text);
CREATE TABLE
INSERT INTO hoge VALUES
('elephant', '🐘', 'paon'),
('cat', '😺', 'nya-n'),
('dolphin', '🐬', 'kyu-')
;
INSERT 0 3
SELECT * FROM hoge;
    n     | i  |   v
----------+----+-------
 elephant | 🐘 | paon
 cat      | 😺 | nya-n
 dolphin  | 🐬 | kyu-
(3 rows)
```

PostgreSQL 15 beta1
```sql
$ ~/pgsql/pgsql-15b1/bin/psql -U postgres -p 10015 test -e -f emoji.sql
CREATE TEMP TABLE hoge (n text, i text, v text);
CREATE TABLE
INSERT INTO hoge VALUES
('elephant', '🐘', 'paon'),
('cat', '😺', 'nya-n'),
('dolphin', '🐬', 'kyu-')
;
INSERT 0 3
SELECT * FROM hoge;
    n     | i  |   v
----------+----+-------
 elephant | 🐘 | paon
 cat      | 😺 | nya-n
 dolphin  | 🐬 | kyu-
(3 rows)
```

### 参考情報
この改善は、Commitfest 2021-09でコミットされた。
[badly calculated width of emoji in psql](https://commitfest.postgresql.org/34/3087/)

# おわりに
リリースノートに記載されていない項目があったり、Commitfestに記載されていない改善項目があったり、既にPostgreSQL 14.3では対応ずみ？な項目もあったりしたので、今回の調査は意外に難航した・・・。

``psql``は、PostgreSQLを使う時にいつもお世話になっているユーティリティなんだけど、未だに進化を続けているというのは凄いことだなあ、と毎回バージョンアップ時に思っている。
これが便利すぎるから、pgAdmin4等のGUI系クライアントインタフェースを使わなくなってしまうんだよね・・・。