postgresqlでERROR portal "C_n" does not existが出た時の原因と対処法
2023/11/26
javaアプリからpostgreSQLへアクセスして処理している間に、portal “C_7”が見つからないというようなエラーが発生したため原因と対処法を紹介します。
エラーの内容
javaアプリの中でトランザクションを作りループ処理をしていた時に、ループ2回目で以下のようなエラーがjavaアプリで出ました。postgreSQLサーバ側でも調べてみると、同様のエラーが出ていたため、DBサーバ側でエラーとなったと思われます。
org.postgresql.util.PSQLException: ERROR: portal "C_1" does not exist
エラーの詳細
portal “C_n”はカーソルを表しているようでした。C_nという名前のカーソルへアクセスしようとしたときに、そのカーソルがDBサーバ上からなくなっていると本エラーが出るようです。
カーソル名は変わるため、C_1やC_2だったりしますので、そこは変動します。
ループ処理中にcommitしたことが原因
トランザクション中に最初にselectでカーソルを取得し、そのループ処理中で毎回Commitをしていたのですが、どうやらCommitをすると最初に取得したカーソルが解放されてなくなってしまい、2回目のループでカーソルがないというエラーになったようでした。
対策①:コミットはループ処理の最後に変更
ループごとに毎回コミットするのをやめて、ループ処理の最後にコミットをすることで解消することができます。
但し、トランザクションの単位が変わってしまうため、エラー時の運用影響がないなどの確認が必要です。
対策②:With holdオプションを利用する
javaやjdbcから直接指定はできないですが、PL/pgSQLでは
DECLARE CURSOR WITH HOLD
オプションを指定することで、トランザクションがコミット処理をした後もカーソルを継続利用できるようになります。
対策③:javaではjdbcのcreateStatementやprepareStatementのresultSetHoldabilityオプションで設定
javaからwith hold同等のオプションを利用するためにはcreateStatementやprepareStatementのresultSetHoldabilityオプションで指定できるようです。
但し、PGConnectionで拡張する必要があります。
以下のresultSetHoldability引数に、ResultSet.HOLD_CURSORS_OVER_COMMITを指定します。
publicStatement createStatement(int resultSetType,
int resultSetConcurrency,
int resultSetHoldability)
throwsSQLException
publicPreparedStatement prepareStatement(String sql,
int resultSetType,
int resultSetConcurrency,
int resultSetHoldability)
throwsSQLException
PgConnection (PostgreSQL JDBC postgresql API version 42.3.1)