PostgreSQL 9.6 - みんな大好きpsql

ということで、今日はみんな大好き psql の9.6改造項目について、実際に動かして試してみましたよ。
psqlがあればGUIクライアント要らないよねw
なお、今回のエントリ、ちょい長いです。

複数のコマンド指定、複数のコマンドファイル指定

リリースノートの記述。

Support multiple -c and -f command-line options (Pavel Stehule, Catalin Iacob)
To allow this with sane behavior, one backwards incompatibility had to be introduced: -c no longer implies --no-psqlrc.

要するに、-c コマンド文字列 や -f コマンドファイル名 が複数記述可能になったっぽい。
試してみよう。

[nuko@localhost ~]$ psql postgres -c "SELECT pg_backend_pid()" -c "SELECT pg_backend_pid()"
 pg_backend_pid 
----------------
          12699
(1 row)

 pg_backend_pid 
----------------
          12699
(1 row)

どうやら、この挙動を見る限りでは、複数回 -c オプションで指定したときには、同じセッション内で動作するっぽいですね。

じゃあ、次は複数の-fオプション指定。
こんな2つのファイルを用意する。

[nuko@localhost ~]$ cat /tmp/file1.txt 
BEGIN;
SELECT pg_backend_pid();
[nuko@localhost ~]$ cat /tmp/file2.txt 
SELECT pg_backend_pid();
COMMIT;

トランザクション途中でファイルを分割していることに注目。

で、これを複数同時に指定してみる。

[nuko@localhost ~]$ psql postgres -f /tmp/file1.txt -f /tmp/file2.txt 
BEGIN
 pg_backend_pid 
----------------
          12416
(1 row)

 pg_backend_pid 
----------------
          12416
(1 row)

COMMIT

複数のファイルにまたがってトランザクションが継続しているのがわかりますね。
(そうでなければ、最後のCOMMITで"WARNING: there is no transaction in progress"と怒られるはず。

まあ、これはあると嬉しい機能ですよね。

クロスタブ集計ビュー

Add a \crosstabview command that prints the results of a query in a cross-tabulated display

面白いけど、謎機能その1。

たとえば、こんなクエリを発行して、以下の様な結果を取得する。

tmp=# TABLE sales ;
    date    | item  | sales 
------------+-------+-------
 2016-05-16 | Ramen |  1650
 2016-05-16 | Curry |  2500
 2016-05-16 | Udon  |   500
 2016-05-16 | Soba  |   600
 2016-05-17 | Ramen |  2400
 2016-05-17 | Curry |   800
 2016-05-18 | Ramen |  1400
 2016-05-18 | Curry |  1600
 2016-05-18 | Udon  |  1000
(9 rows)

この状態で、以下の様な9.6新規のpsqlのメタコマンドを発行する。

tmp=# \crosstabview date item sales
    date    | Ramen | Curry | Udon | Soba 
------------+-------+-------+------+------
 2016-05-16 |  1650 |  2500 |  500 |  600
 2016-05-17 |  2400 |   800 |      |     
 2016-05-18 |  1400 |  1600 | 1000 |     
(3 rows)

なんかクロスタブ集計っぽい結果が出てきたw

\crosstab メタコマンドは3つないし4つの引数をとる。
1つ目は縦側の列、2つ目は横側の列、3つ目はセルに相当する列を指定。
4つ目の引数にはソートしたい列名を指定する。

面白い機能なんだけど、
なぜpsql上でクロスタブ集計ぽいことしたかったのか、という疑問はある。いいぞ、もっとやれw

\errverbose

Add an \errverbose command that shows the last server error at full verbosity

直前のエラー情報詳細を表示してくれるメタコマンド。

tmp=# SELECT hoge;
ERROR:  column "hoge" does not exist
LINE 1: SELECT hoge;
               ^
tmp=# \errverbose 
ERROR:  42703: column "hoge" does not exist
LINE 1: SELECT hoge;
               ^
LOCATION:  errorMissingColumn, parse_relation.c:3090
tmp=# SELECT 1;
 ?column? 
----------
        1
(1 row)

tmp=# \errverbose 
ERROR:  42703: column "hoge" does not exist
LINE 1: SELECT hoge;
               ^
LOCATION:  errorMissingColumn, parse_relation.c:3090

現実社会でもいるよね。
いつまでも失敗を覚えてぐちぐち蒸し返す奴ってwww

\evコマンドと\svコマンド

Add \ev and \sv commands for editing and showing view definitions

\ev コマンドは、クエリエディタコマンド \e をちょい拡張したもので、ビュー編集テンプレートをエディタに表示してくれる。

CREATE VIEW  AS
 SELECT
  -- something...

似たような機能として、 \ef (関数のテンプレート表示)があったけど、それのView版ね。

\sv はビュー生成時のクエリを表示してくれるメタコマンド。こんな感じ。

tmp=# \sv sales_date 
CREATE OR REPLACE VIEW public.sales_date AS
 SELECT sales.date,
    sum(sales.sales) AS sum
   FROM sales
  GROUP BY sales.date

\gexec

Add a \gexec command that executes a query and re-submits the result(s) as new queries

最初はよくわかんなかったけど、最後に実行した実行結果を、SQLとして実行するというメタコマンド。
つまり、\gexec の直前に実行するクエリは
SQLを生成するクエリ
である必要がある。変態ですね。
実例を示したほうが早いかな。

まず、こんなクエリを実行する。

tmp=# SELECT 'SELECT now()' FROM (SELECT generate_series(1,2)) AS t;
   ?column?   
--------------
 SELECT now()
 SELECT now()
(2 rows)

2行の SELECT now() という文字列が結果として返却される。

この状態で、 \gexecを実行すると・・・

tmp=# \gexec
              now              
-------------------------------
 2016-05-18 20:31:06.846529+09
(1 row)

              now              
-------------------------------
 2016-05-18 20:31:06.846616+09
(1 row)

さっきの結果文字列 SELECT now() が2回実行される。
さて、この結果を見るとわかると思うけど、個々のクエリは別トランザクションとして実行されている。
が、これは \gexec の問題ではなく、psqlの自動コミットモードがデフォルトで on になっているため。
なので、自動コミットモードを off にすると、2回とも now() は同じ時刻を返却する(同一トランザクションになるので)。

tmp=# \set AUTOCOMMIT off
tmp=# ;
tmp=# SELECT 'SELECT now()' FROM (SELECT generate_series(1,2)) AS t;
   ?column?   
--------------
 SELECT now()
 SELECT now()
(2 rows)

tmp=# \gexec
              now              
-------------------------------
 2016-05-18 22:26:15.190622+09
(1 row)

              now              
-------------------------------
 2016-05-18 22:26:15.190622+09
(1 row)

おわりに

今日はネムイ(´・ωゞ)のでここまで。
psql は他にも9.6での改善項目、特にTAB補完などは次のエントリで紹介しようと思います。

それにしても、psqlって単なるSQLフロントエンドとは思えないほど、
変態的に高度な機能を実装しちゃってるよなー。
これだからpgAdminが使われなく(ry