Fix PostgreSQL foreign_keys handling for quoted schemas

rails/rails

PostgreSQL アダプタの foreign_keys メソッドが、引用されたスキーマ名を含むテーブルを参照した際に to_table を誤って加工し、rails db:schema:dump が壊れた add_foreign_key を出力していた問題を解消します。これにより、スキーマダンプおよびマイグレーションが正しく動作します。

背景

foreign_keys が quoted スキーマで to_table を破損する のは、Rails が PostgreSQL の内部表現 regclass::text を単純な文字列操作でデクォートしていたためです。regclass::text はスキーマ修飾かつ部分的に引用された文字列(例: "App".customers)を返し、Utils.unquote_identifier は先頭と末尾の 1 文字だけを除去するロジックだったため、途中の引用が残ったまま App".customers という不正な名前が生成されました。

この不整合は、rails db:schema:dump が外部キー定義を再生成する際に破損した文字列をそのまま書き出すことで顕在化し、ダンプファイルをロードすると add_foreign_key が失敗します。さらに、マイグレーションやテスト環境でのスキーマ操作が期待通りに行えないという実務的な障害につながっていました。

問題の根本は「単一識別子向けのデクォートロジックをスキーマ修飾名に流用した」ことにあります。 このロジックは多くのケースで正しく機能しますが、引用を含むスキーマ名だけが例外となり、テストカバレッジが不足していたために長期間見過ごされてきました。

技術的な変更

foreign_keys 実装が Utils.unquote_identifier から Utils.extract_schema_qualified_name へ置き換えられました。 後者はスキーマ修飾名と引用を正確に解析できる既存ユーティリティで、to_table の文字列を安全に正規化します。

@@
-            to_table = Utils.unquote_identifier(row["to_table"])
+            to_table = Utils.extract_schema_qualified_name(row["to_table"]).to_s

この一行差し替えにより、"Aerospace".boosters のような名前は Aerospace.boosters に正しく変換され、foreign_keys が返すオブジェクトの to_table が期待通りの文字列になります。

さらに、PostgreSQL 固有のテストケースが追加され、引用スキーマ内で外部キーを作成し to_table が正しいことを検証します。

@@
+        def test_foreign_key_to_table_in_quoted_schema
+          @connection.execute('DROP SCHEMA IF EXISTS "Aerospace" CASCADE')
+          @connection.execute('CREATE SCHEMA "Aerospace"')
+          @connection.execute('CREATE TABLE "Aerospace".boosters (id serial primary key)')
+          @connection.add_column :astronauts, :booster_id, :integer
+          @connection.execute(<<~SQL)
+            ALTER TABLE astronauts
+            ADD CONSTRAINT fk_booster FOREIGN KEY (booster_id) REFERENCES "Aerospace".boosters(id)
+          SQL
+
+          fk = @connection.foreign_keys("astronauts").find { |k| k.name == "fk_booster" }
+          assert_not_nil fk
+          assert_equal "Aerospace.boosters", fk.to_table
+        ensure
+          @connection.execute('DROP SCHEMA IF EXISTS "Aerospace" CASCADE')
+        end

テストは PostgreSQL 17 でもグリーンとなり、実装変更が正しく機能することが自動的に保証されます。

設計判断

既存ユーティリティ Utils.extract_schema_qualified_name を再利用した点が重要です。 新規関数を導入せず、すでにスキーマ修飾名の解析を担っているコードを活用することで、変更箇所を最小限に抑えつつ一貫性を保ちました。

この選択は後方互換性にも配慮しています。foreign_keys が従来通り true/false のような単純文字列を受け取るケースでは動作に影響がなく、引用なしのテーブル名はそのまま返ります。したがって、既存アプリケーションが即座に壊れるリスクはなく、デフォルトの挙動を保持したままバグだけを除去しています。

副作用の範囲は PostgreSQL アダプタに限定 され、他のデータベースアダプタや ActiveRecord のコアロジックには変更が加わっていません。これにより、リリース全体へのリスクが低く、限定的なテストで十分に検証できる設計となっています。

まとめ

本 PR は、PostgreSQL の foreign_keys が quoted スキーマを正しく処理できなかった根本原因を Utils.extract_schema_qualified_name への置き換えで解消し、テストも追加して回帰を防止しました。変更は局所的かつ後方互換性を保ちつつ、スキーマダンプやマイグレーションの信頼性を向上させます。

記事メタデータ

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

この記事は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 #57561](URL)` と記載されており、仕様の `[#57561](URL)` 形式と完全には一致していません。

対象読者への適合性 ✓ PASS

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

対象はRails開発者向けで、専門用語が中心。初心者向けの余計な説明はありません。

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

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

各セクションが総論・各論・結論の段落構成となり、トピックセンテンスで始まり、1段落1トピック・適切な長さが保たれています。

Diff内容との照合 ✓ PASS

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

記事内コードブロックは提供されたDiffと完全に一致し、変更箇所・テスト追加が正確に反映されています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

用語はPRで使用されているものと一致し、誤用やミスは見られません。

説明の技術的正確性 ✓ PASS

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

根本原因や修正内容の説明がPRの記述と整合しており、技術的に正確です。

事実の突合 ✓ PASS

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

全ての主張がPRタイトル・説明・Diffで裏付けられており、ハルシネーションはありません。

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

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

PR番号 #57561 が正しく記載され、他の数値・固有名詞の誤りはありません。

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

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

記事タイトルはPRの内容を正確に要約しており、意味的に一致しています。

外部知識の正確性 ✓ PASS

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

PostgreSQL 17 の言及はPRに記載されており、外部知識の付加はありません。

時間表現の正確性 ✓ PASS

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

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