【Shell】【PostgreSQL】シェルでSQL結果を受け取る ~ エラーハンドリング編 ~

■ はじめに

https://dk521123.hatenablog.com/entry/2021/08/16/231459

の続き。

先日行ったシェルではエラーハンドリングまで考慮されていなかったので
EMR上でStepごとにシェル実行した際に、そのStep内でエラーが発生しても、
途中でキャンセルにならずに、継続されて実行してしまう。

ということで、エラーハンドリングを実装しようと思ったが
思いのほかハマったので、メモしておく。

目次

【1】エラーハンドリングの実装において
 1)実装上の注意点
【2】サンプル

【1】エラーハンドリングの実装において

* 以下を参考に行った。
 => ただし、以下のサイトの実装(${0} != 0)だけでは不十分。
 => 詳細は、「1)実装上の注意点」を参照のこと。

https://unix.stackexchange.com/questions/370283/how-to-save-psql-error-message-output-in-bash-variable

1)実装上の注意点

例えば、ポート番号を間違っていた場合などについては
エラーとして返却してくれるが、
存在しないテーブルを指定した場合などについては、
${0} = 0 で返却されてしまう。

考慮すべき点

* 結論としては、標準エラー出力まで見なくては、
 正確なエラーハンドリングにはならない。
 ただし、※1について注意。
 (この辺は、仕様によって、どこまで考慮するかを決めればいい)

=> 標準エラーを下記「サンプル」のようにファイル保存している場合は
  そのファイルを実行前にクリアしておくなどの対応が必要
 (「サンプル」だと「trap 'rm -f "$errorlog"' EXIT」部分)

=> 下記「サンプル」を改めてみると、
  標準エラーだけみれば${0}をみる必要ないかもって思った

※1:DROP TABLE IF <table_name>について

* DROP TABLE IF <table_name> 時でも、標準エラーが発生してしまうので注意
 => 「IF EXISTS」してんだからエラーに出力しないでくれよ、、、

experiment2.sql

DROP TABLE IF EXISTS :target_table_name;

実行結果

$ ./experiment2.sh Mike 5432 aaa
TARGET_USER_NAME = Mike
PORT = 5432
TARGET_TABLE_NAME = aaa
Result : 0
Error from SQL... <= エラーになってしまう
psql:experiment2.sql:1: NOTICE:  テーブル"aaa"は存在しません、スキップします

※補足:「set -e」について

なお、この注意点は、
以下の関連記事に記載している「set -e」を付加しても
エラーハンドリングできなかった。

https://dk521123.hatenablog.com/entry/2018/03/03/210642

【2】サンプル

https://dk521123.hatenablog.com/entry/2021/08/16/231459

の「【4】の例2」をベースにエラーハンドリングを追加したもの。

 => エラーを起こしやすく実験しやすいように
  引数「PORT=${2}(ポート番号)」
  「TARGET_TABLE_NAME=${3}(テーブル名)」を追加

experimen2.sh

#!/bin/bash

# {1} : target user name (e.g. 'Mike')
# {2} : Port number (e.g. 5432)
# {3} : target table name (e.g. 'user')
TARGET_USER_NAME=${1}
PORT=${2}
TARGET_TABLE_NAME=${3}

echo "TARGET_USER_NAME = ${TARGET_USER_NAME}"
echo "PORT = ${PORT}"
echo "TARGET_TABLE_NAME = ${TARGET_TABLE_NAME}"

# Error Handling
error_log="./error.log"

trap 'rm -f "$errorlog"' EXIT

user_count=$(psql -qtAX -h localhost -p ${PORT} -d sample_db -U postgres -f experimen2.sql -v target_name="'${TARGET_USER_NAME}'" -v target_table_name="${TARGET_TABLE_NAME}" 2> "$error_log")
if [[ 0 -ne ${?} ]]; then
  echo "Something went wrong; error log follows:"
  cat "${error_log}"
  exit ${?}
fi
# error file が空じゃない場合、DB内でエラーになっているのでチェック
if [[ -s $error_log ]]; then
  echo "Error from SQL..."
  cat "${error_log}"
  exit 1
fi

echo "user_count = ${user_count}"

# ${user_count} > 0
if [ ${user_count} -gt 0 ]; then
  echo "OK"
  exit 0
else
  echo "ERROR..."
  exit 1
fi

experimen2.sql

SELECT
  COUNT(*) AS user_count
FROM
  :target_table_name
WHERE
  user_name=:target_name
;

出力結果

$ ./experimen2.sh Mike 5431 users <= 間違ったポート番号を指定
TARGET_USER_NAME = Mike
PORT = 5431
TARGET_TABLE_NAME = users
Something went wrong; error log follows:
psql: ...: Connection refused (0x0000274D/10061)

$ ./experiment2.sh Mike 5432 users <= 正常
TARGET_USER_NAME = Mike
PORT = 5432
TARGET_TABLE_NAME = users
user_count = 1
OK

$ ./experimen2.sh xxxx 5432 users <= 正常だけで検索に引っかからないデータ
TARGET_USER_NAME = xxxx
PORT = 5432
TARGET_TABLE_NAME = users
user_count = 0
ERROR...

$ ./experimen2.sh Mike 5432 xxxxx <= 存在しないテーブル名を指定
TARGET_USER_NAME = Mike
PORT = 5432
TARGET_TABLE_NAME = xxxx
Error from SQL...
psql:experimen2.sql:7: ERROR:  リレーション"xxxx"は存在しません

関連記事

シェル ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2014/10/23/005406
シェルでSQL結果を受け取る
https://dk521123.hatenablog.com/entry/2021/08/16/231459
psql コマンドでファイル実行した際に引数を渡すには
https://dk521123.hatenablog.com/entry/2021/08/01/000000
psqlでパスワードを省略する
https://dk521123.hatenablog.com/entry/2020/03/06/000000
シェル ~ 基本編・条件分岐 if / case ~
https://dk521123.hatenablog.com/entry/2015/05/01/000043
シェルスクリプトあれこれ
https://dk521123.hatenablog.com/entry/2018/03/03/210642
ファイルへの書き出し
https://dk521123.hatenablog.com/entry/2021/08/14/000000
ヒアドキュメント ~ 複数行の テキストをファイル出力する ~
https://dk521123.hatenablog.com/entry/2016/05/13/231535