MySQL のインデックス有効化/無効化でインデックス名をクォート
Rails の MySQL アダプタで enable_index と disable_index がインデックス名を正しくクォートするようになり、予約語や特殊文字を含むインデックスでもマイグレーションが失敗しなくなります。
背景
このセクションでは、インデックス名が未クォートで生成されたことがもたらした問題点を示します。MySQL / MariaDB では、予約語(例: order)やハイフン等の特殊文字を含む識別子はバッククオートで囲む必要があり、未クォートのまま ALTER INDEX を実行すると構文エラーが発生します。実際に ALTER TABLEtestingsALTER INDEX order VISIBLE と生成された SQL がエラーとなり、マイグレーションのデプロイ時に障害が報告されていました。
技術的な変更
このセクションでは、コードベースに加えられた具体的な修正内容を解説します。abstract_mysql_adapter.rb の enable_index と disable_index メソッドで、インデックス名を quote_column_name で囲むよう変更され、生成される SQL がバッククオートで安全にエスケープされるようになりました。
変更前(enable_index):
ALTER TABLE #{quote_table_name(table_name)} ALTER INDEX #{index_name} #{mariadb? ? "NOT IGNORED" : "VISIBLE"}
変更後(enable_index):
ALTER TABLE #{quote_table_name(table_name)} ALTER INDEX #{quote_column_name(index_name)} #{mariadb? ? "NOT IGNORED" : "VISIBLE"}
同様に disable_index でもインデックス名が quote_column_name でクォートされるよう修正され、ALTER INDEX 文が安全に実行できるようになりました。
変更前(disable_index):
ALTER TABLE #{quote_table_name(table_name)} ALTER INDEX #{index_name} #{mariadb? ? "IGNORED" : "INVISIBLE"}
変更後(disable_index):
ALTER TABLE #{quote_table_name(table_name)} ALTER INDEX #{quote_column_name(index_name)} #{mariadb? ? "IGNORED" : "INVISIBLE"}
テストスイートにも test_disable_and_enable_index_with_reserved_word_name が追加され、予約語 order を名前に持つインデックスの無効化・有効化が期待通りに動作することを検証しています。
この変更は API のシグネチャを変更せず、既存の呼び出し側に影響を与えません。enable_index(table, name) と従来通り呼び出すだけで、内部で安全にクォートされた SQL が実行されます。
設計判断
このセクションでは、採択された実装方針とその背景を説明します。インデックス名のエスケープに新規ヘルパーを導入せず、quote_column_name を再利用することで、コードベース全体で一貫したクォートロジックを保ちました。
既存の quote_column_name はテーブル名・カラム名のエスケープに広く利用されており、追加の抽象化層を設けないことで保守コストを最小化しています。これにより、他のインデックス操作(例: rename_index)と同様の振る舞いが保証されました。
結果として、インデックス名の取り扱いが安全になると同時に、既存コードとの後方互換性が維持され、開発者は追加の変更なしに新機能を利用できるようになりました。
まとめ
enable_index と disable_index がインデックス名を正しくクォートするようになったことで、予約語や特殊文字を含むインデックスでもマイグレーションが安定します。このシンプルな修正は既存インターフェースをそのまま活かしつつ、SQL 構文エラーの根本原因を除去し、Rails の MySQL アダプタの信頼性を向上させました。