くらしのマーケット開発ブログ

「くらしのマーケット」を運営する、みんなのマーケット株式会社のテックブログ。積極採用中です。

BashとPSQLを使う時の注意事項

こんにちは!エンジニアのカーキです。 今日はBashPSQLを使ってSQLを実行した時にハマったエピソードについて紹介したいと思います。

なにでハマったのか?

自分が書いたBashスクリプトpsqlを使っていくつかの簡単なSQL (主にINSERT, UPDATE, DELETE)を実行しようとした時にスクリプトが成功してもデータベースには期待していた変更がありませんでした。

実行していたSQLは今まで成功していたSQLだったので正しいと思い込んでスクリプトの動作だけで確認してたのですが、実はpermission 関係でSQLが失敗していたようです。ただ、Bashスクリプトのエラー時の設定とpsqlの実行方法によってSQL上のエラーに気付かず、スクリプトが最後まで走って、成功したように見えたいただけでした。

エラーに気付かなかった要因とその解決方法

要因 1.

Bashはデフォルトで実行途中でエラーになっても次に進むので注意しなければならない。

例: 以下のスクリプトを実行すると、cd のところでエラーになりますが、スクリプトはそこで中断されず、次の echo コマンドまで実行されてしまう。

#!/bin/bash

echo "This should run"
cd 存在しないパス
echo "This should not run!"

実行結果:

~/workspace ❯ ./hoge.sh
This should run
./hoge.sh: line 4: cd: 存在しないパス: No such file or directory
This should not run!

~/workspace ❯ 

解決方法:

-e フラグをセットすることで、エラーになったらすぐにスクリプトが止まるようにすることができます 。-e をセットするとBashが各コマンドの終了ステータス (Exit status)を確認し、0(成功)以外の終了ステータスが出たらスクリプトを止めてくれるみたいです。

-eフラグは以下のいずれかのやり方でできます:

  • シバンに -e を付ける
    #!/bin/bash -e

    echo "This should run"
    cd 存在しないパス
    echo "This should not run!"
    #!/bin/bash

    set -e
    echo "This should run"
    cd 存在しないパス
    echo "This should not run!"

実行結果:

~/workspace ❯ ./hoge.sh
This should run
./hoge.sh: line 4: cd: 存在しないパス: No such file or directory

~/workspace ❯ 

要因 2.

Postgresを使っていればpsql (Postgresのクライアント) を使ってSQLを実行することがよくありますが、その時に注意しないとエラーに気付かない場合があります。

psqlSQLコマンドを実行したい時に、ファイルから実行する方法と直接 psqlコマンドに-cオプションで実行する方法がありますが、それぞれ少し挙動が違います。 -cで実行した場合SQLでエラーになったらpsqlコマンドの終了コードが1 (エラー) になりますが、ファイルを流してSQL実行した時に終了ステータスが 0になります。

ということは、もしBash-cSQL実行してエラーになったらBash的にエラー扱いになりますが、同じSQLをファイルで流して実行すると終了ステータスが0になるので成功扱いになってしまいます。

例: 必ずエラーになるSQLを両パターンで実行してみましょう。 エラーになるように存在しないテーブルをSELECTしてみます。

  • -cオプションで実行した場合:
    ~/workspace ❯ psql -U postgres -h localhost -p 5432 postgres -c "SELECT * FROM foo"    
    ERROR:  relation "foo" does not exist
    LINE 1: SELECT * FROM foo
                        ^

    ~/workspace ❯ echo $?
    1    
    👆 終了ステータス !=0 なのでエラー扱いになる
  • ファイルに同じSQLを書いて実行した場合:
    ~/workspace ❯ psql -U postgres -h localhost -p 5432 postgres < foo.sql
    ERROR:  relation "foo" does not exist
    LINE 1: SELECT * FROM foo
                        ^

    ~/workspace ❯ echo $?
    0
    👆 終了ステータス = 0 なので成功扱いになる

例の通り、psqlでファイルを流してSQL実行した時にSQLでエラーが出ても終了コードが0になるので、エラーになった時に気付きづらいです。こうなるといくらBash-e フラグをセットしてもエラーとして扱われないので危険です。

解決方法:

psql コマンドに ON_ERROR_STOPオプションをセットすれば、エラーになったらすぐにスクリプトが中断されるようになります。

例:

~/workspace ❯ psql -v ON_ERROR_STOP=1 -U postgres -h localhost -p 5432 postgres < foo.sql
ERROR:  relation "foo" does not exist
LINE 1: SELECT * FROM foo
                      ^

~/workspace ❯ echo $?
3
👆 終了ステータス != 0 なのでエラー扱いになる

以上Bashpsqlを使った時の注意事項でした。終了ステータスをちゃんと意識しないとハマりやすいところだと思うのでみなさんも気をつけましょう!