unique_constraint_exists? が :column を受け付けるよう修正
PostgreSQL 用の unique_constraint_exists? が、列指定(:column)で呼び出すと ArgumentError を投げていたバグを修正しました。従来は :name か :expression が必須とされ、列ベースのチェックができませんでした。今回の変更で、同メソッドは :column オプションを正しく受け取り、真偽値を返すようになります。
背景
unique_constraint_exists? はユニーク制約の有無を判定するユーティリティで、他のユニーク制約関連メソッド(例: remove_unique_constraint や unique_constraint_for)は :column キーで操作します。にもかかわらず、当該メソッドは name または expression が必要とされ、列指定で呼び出すと例外が発生していました。結果としてマイグレーションやスキーマチェックで正しい判定ができず、開発者は余計なエラーハンドリングを強いられていました。
この不整合は、除外制約(exclusion_constraint_exists?)のガードロジックが流用されたことが原因です。除外制約は式ベースで定義されるため :expression が正当ですが、ユニーク制約では列ベースが主流です。ガードが不適切だったため、エラーメッセージも実際に受け付けない :expression を指していました。
技術的な変更
ガードロジックを !options.key?(:name) && !options.key?(:column) に変更し、エラーメッセージも "At least one of :name or :column must be supplied" に更新しました。これにより :column が指定された場合は例外が発生せず、内部で unique_constraint_for が正しく処理されます。
@@
- if !options.key?(:name) && !options.key?(:expression)
- raise ArgumentError, "At least one of :name or :expression must be supplied"
+ if !options.key?(:name) && !options.key?(:column)
+ raise ArgumentError, "At least one of :name or :column must be supplied"
end
テストも同様に拡張され、column: オプションでの成功・失敗ケースが確認できるようになりました。追加されたテストは unique_constraint_test.rb に以下のように記述されています。
@@
- assert_not @connection.unique_constraint_exists?(:sections, column: :non_position, name: "unique_constraint")
+ assert @connection.unique_constraint_exists?(:sections, column: :position)
+ assert_not @connection.unique_constraint_exists?(:sections, column: :non_position)
設計判断
ガード条件を :column に合わせたのは、既存 API と一貫性を持たせるための判断です。remove_unique_constraint が列ベースで動作し、unique_constraint_for が同様のオプションを受け取る中で、unique_constraint_exists? だけが例外的に :expression を要求していた点を統一しました。
除外制約とユニーク制約は概念的に異なるため、ロジックのコピーは避けるべきという議論がありました。結果として、不要な :expression ガードが除去され、エラーメッセージも実際に受け付けるオプションに合わせて修正されました。これにより API の予測可能性が向上し、開発者は一貫したインターフェースで制約チェックを行えます。
まとめ
本修正は unique_constraint_exists? が PostgreSQL のユニーク制約を列ベースで判定できるようにし、例外を防いで正しい真偽値を返すようにしました。ガードロジックとエラーメッセージの統一により、他のユニーク制約操作と API の整合性が保たれ、マイグレーションやテストの信頼性が向上します。