remove_check_constraintのif_existsが式形式でも正しく動作するよう修正

rails/rails

remove_check_constraintif_exists: true を指定した際、式(expression)で指定したチェック制約が存在しないと ArgumentError が発生し、期待通りの no‑op にならなかった問題を解消します。

背景

remove_check_constraint は制約名または のどちらでも削除でき、if_exists オプションは対象が見つからない場合に例外を抑制するために提供されています。実装上は check_constraint_exists? に制約情報を渡して存在チェックを行う設計です。

しかし、式形式で呼び出したとき if_exists の判定が expression をオプションに含めずcheck_constraint_exists? を呼び出していたため、nameexpression も渡らず ArgumentError: At least one of :name or :expression must be supplied がスローされました。結果として if_exists: true が期待通りに機能せず、マイグレーションの可逆性が損なわれていました。

技術的な変更

抽象アダプタ (abstract/schema_statements.rb) では、存在チェックの呼び出しに expression: expression を追加しました。

@@ -1445,7 +1445,7 @@ def check_constraint_options(table_name, expression, options) # :nodoc:
       def remove_check_constraint(table_name, expression = nil, if_exists: false, **options)
         return unless supports_check_constraints?

-        return if if_exists && !check_constraint_exists?(table_name, **options)
+        return if if_exists && !check_constraint_exists?(table_name, expression: expression, **options)

         chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name

この変更により、if_exists 判定が式情報を正しく参照でき、制約が存在しない場合は単にリターンし、例外は発生しません。

SQLite3 アダプタ (sqlite3/schema_statements.rb) でも同様のパッチを適用し、オーバーライド実装でも一貫した動作を保証しています。

@@ -101,7 +101,7 @@ def add_check_constraint(table_name, expression, **options)
         end

         def remove_check_constraint(table_name, expression = nil, if_exists: false, **options)
-          return if if_exists && !check_constraint_exists?(table_name, **options)
+          return if if_exists && !check_constraint_exists?(table_name, expression: expression, **options)

           check_constraints = check_constraints(table_name)
           chk_name_to_delete = check_constraint_for!(table_name, expression: expression, **options).name

テストケース が追加され、式形式で if_exists: true を指定したとき例外が発生しないことを明示的に確認しています。

@@
           @connection.remove_check_constraint :trades, name: "quantity_check", if_exists: true
         end

+        assert_nothing_raised do
+          @connection.remove_check_constraint :trades, "quantity > 0", if_exists: true
+        end
+
         error = assert_raises ArgumentError do
           @connection.remove_check_constraint :trades, name: "quantity_check"
         end

これにより、式指定でも if_exists が正しく機能することがテストで保証されました。

設計判断

本修正は API のシグネチャを変更せず、内部ロジックだけを調整することで互換性を維持しています。既存の remove_check_constraint(table, name: ..., if_exists: true) の呼び出しは影響を受けず、式指定を利用しているコードだけが期待通りに動作するようになります。

また、ドキュメントで推奨されている式形式 と実装を合わせることで、マイグレーションの可逆性が保証されます。if_exists が正しく機能すれば、制約が削除済みでも安全にマイグレーションを再実行できるため、開発フローが一層堅牢になります。

まとめ

この PR は remove_check_constraintif_exists オプションが式形式でも正しく動作しないバグを修正し、抽象アダプタと SQLite3 アダプタでロジックを統一しました。テスト追加により回帰が防止され、マイグレーションの可逆性と開発者体験が向上します。

記事メタデータ

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

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

品質レビュー結果

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

Review Criteria:

記事構成 ✓ PASS

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

リード文、背景、技術的な変更、設計判断(任意)およびまとめがすべて揃っており、総論→各論→結論の流れが明確です。

カスタムMarkdown構文 ⚠ WARNING

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

コードブロックは `diff` のみでファイル名指定はありませんが、GitHubリンクが "PR #57871" の形式であり、仕様が求める "[#57871](URL)" とは若干異なります。リンク形式を統一すれば問題ありません。

対象読者への適合性 ✓ PASS

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

内容はRailsの内部実装に詳しいエンジニア向けで、過度な基礎説明はなく適切です。

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

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

各セクションが総論・各論・結論の構成を保ち、段落は1トピック・トピックセンテンスで始まり、長さも適切に分割されています。

Diff内容との照合 ✓ PASS

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

記事中のdiffブロックは提供されたPRのdiffと完全に一致しており、コード引用に誤りや省略はありません。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

使用されている技術用語はPRでの記述と一致しており、誤用は見られません。

説明の技術的正確性 ✓ PASS

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

バグの原因、修正内容、影響範囲の説明がPRの内容と合致しており、技術的に正確です。

事実の突合 ✓ PASS

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

記事の主張はすべてPRのタイトル・説明・diffで裏付けられており、推測や捏造はありません。

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

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

PR番号 #57871 の記載は正確で、他の数値や固有名詞の誤りはありません。

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

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

記事タイトルはPRの主旨を正確に日本語化しており、一致しています。

外部知識の正確性 ✓ PASS

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

記事はPRに記載された情報以外の外部知識を含んでいません。

時間表現の正確性 ✓ PASS

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

時間表現の使用はなく、PRの記述と齟齬はありません。