PostgreSQL 警告をクエリ実行後に確実にクリアする修正
この PR は、execute 以外のクエリでも PostgreSQL の SQL 警告が残らないようにし、障害時に警告処理が元例外を隠さないよう振る舞いを統一しました。
背景
PostgreSQL の警告は @notice_receiver_sql_warnings に蓄積され、従来は execute メソッドの ensure 節でのみクリアされていました。そのため exec_query や select_all といった他のラッパーメソッドで警告が発生すると、次回以降のクエリでも古い警告が再び報告されるという状態が残っていました。また、クエリが例外を投げた場合に handle_warnings が再度例外を発生させると、元のエラーが隠蔽されるリスクがありました。PR #57584 でこれらの課題が指摘されたことを受け、8‑1‑stable に同様の修正をバックポートする必要がありました。
技術的な変更
抽象層 (abstract/database_statements.rb) では、クエリ実行を begin … ensure で包み、handle_warnings 呼び出しを必ず実行するようにしました。query_completed フラグで例外が発生したかどうかを判定し、クエリが失敗した際に handle_warnings が例外を上げても元例外を上書きしないように raise if query_completed としています。
@@
- result = perform_query(conn, sql, binds, type_casted_binds, prepare: prepare, notification_payload: notification_payload, batch: batch)
- handle_warnings(result, sql)
+ begin
+ result = perform_query(conn, sql, binds, type_casted_binds, prepare: prepare, notification_payload: notification_payload, batch: batch)
+ query_completed = true
+ ensure
+ begin
+ handle_warnings(result, sql)
+ rescue
+ raise if query_completed
+ # query failed → swallow warning‑handling error
+ end
+ end
PostgreSQL 固有実装 (postgresql/database_statements.rb) では、execute の ensure で行っていた警告クリアを削除し、代わりに handle_warnings 内で警告配列をリセットします。これにより、全てのクエリ入口で同一ロジックが適用されます。
@@
- @notice_receiver_sql_warnings = []
+ # cleared by handle_warnings
@@
- @notice_receiver_sql_warnings.each do |warning|
+ warnings, @notice_receiver_sql_warnings = @notice_receiver_sql_warnings, []
+
+ warnings.each do |warning|
next if warning_ignored?(warning)
warning.sql = sql
テスト も追加され、exec_query 後に警告がクリアされること、警告ハンドラが :raise を指定した際に例外が正しく伝播すること、クエリ失敗時に警告処理が例外を隠さないことを検証しています。これらのテストは新たに 54 行が追加され、全ケースがパスすることが確認されています。
設計判断
警告クリアの責務を handle_warnings に集約 したことは、execute に限らず全てのデータベースステートメントで一貫した動作を提供する設計です。ensure 節での例外管理は、元の例外を保護しつつ警告処理エラーを安全に無視する最小限のロジックに留められました。これにより既存コードへの影響はなく、警告リストがクエリ間で汚染されるリスクだけが排除されます。
まとめ
本バックポートは、PostgreSQL 警告のクリアを全クエリパスに統一し、失敗時の例外隠蔽を防止することで、Rails アプリケーションのデータベース操作の信頼性を向上させました。既存の execute 動作は変更せず、警告処理ロジックだけを整理した点が特徴です。