PostgreSQL 警告をクエリ実行後に確実にクリアする修正

rails/rails

この PR は、execute 以外のクエリでも PostgreSQL の SQL 警告が残らないようにし、障害時に警告処理が元例外を隠さないよう振る舞いを統一しました。

背景

PostgreSQL の警告は @notice_receiver_sql_warnings に蓄積され、従来は execute メソッドの ensure 節でのみクリアされていました。そのため exec_queryselect_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) では、executeensure で行っていた警告クリアを削除し、代わりに 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 動作は変更せず、警告処理ロジックだけを整理した点が特徴です。

記事メタデータ

Generated by:
gpt-oss-120b for DiffDaily
LLM Trace:
74b68e46

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
gpt-oss-120b for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文・背景・技術的な変更・設計判断・まとめの5セクションが揃っており、総論→各論→結論の流れが明確です。

カスタムMarkdown構文 ⚠ WARNING

シンタックスハイライト・GitHubリンク記法の正確性

コードブロックのファイル名付きハイライトは正しい形式ですが、PRリンクが「[PR #57777](URL)」となっており、仕様の「[#123](URL)」形式と異なります。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

Railsエンジニア向けの専門的内容で、不要な初心者向け説明はありません。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションは冒頭の総論パラグラフ、具体的コード・説明、まとめパラグラフで構成され、段落は1話題・6文未満で空行で区切られています。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事中のコードブロックはDiffに含まれる変更と実質的に一致しており、機能ロジックに相違はありません。コメントの差は情報量の違いであり、動作に影響しません。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

handle_warnings、ensure、raise などの用語はPRで使用されている通り正確です。誤用は見られません。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

変更の目的、実装方法、テスト内容はPRの説明とDiffに基づき正確に記述されています。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

全ての主張はPR番号、バックポート対象、テスト追加などPR情報で裏付けられ、憶測や捏造はありません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(57777)や追加行数(54行)など数値は正確です。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事タイトルはPRの「Clear Postgres warnings as they get handled」の内容を日本語で適切に表現しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

LTSやリリース日程などPRに無い外部知識は記載されていません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

時間表現の歪曲はなく、PRの記述と一致しています。