aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/associations
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/associations')
-rw-r--r--activerecord/lib/active_record/associations/preloader.rb2
-rw-r--r--activerecord/lib/active_record/associations/preloader/association.rb24
-rw-r--r--activerecord/lib/active_record/associations/preloader/has_and_belongs_to_many.rb4
-rw-r--r--activerecord/lib/active_record/associations/preloader/has_many_through.rb4
-rw-r--r--activerecord/lib/active_record/associations/preloader/through_association.rb27
5 files changed, 53 insertions, 8 deletions
diff --git a/activerecord/lib/active_record/associations/preloader.rb b/activerecord/lib/active_record/associations/preloader.rb
index 4ef361fccf..3add378386 100644
--- a/activerecord/lib/active_record/associations/preloader.rb
+++ b/activerecord/lib/active_record/associations/preloader.rb
@@ -127,7 +127,7 @@ module ActiveRecord
loaders = preloaders_for_one parent, records
- recs = loaders.flat_map(&:target_records).uniq
+ recs = loaders.flat_map(&:preloaded_records).uniq
loaders.concat Array.wrap(child).flat_map { |assoc|
preloaders_on assoc, recs
}
diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb
index ef48e9f174..6c60d03998 100644
--- a/activerecord/lib/active_record/associations/preloader/association.rb
+++ b/activerecord/lib/active_record/associations/preloader/association.rb
@@ -17,6 +17,7 @@ module ActiveRecord
@owners_by_key = nil
@type_caster = IDENTITY_CASTER
@associated_records_by_owner = nil
+ @loaded = false
end
def run
@@ -71,20 +72,36 @@ module ActiveRecord
reflection.options
end
- def target_records
+ def preloaded_records1
associated_records_by_owner.values.flatten
end
+ def preloaded_records
+ if owners.first.association(reflection.name).loaded?
+ []
+ else
+ associated_records_by_owner.values.flatten
+ end
+ end
+
+ def loaded?
+ @loaded
+ end
+
private
def associated_records_by_owner
+ @loaded = true
+
return @associated_records_by_owner if @associated_records_by_owner
owners_map = owners_by_key
owner_keys = owners_map.keys.compact
# Each record may have multiple owners, and vice-versa
- records_by_owner = Hash[owners.map { |owner| [owner, []] }]
+ records_by_owner = Hash.new do |h,owner|
+ h[owner] = []
+ end
if klass && owner_keys.any?
# Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
@@ -104,6 +121,9 @@ module ActiveRecord
end
end
end
+ owners.each_with_object(records_by_owner) do |owner,h|
+ h[owner] ||= []
+ end
@associated_records_by_owner = records_by_owner
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 48ae99819b..fce3cb78e0 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
@@ -35,10 +35,10 @@ module ActiveRecord
# actual records, ensuring that we don't create more than one instances of the same
# record
def associated_records_by_owner
- return @records_by_owner if @records_by_owner
+ return @associated_records_by_owner if @associated_records_by_owner
records = {}
- @records_by_owner = super.each_value do |rows|
+ @associated_records_by_owner = super.each_value do |rows|
rows.map! { |row| records[row[klass.primary_key]] ||= klass.instantiate(row) }
end
end
diff --git a/activerecord/lib/active_record/associations/preloader/has_many_through.rb b/activerecord/lib/active_record/associations/preloader/has_many_through.rb
index 157b627ad5..39a938c266 100644
--- a/activerecord/lib/active_record/associations/preloader/has_many_through.rb
+++ b/activerecord/lib/active_record/associations/preloader/has_many_through.rb
@@ -5,13 +5,15 @@ module ActiveRecord
include ThroughAssociation
def associated_records_by_owner
+ return @associated_records_by_owner if @associated_records_by_owner
+
records_by_owner = super
if reflection_scope.distinct_value
records_by_owner.each_value { |records| records.uniq! }
end
- records_by_owner
+ @associated_records_by_owner = records_by_owner
end
end
end
diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb
index eaceb6c3d9..a62815830c 100644
--- a/activerecord/lib/active_record/associations/preloader/through_association.rb
+++ b/activerecord/lib/active_record/associations/preloader/through_association.rb
@@ -12,6 +12,10 @@ module ActiveRecord
end
def associated_records_by_owner
+ @loaded = true
+
+ return @associated_records_by_owner if @associated_records_by_owner
+
left_loader = Preloader.new(owners,
through_reflection.name,
through_scope)
@@ -36,12 +40,31 @@ module ActiveRecord
preloader = Preloader.new(middle_records,
source_reflection.name,
reflection_scope)
+
preloader.run
- through_records.each_with_object({}) { |(lhs,middles,assoc),h|
- h[lhs] = middles.flat_map { |r|
+ middle_to_pl = preloader.preloaders.each_with_object({}) do |pl,h|
+ pl.owners.each { |middle|
+ h[middle] = pl
+ }
+ end
+
+ @associated_records_by_owner = through_records.each_with_object({}) { |(lhs,middles,assoc),h|
+ x = middle_to_pl[middles.first]
+
+ rhs_records = middles.flat_map { |r|
r.send(source_reflection.name)
}.compact
+
+ if x && x.loaded?
+ rs = rhs_records.sort_by { |rhs|
+ x.preloaded_records1.index(rhs)
+ }
+ else
+ rs = rhs_records
+ end
+
+ h[lhs] = rs
}
end