PostgreSQL 警告をクエリ実行後にクリアして二重表示を防止
PostgreSQL アダプターは、execute メソッドでのみ警告リストをクリアしていたため、select_all など他のクエリで発生した警告が次回のクエリで再度報告される問題がありました。今回の変更は、全てのクエリ実行後に警告を確実に処理・クリアし、同一警告の二重表示を防ぎます。
背景
警告リストがクリアされない ことにより、複数クエリを連続実行した際に前回の警告が残り、execute 以外のメソッドで生成された警告が次のクエリで再度報告されていました。テストコードでも exec_query 後に警告が残るケースが確認されており、実運用での誤解やノイズ増加が懸念されていました。この PR はその状態を修正し、警告の取り扱いを一貫させることを目的としています。
技術的な変更
抽象データベースステートメントの修正
activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb の execute_intent メソッドに ensure ブロックが追加され、クエリ実行後必ず handle_warnings が呼び出されるようになりました。失敗したクエリの場合は、警告処理で発生した例外を元の例外が発生していないときだけ抑制し、元のエラーを隠さない設計です。
@@
- result = perform_query(conn, intent)
- intent.raw_result = result
- handle_warnings(result, intent.processed_sql)
+ begin
+ result = perform_query(conn, intent)
+ intent.raw_result = result
+
+ query_completed = true
+ ensure
+ begin
+ handle_warnings(result, intent.processed_sql)
+ rescue
+ raise if query_completed
+
+ # The query failed, so we need to swallow this exception
+ # from handle_warnings to avoid masking the original.
+ end
+ end
PostgreSQL 固有ステートメントの修正
activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb では、execute の ensure 部分で警告リストをリセットしていた処理を削除し、代わりに handle_warnings 内で警告配列を取得してから空にしています。これにより、execute だけでなく exec_query など他のメソッドでも同様に警告がクリアされます。
@@
- @notice_receiver_sql_warnings = []
+ warnings, @notice_receiver_sql_warnings = @notice_receiver_sql_warnings, []
+
+ warnings.each do |warning|
next if warning_ignored?(warning)
warning.sql = sql
テストの追加
activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb に 4 つのテストが追加され、警告がクリアされるケース、警告処理が例外を隠さないケース、失敗したクエリ後に次のクエリで警告が正しく取得できるケースを検証しています。これにより、変更の安全性が自動テストで保証されます。
設計判断
警告クリアの責務をクエリ実行層に集約 する方針が採られました。従来は execute のみがリセットを担っていたため、他メソッドでの警告残存という不整合が生じていました。全クエリの完了時に handle_warnings が必ず走るようにしたことで、責務が一元化され、将来別のクエリメソッドが追加されても同様の問題が再発しにくくなります。また、警告処理中の例外は元のクエリが成功した場合にのみ再送出し、失敗時は元の例外を優先することで 例外のマスキング防止 を実現しています。これらの判断はコードの可読性と堅牢性のバランスを考慮した結果です。
まとめ
この PR は、PostgreSQL アダプターが生成する警告を全クエリ実行後に確実に処理・クリアすることで、同一警告の二重表示を防ぎ、例外マスキングを回避する設計変更です。テスト追加により振る舞いが保証され、将来的なクエリ実装拡張でも一貫した警告管理が期待できます。