diff options
Diffstat (limited to 'activerecord/lib')
6 files changed, 44 insertions, 72 deletions
diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb index e2d4cf4378..6e08f67286 100644 --- a/activerecord/lib/active_record/associations/join_dependency.rb +++ b/activerecord/lib/active_record/associations/join_dependency.rb @@ -61,32 +61,26 @@ module ActiveRecord build tree, @join_root, Arel::InnerJoin end - def graft(*associations) - associations.reject { |join_node| - find_node join_node - }.each { |join_node| - parent = find_node(join_node.parent) || join_root - reflection = join_node.reflection - type = join_node.join_type - - next if parent.children.find { |j| j.reflection == reflection } - build_scalar reflection, parent, type - } - self - end - def reflections join_root.drop(1).map!(&:reflection) end - def join_relation(relation) - nodes = join_root.drop 1 - nodes.each { |n| n.join_type = Arel::OuterJoin } - relation.joins(nodes) + def merge_outer_joins!(other) + left = join_root + right = other.join_root + + if left.match? right + merge_node left, right + else + # If the roots aren't the same, then deep copy the RHS to the LHS + left.children.concat right.children.map { |node| + deep_copy left, node + } + end end def join_constraints - join_root.flat_map(&:join_constraints) + join_root.children.flat_map { |c| c.flat_map(&:join_constraints) } end def columns @@ -103,39 +97,34 @@ module ActiveRecord parents = {} type_caster = result_set.column_type primary_key - assoc = join_root.children records = result_set.map { |row_hash| primary_id = type_caster.type_cast row_hash[primary_key] parent = parents[primary_id] ||= join_root.instantiate(row_hash) - construct(parent, assoc, row_hash, result_set) + construct(parent, join_root, row_hash, result_set) parent }.uniq - remove_duplicate_results!(base_klass, records, assoc) + remove_duplicate_results!(base_klass, records, join_root.children) records end private - def find_node(target_node) - stack = target_node.parents << target_node - - left = [join_root] - right = stack.shift + def merge_node(left, right) + intersection, missing = right.children.map { |node1| + [left.children.find { |node2| node1.match? node2 }, node1] + }.partition(&:first) - loop { - match = left.find { |l| l.match? right } + intersection.each { |l,r| merge_node l, r } - if match - return match if stack.empty? + left.children.concat missing.map { |_,node| deep_copy left, node } + end - left = match.children - right = stack.shift - else - return nil - end - } + def deep_copy(parent, node) + dup = build_join_association(node.reflection, parent, Arel::OuterJoin) + dup.children.concat node.children.map { |n| deep_copy dup, n } + dup end def remove_duplicate_results!(base, records, associations) @@ -195,16 +184,16 @@ module ActiveRecord JoinAssociation.new(reflection, join_root.to_a.length, parent, join_type, alias_tracker) end - def construct(parent, nodes, row, rs) - nodes.sort_by { |k| k.name }.each do |node| - association = construct_association(parent, node, row, rs) - construct(association, node.children, row, rs) if association + def construct(ar_parent, parent, row, rs) + parent.children.each do |node| + association = construct_association(ar_parent, parent, node, row, rs) + construct(association, node, row, rs) if association end end - def construct_association(record, join_part, row, rs) - caster = rs.column_type(join_part.parent.aliased_primary_key) - row_id = caster.type_cast row[join_part.parent.aliased_primary_key] + def construct_association(record, parent, join_part, row, rs) + caster = rs.column_type(parent.aliased_primary_key) + row_id = caster.type_cast row[parent.aliased_primary_key] return if record.id != row_id diff --git a/activerecord/lib/active_record/associations/join_dependency/join_part.rb b/activerecord/lib/active_record/associations/join_dependency/join_part.rb index d536eaf613..d39ce94c99 100644 --- a/activerecord/lib/active_record/associations/join_dependency/join_part.rb +++ b/activerecord/lib/active_record/associations/join_dependency/join_part.rb @@ -29,9 +29,6 @@ module ActiveRecord @children = [] end - def join_constraints; []; end - def join_relation(rel); rel; end - def name reflection.name end @@ -40,25 +37,9 @@ module ActiveRecord self.class == other.class end - def parents - parents = [] - node = parent - while node - parents.unshift node - node = node.parent - end - parents - end - - def each + def each(&block) yield self - iter = lambda { |list| - list.each { |item| - yield item - iter.call item.children - } - } - iter.call children + children.each { |child| child.each(&block) } end def aliased_table @@ -90,7 +71,7 @@ module ActiveRecord unless @column_names_with_alias @column_names_with_alias = [] - ([primary_key] + (column_names - [primary_key])).compact.each_with_index do |column_name, i| + column_names.each_with_index do |column_name, i| @column_names_with_alias << [column_name, "#{aliased_prefix}_r#{i}"] 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 ea21836c65..3166df57eb 100644 --- a/activerecord/lib/active_record/associations/preloader/through_association.rb +++ b/activerecord/lib/active_record/associations/preloader/through_association.rb @@ -15,7 +15,7 @@ module ActiveRecord through_reflection.name, through_scope) - through_records = owners.map do |owner, h| + through_records = owners.map do |owner| association = owner.association through_reflection.name [owner, Array(association.reader)] diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 0132a02f83..8583286de5 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -261,13 +261,13 @@ module ActiveRecord end def construct_relation_for_association_find(join_dependency) - relation = except(:select).select(join_dependency.columns) + relation = except(:select).select(join_dependency.columns + select_values) apply_join_dependency(relation, join_dependency) end def apply_join_dependency(relation, join_dependency) relation = relation.except(:includes, :eager_load, :preload) - relation = join_dependency.join_relation(relation) + relation = relation.joins join_dependency if using_limitable_reflections?(join_dependency.reflections) relation diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb index c05632e688..182b9ed89c 100644 --- a/activerecord/lib/active_record/relation/merger.rb +++ b/activerecord/lib/active_record/relation/merger.rb @@ -94,7 +94,7 @@ module ActiveRecord []) relation.joins! rest - @relation = join_dependency.join_relation(relation) + @relation = relation.joins join_dependency end end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index e1efc6a189..9c9690215a 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -928,7 +928,7 @@ module ActiveRecord :string_join when Hash, Symbol, Array :association_join - when ActiveRecord::Associations::JoinDependency::JoinAssociation + when ActiveRecord::Associations::JoinDependency :stashed_join when Arel::Nodes::Join :join_node @@ -950,7 +950,9 @@ module ActiveRecord join_list ) - join_dependency.graft(*stashed_association_joins) + stashed_association_joins.each do |dep| + join_dependency.merge_outer_joins! dep + end joins = join_dependency.join_constraints |