aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/associations/preloader
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/associations/preloader')
-rw-r--r--activerecord/lib/active_record/associations/preloader/association.rb42
-rw-r--r--activerecord/lib/active_record/associations/preloader/has_and_belongs_to_many.rb7
-rw-r--r--activerecord/lib/active_record/associations/preloader/through_association.rb17
3 files changed, 39 insertions, 27 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
diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb
index de06931845..2c625cec04 100644
--- a/activerecord/lib/active_record/associations/preloader/through_association.rb
+++ b/activerecord/lib/active_record/associations/preloader/through_association.rb
@@ -27,17 +27,16 @@ module ActiveRecord
def through_records_by_owner
Preloader.new(owners, through_reflection.name, through_scope).run
- Hash[owners.map do |owner|
- through_records = Array.wrap(owner.send(through_reflection.name))
+ should_reset = (through_scope != through_reflection.klass.unscoped) ||
+ (reflection.options[:source_type] && through_reflection.collection?)
- # Dont cache the association - we would only be caching a subset
- if (through_scope != through_reflection.klass.unscoped) ||
- (reflection.options[:source_type] && through_reflection.collection?)
- owner.association(through_reflection.name).reset
- end
+ owners.each_with_object({}) do |owner, h|
+ association = owner.association through_reflection.name
+ h[owner] = Array(association.reader)
- [owner, through_records]
- end]
+ # Dont cache the association - we would only be caching a subset
+ association.reset if should_reset
+ end
end
def through_scope