スキーマ修飾テーブル名のバリデーションをPostgreSQL向けに修正
PostgreSQLのマルチスキーマ構成で schema.rb のロードが失敗する問題が修正されました。validate_table_length! がスキーマプレフィックスを含む長さを測定していたため、有効なテーブル名が誤って拒否されていたバグです。
背景
schema_search_path で複数のPostgreSQLスキーマを使うアプリケーションでは、スキーマダンパーがテーブル名をスキーマ修飾形式で schema.rb に書き出します。db:schema:load や db:test:prepare がこのファイルを読み込む際に問題が発生していました。
Rails 7.1 で導入された validate_table_length! は、テーブル名がPostgreSQLの識別子長上限(63文字から _pkey の5文字を引いた58文字)を超えていないかを検証します。しかし、schema.rb から渡されるテーブル名が "public.my_table_name" のようなスキーマ修飾形式である場合、スキーマ部分(public. の8文字)を含んだ文字列全体の長さを測定していました。その結果、52文字の有効なテーブル名に public. を付加した59文字の文字列が上限の58文字を超えるとして ArgumentError を発生させていました。PostgreSQL自体はスキーマ修飾のDDLを問題なく受け入れるため、この拒否はRails側のバリデーションのみに起因するものでした(#57093)。
この問題により、マルチスキーマ構成でテーブル名が上限付近にあるアプリケーションでは db:test:prepare が恒久的に失敗する状態になっていました。
技術的な変更
postgresql/schema_statements.rb に validate_table_length! をオーバーライドするメソッドが追加され、バリデーション前にスキーマプレフィックスを取り除くようになりました。
追加されたコード:
def validate_table_length!(table_name)
_schema, table_name = extract_schema_qualified_name(table_name)
super
end
既存の extract_schema_qualified_name メソッドを利用して "public.my_table_name" を ["public", "my_table_name"] に分解し、スキーマ部分を捨ててテーブル名のみを super に渡します。"my_table_name" だけのスキーマ修飾なしの名前の場合、_schema は nil になり、テーブル名そのものが検証されます。
テストコードも同様に更新されています。test_create_table_raises_for_long_table_names において、PostgreSQLアダプター使用時の short_name(上限ちょうどの長さで ArgumentError が発生しない名前)が "public." + "a" * name_limit に変更され、スキーマプレフィックス付きの有効な名前が誤って拒否されないことを検証するようになりました。
- short_name = "a" * name_limit
+ short_name =
+ if current_adapter?(:PostgreSQLAdapter)
+ "public." + "a" * name_limit
+ else
+ "a" * name_limit
+ end
設計判断
PostgreSQL固有の schema_statements.rb 内でスーパークラスのメソッドをオーバーライドする方式が採用されています。
基底クラスの validate_table_length! 自体は変更されていません。スキーマ修飾という概念はPostgreSQL固有の機能であり、他のアダプターには影響しないため、修正をPostgreSQLアダプター層に閉じ込める設計が妥当です。また、extract_schema_qualified_name は既存の実装であり、新たなパース処理を追加せずに再利用することで変更範囲を最小限に抑えています。
override後の super 呼び出しにより、基底クラスのバリデーションロジック自体はそのまま維持されます。スキーマ修飾なしのテーブル名に対するバリデーション動作は従来と完全に同一であり、後方互換性を損なう変更はありません。
まとめ
本修正は、PostgreSQLのスキーマ修飾テーブル名という実用的なユースケースに対して、バリデーションレイヤーが正しくスコープを認識できていなかったバグを解消します。アダプター固有のオーバーライドと既存ユーティリティの再利用という最小限の変更で、マルチスキーマ構成アプリケーションの db:test:prepare を正常に機能させます。