\if/\else/\endifで条件分岐する

psqlの\ifメタコマンドは、後続の処理を条件に応じて分岐させる。\else\endifを組み合わせて使う。PostgreSQL 10以降のpsqlクライアントで使える。

testdb=# \if true
testdb-# \echo 'true branch'
testdb-# \else
testdb-# \echo 'false branch'
testdb-# \endif
true branch

\ifに指定した条件が真であれば\if\elseの間、偽であれば\else\endifの間が実行される。\elseは省略できる。

\ifは真偽値リテラルしか受け付けない

\ifは式全体を評価するわけではなく、true/false/1/0/yes/no/on/offのような決まった真偽値リテラルしか認識しない。比較演算子を含む式をそのまま渡すとエラーになる。

testdb=# \set myvar 5
testdb=# \if :myvar > 3
unrecognized value "5 > 3" for "\if expression": Boolean expected
testdb=# \echo 'big'
testdb=# \else
testdb=# \echo 'small'
small
testdb=# \endif

:myvar > 35 > 3という文字列に展開されるだけで、psql自身は四則演算や比較演算をしない。条件式を評価したい場合は、後述のようにSQLクエリの結果を\gsetで取り込む必要がある。

SQLクエリの結果を条件に使う

比較や計算を伴う条件分岐をしたい場合は、SQLで真偽値を計算して\gsetでpsql変数に取り込み、その変数を\ifに渡す。

testdb=# SELECT count(*) = 2 AS user_count_is_two FROM users
testdb-# \gset
testdb=# \if :user_count_is_two
testdb-# \echo 'exactly two users'
testdb-# \endif
exactly two users

user_count_is_two列の値(tまたはf)は、そのまま\ifが認識できる真偽値として扱われる。

参考: 【PostgreSQL】psqlのクエリバッファを各種メタコマンドで活用する

\elifで複数の条件を分岐する

3つ以上に分岐させたい場合は\elifを使う。if/elif/elseの構造はプログラミング言語の条件分岐とほぼ同じである。

testdb=# \if false
testdb-# \echo 'a'
testdb-# \elif true
testdb-# \echo 'b'
testdb-# \else
testdb-# \echo 'c'
testdb-# \endif
b

最初に真になった\ifまたは\elifの分岐だけが実行され、以降の\elifは評価されない。

\if文はネストできる

\ifの中にさらに\ifを書くこともできる。それぞれの\ifには対応する\endifが必要である。

testdb=# \if true
testdb-#   \if false
testdb-#     \echo 'inner false'
testdb-#   \else
testdb-#     \echo 'inner else'
testdb-#   \endif
testdb-# \endif
inner else

インデントは見やすさのためのものであり、psqlの動作には影響しない。

具体例: サーバーバージョンに応じてスクリプトを分岐する

サーバーのバージョンによって使えるSQL構文が異なる場合、\ifでスクリプトを分岐させると1つのファイルで複数バージョンに対応できる。

-- migrate.sql
SELECT current_setting('server_version_num')::int >= 140000 AS is_pg14_or_later
\gset

\if :is_pg14_or_later
\echo 'PG14+ detected, using new syntax'
-- PostgreSQL 14以降向けのDDLなど
\else
\echo 'older PG, using legacy syntax'
-- 古いバージョン向けの代替処理
\endif

複数バージョンのPostgreSQLが混在する環境で共通のメンテナンススクリプトを配布する場合など、バージョン差異を吸収する目的で使える。

\if内で構文エラーが起きた場合の扱い

条件が偽で実行されないブランチの中身は、psqlによって構文解析はされるが実行はされない。あからさまにおかしい行を書いても、その分岐が選ばれなければエラーにはならない。

testdb=# \if false
testdb-# SELECT * FROM no_such_table_at_all;
testdb-# \endif
testdb=#

存在しないテーブルを参照するSQL文を書いても、\if falseの中にあるためサーバーへ送信されずエラーにならない。

参考