ThroughReflectionにおける複合主キーの処理を修正
ThroughReflection#association_primary_key が複合主キーを正しく処理できていなかった問題が解決されました。これにより、has_many through関連付けで複合主キーを使用する際の _ids リーダーメソッドが正常に動作するようになります。
背景
ThroughReflection#association_primary_key メソッドは、ソース関連付けに primary_key: [:col1, :col2] のような複合主キーが指定されている場合、配列全体に対して .to_s を呼び出していました。この処理により、期待される ["col1", "col2"] の配列ではなく、"[:col1, :col2]" という文字列が生成されていました。
この不具合は、自動生成される _ids リーダーメソッドを使用する際にエラーを引き起こしていました。具体的には、以下のような ActiveRecord::UnknownAttributeReference 例外が発生していました:
ActiveRecord::UnknownAttributeReference: Dangerous query method called with non-attribute argument(s): "[:col1, :col2]"
一方、BelongsToReflection#association_primary_key は既に複合主キーを正しく処理する実装になっており、この挙動の不一致が問題となっていました。
技術的な変更
activerecord/lib/active_record/reflection.rb の ThroughReflection#association_primary_key メソッドが、BelongsToReflection の実装と同様の処理に修正されました。
変更前:
if primary_key = actual_source_reflection.options[:primary_key]
@association_primary_key ||= -primary_key.to_s
else
primary_key(klass || self.klass)
end
変更後:
if primary_key = actual_source_reflection.options[:primary_key]
@association_primary_key ||= if primary_key.is_a?(Array)
primary_key.map { |pk| pk.to_s.freeze }.freeze
else
-primary_key.to_s
end
else
primary_key(klass || self.klass)
end
primary_key が配列の場合の処理 が追加され、各要素を文字列に変換して凍結した配列を返すようになりました。単一キーの場合は、従来通り単項マイナス演算子による文字列の重複排除最適化が適用されます。
テストコードでは、Sharded::Blog モデルに comments_via_posts 関連付けが追加され、複合主キーを持つ through 関連付けのシナリオが検証されています:
has_many :comments_with_composite_pk,
class_name: "Sharded::Comment",
primary_key: [:blog_id, :id],
foreign_key: [:blog_id, :blog_post_id]
この関連付けを経由する blog.comments_via_post_ids の呼び出しが、期待通り [[comment.blog_id, comment.id]] 形式の配列を返すことが確認されています。
設計判断
BelongsToReflection との実装の統一 が優先されました。
BelongsToReflection#association_primary_key は既に primary_key.is_a?(Array) による分岐を持っており、本PRはその実装を ThroughReflection にも適用する形になっています。配列の各要素に対する .freeze 呼び出しも既存実装と同様で、メモリ効率の向上とイミュータブル性の保証を両立しています。
単一キーの場合に使用される単項マイナス演算子 -primary_key.to_s は、Rubyの文字列重複排除機能を活用した最適化です。この既存の最適化手法を維持しつつ、複合キーのケースにのみ新しい分岐を追加することで、パフォーマンスへの影響を最小限に抑えています。
まとめ
本PRは、ThroughReflection における複合主キーの処理を BelongsToReflection と同じロジックに統一した修正です。配列判定と要素ごとの文字列変換を追加することで、has_many through 関連付けでの複合主キー使用時のエラーを解消し、_ids リーダーメソッドの正常動作を実現しています。