diff options
Diffstat (limited to 'activerecord/lib')
-rw-r--r-- | activerecord/lib/active_record/associations/preloader/association.rb | 42 | ||||
-rw-r--r-- | activerecord/lib/active_record/associations/preloader/has_and_belongs_to_many.rb | 7 |
2 files changed, 31 insertions, 18 deletions
diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb index 0cc836f991..928da71eed 100644 --- a/activerecord/lib/active_record/associations/preloader/association.rb +++ b/activerecord/lib/active_record/associations/preloader/association.rb @@ -29,6 +29,10 @@ module ActiveRecord end def records_for(ids) + query_scope(ids) + end + + def query_scope(ids) scope.where(association_key.in(ids)) end @@ -52,12 +56,9 @@ module ActiveRecord raise NotImplementedError end - # We're converting to a string here because postgres will return the aliased association - # key in a habtm as a string (for whatever reason) def owners_by_key @owners_by_key ||= owners.group_by do |owner| - key = owner[owner_key_name] - key && key.to_s + owner[owner_key_name] end end @@ -71,27 +72,34 @@ module ActiveRecord owners_map = owners_by_key owner_keys = owners_map.keys.compact - if klass.nil? || owner_keys.empty? - records = [] - else + # Each record may have multiple owners, and vice-versa + records_by_owner = Hash[owners.map { |owner| [owner, []] }] + + if klass && owner_keys.any? # Some databases impose a limit on the number of ids in a list (in Oracle it's 1000) # Make several smaller queries if necessary or make one query if the adapter supports it sliced = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size) - records = sliced.flat_map { |slice| records_for(slice).to_a } + sliced.each { |slice| + records = records_for(slice) + caster = type_caster(records, association_key_name) + records.each do |record| + owner_key = caster.call record[association_key_name] + + owners_map[owner_key].each do |owner| + records_by_owner[owner] << record + end + end + } end - # Each record may have multiple owners, and vice-versa - records_by_owner = Hash[owners.map { |owner| [owner, []] }] - records.each do |record| - owner_key = record[association_key_name].to_s - - owners_map[owner_key].each do |owner| - records_by_owner[owner] << record - end - end records_by_owner end + IDENTITY = lambda { |value| value } + def type_caster(results, name) + IDENTITY + end + def reflection_scope @reflection_scope ||= reflection.scope ? klass.unscoped.instance_exec(nil, &reflection.scope) : klass.unscoped end diff --git a/activerecord/lib/active_record/associations/preloader/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/preloader/has_and_belongs_to_many.rb index 9a3fada380..c042a44b21 100644 --- a/activerecord/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +++ b/activerecord/lib/active_record/associations/preloader/has_and_belongs_to_many.rb @@ -12,7 +12,7 @@ module ActiveRecord # Unlike the other associations, we want to get a raw array of rows so that we can # access the aliased column on the join table def records_for(ids) - scope = super + scope = query_scope ids klass.connection.select_all(scope.arel, 'SQL', scope.bind_values) end @@ -40,6 +40,11 @@ module ActiveRecord end end + def type_caster(results, name) + caster = results.column_types.fetch(name, results.identity_type) + lambda { |value| caster.type_cast value } + end + def build_scope super.joins(join).select(join_select) end |