Fix reversibility check for `drop_virtual_table`
drop_virtual_table の逆変換ガードが誤った引数を参照していたため、values が欠如していても不可逆と判定できないバグを修正しました。正しい values の有無をチェックできるようにし、テストで挙動を検証しています。
背景
drop_virtual_table は テーブル名・モジュール名・列定義(values) の3つの位置引数を取りますが、逆変換ロジック invert_drop_virtual_table が invert_drop_enum をコピーした形で2つしかデストラクチャリングしていませんでした。結果として values が常に存在すると誤認識され、ActiveRecord::IrreversibleMigration ガードが発火しませんでした。
この欠陥は、values が省略された状態で drop_virtual_table が記録された場合に、逆変換として生成される create_virtual_table が実行時エラーを起こす根本原因となっていました。リバースマイグレーションの信頼性が損なわれる重大な問題です。
修正前は module_name が常に存在するため、欠損した values が検出されずにマイグレーションが不完全な形で逆適用されていました。
技術的な変更
invert_drop_virtual_table の引数デストラクチャリングを 3要素 に修正し、正しく values を取得するようにしました。これにより values が nil の場合に ActiveRecord::IrreversibleMigration が適切にスローされます。
@@
- _enum, values = args.dup.tap(&:extract_options!)
+ _table_name, _module_name, values = args.dup.tap(&:extract_options!)
raise ActiveRecord::IrreversibleMigration, "drop_virtual_table is only reversible if given options." unless values
super
加えて、command_recorder_test.rb に逆変換不可ケースを検証するテストを追加し、values が無い場合に例外が発生することを明示しました。
@@
def test_invert_drop_virtual_table_without_options
@recorder.inverse_of :drop_virtual_table, [:searchables]
end
+
+ def test_invert_drop_virtual_table_without_values
+ assert_raises(ActiveRecord::IrreversibleMigration) do
+ @recorder.inverse_of :drop_virtual_table, [:searchables, :fts5]
+ end
+ end
この変更は既存の呼び出しシグネチャを保持したまま、内部ロジックだけを正しく修正するので、既存アプリケーションへの後方互換性に影響を与えません。
設計判断
バグ修正の選択肢としては、引数検証ロジックを別に追加する方法や drop_virtual_table のシグネチャを変更する方法が考えられましたが、既存 API の互換性を最優先し、デストラクチャリング修正だけで対処する方針が採られました。これにより、呼び出し側のコードは変更不要で、マイグレーションの記録形式も変わりません。
また、テスト追加により リバースマイグレーションの安全性 が自動的に検証されるようになり、将来的な類似バグの回帰防止が期待できます。トレードオフとしては、ガードロジックが invert_drop_virtual_table 内部に埋め込まれるため、他のマイグレーションコマンドでも同様のチェックが必要になる場合は個別実装が必要になる点です。
まとめ
drop_virtual_table の逆変換ガードが誤った引数を参照していたバグを、デストラクチャリング修正とテスト追加で解消しました。これにより values が欠如した場合に 不可逆マイグレーション例外 が正しく発生し、リバースマイグレーションの信頼性が向上します。