ぬまろぐ

←戻る

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だったりしますので、そこは変動します。

IMG

ループ処理中に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

PostgreSQL の JDBC API への拡張

PgConnection (PostgreSQL JDBC postgresql API version 42.3.1)