Skip to content
Browse files

Merge branch 'typecast'

* typecast:
  correctly typecast keys, remove conditionals, reduce object allocations
  no need to to_a the scope
  remove extra flat_map array
  avoid extra empty array allocation
  extract owner id calculation to a method
  only call to_a when we have to
  • Loading branch information...
2 parents 60209e8 + ef724e4 commit fb321017b6eab5c3f0c0c3187585674412ec53ae @tenderlove tenderlove committed Aug 28, 2013
View
42 activerecord/lib/active_record/associations/preloader/association.rb
@@ -29,6 +29,10 @@ def scope
end
def records_for(ids)
+ query_scope(ids)
+ end
+
+ def query_scope(ids)
scope.where(association_key.in(ids))
end
@@ -52,12 +56,9 @@ def owner_key_name
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 @@ def associated_records_by_owner
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
View
7 activerecord/lib/active_record/associations/preloader/has_and_belongs_to_many.rb
@@ -12,7 +12,7 @@ def initialize(klass, records, reflection, preload_options)
# 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 @@ def associated_records_by_owner
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

0 comments on commit fb32101

Please sign in to comment.
Something went wrong with that request. Please try again.