aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib')
-rw-r--r--activerecord/lib/active_record/associations/join_dependency.rb77
-rw-r--r--activerecord/lib/active_record/associations/join_dependency/join_part.rb25
-rw-r--r--activerecord/lib/active_record/associations/preloader/through_association.rb2
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb4
-rw-r--r--activerecord/lib/active_record/relation/merger.rb2
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb6
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