こんにちは!エンジニアのカーキです。 今日はBashとPSQLを使って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!"
- スクリプト内に
set -e
を書く
#!/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を実行することがよくありますが、その時に注意しないとエラーに気付かない場合があります。
psql
でSQLコマンドを実行したい時に、ファイルから実行する方法と直接 psql
コマンドに-c
オプションで実行する方法がありますが、それぞれ少し挙動が違います。
-c
で実行した場合SQLでエラーになったらpsql
コマンドの終了コードが1
(エラー) になりますが、ファイルを流してSQL実行した時に終了ステータスが 0
になります。
ということは、もしBashで-c
でSQL実行してエラーになったら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 なのでエラー扱いになる
以上Bash
とpsql
を使った時の注意事項でした。終了ステータスをちゃんと意識しないとハマりやすいところだと思うのでみなさんも気をつけましょう!