PostgreSQLのenumスキーマダンプで型名を完全修飾名で出力するように修正
PostgreSQLにおいて、db:schema:dumpでenumカラムの:enum_typeオプションを出力する際、異なるスキーマに同名のenumが存在する場合に曖昧な型名が生成される問題を修正しました。スキーマ名を含む完全修飾名を使用することで、スキーマロード時に誤った型が参照されるリスクを排除します。
背景
PostgreSQLでは、同一データベース内の異なるスキーマに同名のenumを定義できます。この状況でdb:schema:dumpを実行すると、:enum_typeオプションにスキーマ名なしの型名が書き出されるため、どのスキーマのenumを参照するか曖昧になる問題がありました。
例えば、public.moodとtest_schema.moodというenumが両方存在する環境でtest_schemaに属するテーブルをダンプした場合、出力が enum_type: "mood" のままでは、スキーマロード時にsearch_pathの設定次第で意図しない方のenumが使われる可能性がありました。この問題は #56278 で最初に提起され、今回のPRはそのリベースおよびリワークとして取り込まれています。
複数スキーマをダンプ対象とするケースでは、型の帰属先を一意に特定できなければスキーマの再現性が保てません。完全修飾名の出力はこの前提条件を満たすための必要な変更です。
技術的な変更
変更の核心は postgresql/schema_dumper.rb の1行で、relation_name メソッドを介して型名を出力するように切り替えた点にあります。
変更前:
spec[:enum_type] = column.sql_type.inspect if column.enum?
変更後:
spec[:enum_type] = relation_name(column.sql_type).inspect if column.enum?
relation_name は既存のヘルパーメソッドであり、型名がまだ完全修飾されていない場合かつ複数スキーマがダンプ対象に含まれる場合に、スキーマ名を付与して返します。すでに完全修飾名が付いている場合はそのまま返すため、二重にスキーマ名が付くことはありません。
テストでは、test_schema.moodとpublic.moodが共存する状況を新たに再現するケース test_schema_dump_enums_with_same_name_in_different_schemas が追加されています。このテストにより、postgresql_enums_in_test_schemaテーブルのダンプ出力に enum_type: "test_schema.mood" が含まれることが検証されます。また、既存のテスト test_schema_dump_scoped_to_schemas も期待値を "mood_in_test_schema" から "test_schema.mood_in_test_schema" に更新し、複数スキーマ環境では常に完全修飾名が出力されることを確認しています。
設計判断
既存のrelation_nameメソッドを再利用するアプローチが選ばれており、スキーマ修飾の有無を判定するロジックをprepare_column_options内に新たに書き下ろすことはしていません。
relation_nameは他のリレーション(テーブル名など)の完全修飾名を解決するために既に使われているメソッドであり、enumの型名にも同じロジックを適用することで、スキーマ修飾の判断基準を一箇所に集約しています。変更箇所が1行にとどまるのは、この設計の一貫性の表れです。
また、複数スキーマがダンプ対象に含まれる場合のみスキーマ名を付与するというrelation_nameの挙動により、単一スキーマ構成(最も一般的なケース)では従来通りの短い型名が出力されます。これにより、既存プロジェクトのスキーマダンプ出力に不必要な変化を与えない配慮がされています。
まとめ
1行の変更ながら、複数スキーマを運用するPostgreSQL環境でのスキーマダンプの信頼性を大きく向上させる修正です。relation_nameという既存の抽象化を活用することで、enumの型参照もテーブルやその他のリレーションと同じ一貫したルールのもとで完全修飾名を解決できるようになりました。