SQLite3アダプターの `NATIVE_DATABASE_TYPES` からfreezeを解除
#57342 は、SQLite3アダプターの NATIVE_DATABASE_TYPES ハッシュに付与されていた .freeze を取り除く一行修正です。これにより、カスタム型を登録するサードパーティgemが FrozenError でクラッシュする問題が解消されます。
背景
NATIVE_DATABASE_TYPES はActive Recordアダプターにおける 公式の拡張ポイント であり、neighbor や activerecord-enhancedsqlite3-adapter のようなgemがカスタム型(例: vector)を登録するために使用しています。PostgreSQLアダプターのドキュメントにも、このハッシュを編集してカスタム型を追加できる旨が明記されています。
問題の根源は #57323 にあります。このPRはRactor安全性の確保を目的として Style/MutableConstant Rubocop copを有効化し、リテラル定数に .freeze を自動適用しました。しかし、NATIVE_DATABASE_TYPES のように外部からの変更を意図して設計された定数まで一律にfreezeされてしまい、拡張用ハッシュを変更するgemがロード時に FrozenError: can't modify frozen Hash でクラッシュするようになりました。
MySQL アダプターについては既に #57333 で同様の修正が適用されており、本PRはそのパターンをSQLite3アダプターに対して踏襲するものです。
技術的な変更
変更内容は sqlite3_adapter.rb の NATIVE_DATABASE_TYPES 定義から .freeze を削除し、rubocop:disable コメントを追加する一行修正です。
変更前:
NATIVE_DATABASE_TYPES = {
primary_key: "integer PRIMARY KEY AUTOINCREMENT NOT NULL",
string: { name: "varchar" },
# ...
json: { name: "json" },
}.freeze
変更後:
NATIVE_DATABASE_TYPES = { # rubocop:disable Style/MutableConstant
primary_key: "integer PRIMARY KEY AUTOINCREMENT NOT NULL",
string: { name: "varchar" },
# ...
json: { name: "json" },
}
あわせて sqlite3_adapter_test.rb にテストが2件追加されました。test_native_database_types_is_mutable はハッシュがfreezeされていないことを直接検証し、test_native_database_types_allows_custom_type は :vector キーへの書き込みと読み取りが正常に行えることを確認します。後者のテストは ensure ブロックで replace を使って元の状態に戻し、テスト間の副作用を防いでいます。
設計判断
rubocop:disable Style/MutableConstant コメントを付与する方式 が採用されており、「意図的にmutableにしている」ことをコード上で明示しています。単にcop違反を黙認するのではなく、拡張ポイントであることの意図をコードレビュアーや将来のメンテナーに伝える役割を果たしています。
このパターンはMySQL・PostgreSQLアダプターとも共通しており、各アダプターで一貫した方針が取られています。Ractor安全性のためにfreezeを推進しつつ、エコシステムの互換性を維持するための意図的な例外として NATIVE_DATABASE_TYPES を扱う、というトレードオフの判断です。
まとめ
Ractor対応を目的とした一括freeze適用が意図せず拡張ポイントを破壊したことに対し、アダプターごとに rubocop:disable で明示的な例外を設けることで対処したケースです。「定数のimmutabilityを高める」という方向性を維持しながら、エコシステムが依存してきた拡張インターフェースを守る判断が、テストの追加とあわせて記録されました。