diff options
6 files changed, 29 insertions, 17 deletions
diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb index b76005b587..f35a40fb2f 100644 --- a/activerecord/lib/active_record/associations/join_dependency.rb +++ b/activerecord/lib/active_record/associations/join_dependency.rb @@ -64,16 +64,17 @@ module ActiveRecord end end - def initialize(base, table, associations) + def initialize(base, table, associations, join_type) tree = self.class.make_tree associations @join_root = JoinBase.new(base, table, build(tree, base)) + @join_type = join_type end def reflections join_root.drop(1).map!(&:reflection) end - def join_constraints(joins_to_add, join_type, alias_tracker) + def join_constraints(joins_to_add, alias_tracker) @alias_tracker = alias_tracker construct_tables!(join_root) @@ -82,9 +83,9 @@ module ActiveRecord joins.concat joins_to_add.flat_map { |oj| construct_tables!(oj.join_root) if join_root.match? oj.join_root - walk join_root, oj.join_root + walk(join_root, oj.join_root, oj.join_type) else - make_join_constraints(oj.join_root, join_type) + make_join_constraints(oj.join_root, oj.join_type) end } end @@ -125,7 +126,7 @@ module ActiveRecord end protected - attr_reader :join_root + attr_reader :join_root, :join_type private attr_reader :alias_tracker @@ -151,7 +152,7 @@ module ActiveRecord end end - def make_constraints(parent, child, join_type = Arel::Nodes::OuterJoin) + def make_constraints(parent, child, join_type) foreign_table = parent.table foreign_klass = parent.base_klass joins = child.join_constraints(foreign_table, foreign_klass, join_type, alias_tracker) @@ -173,13 +174,13 @@ module ActiveRecord join ? "#{name}_join" : name end - def walk(left, right) + def walk(left, right, join_type) intersection, missing = right.children.map { |node1| [left.children.find { |node2| node1.match? node2 }, node1] }.partition(&:first) - joins = intersection.flat_map { |l, r| r.table = l.table; walk(l, r) } - joins.concat missing.flat_map { |_, n| make_constraints(left, n) } + joins = intersection.flat_map { |l, r| r.table = l.table; walk(l, r, join_type) } + joins.concat missing.flat_map { |_, n| make_constraints(left, n, join_type) } end def find_reflection(klass, name) diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 9450e4d3c5..9c7ac80447 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -371,7 +371,9 @@ module ActiveRecord end def apply_join_dependency(eager_loading: group_values.empty?) - join_dependency = construct_join_dependency(eager_load_values + includes_values) + join_dependency = construct_join_dependency( + eager_load_values + includes_values, Arel::Nodes::OuterJoin + ) relation = except(:includes, :eager_load, :preload).joins!(join_dependency) if eager_loading && !using_limitable_reflections?(join_dependency.reflections) diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb index 6bb77b355c..84fe424ef0 100644 --- a/activerecord/lib/active_record/relation/merger.rb +++ b/activerecord/lib/active_record/relation/merger.rb @@ -123,7 +123,9 @@ module ActiveRecord end end - join_dependency = other.construct_join_dependency(associations) + join_dependency = other.construct_join_dependency( + associations, Arel::Nodes::InnerJoin + ) relation.joins!(join_dependency, *others) end end @@ -135,7 +137,9 @@ module ActiveRecord relation.left_outer_joins!(*other.left_outer_joins_values) else associations = other.left_outer_joins_values - join_dependency = other.construct_join_dependency(associations) + join_dependency = other.construct_join_dependency( + associations, Arel::Nodes::OuterJoin + ) 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 c03ca7f1e7..b8fd2fce14 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -1005,9 +1005,9 @@ module ActiveRecord @arel ||= build_arel(aliases) end - def construct_join_dependency(associations) # :nodoc: + def construct_join_dependency(associations, join_type) # :nodoc: ActiveRecord::Associations::JoinDependency.new( - klass, table, associations + klass, table, associations, join_type ) end @@ -1102,7 +1102,7 @@ module ActiveRecord def build_joins(manager, joins, aliases) unless left_outer_joins_values.empty? left_joins = valid_association_list(left_outer_joins_values.flatten) - joins << construct_join_dependency(left_joins) + joins.unshift construct_join_dependency(left_joins, Arel::Nodes::OuterJoin) end buckets = joins.group_by do |join| @@ -1134,9 +1134,9 @@ module ActiveRecord join_list = join_nodes + convert_join_strings_to_ast(string_joins) alias_tracker = alias_tracker(join_list, aliases) - join_dependency = construct_join_dependency(association_joins) + join_dependency = construct_join_dependency(association_joins, join_type) - joins = join_dependency.join_constraints(stashed_joins, join_type, alias_tracker) + joins = join_dependency.join_constraints(stashed_joins, alias_tracker) joins.each { |join| manager.from(join) } manager.join_sources.concat(join_list) diff --git a/activerecord/test/cases/associations/left_outer_join_association_test.rb b/activerecord/test/cases/associations/left_outer_join_association_test.rb index 0a8863c35d..d44c6407f5 100644 --- a/activerecord/test/cases/associations/left_outer_join_association_test.rb +++ b/activerecord/test/cases/associations/left_outer_join_association_test.rb @@ -32,6 +32,10 @@ class LeftOuterJoinAssociationTest < ActiveRecord::TestCase assert_equal 17, Post.left_outer_joins(:comments).count end + def test_merging_left_joins_should_be_left_joins + assert_equal 5, Author.left_joins(:posts).merge(Post.no_comments).count + end + def test_left_joins_aliases_left_outer_joins assert_equal Post.left_outer_joins(:comments).to_sql, Post.left_joins(:comments).to_sql end diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb index 090b576a5d..50c0dddcf2 100644 --- a/activerecord/test/models/post.rb +++ b/activerecord/test/models/post.rb @@ -43,6 +43,7 @@ class Post < ActiveRecord::Base has_one :first_comment, -> { order("id ASC") }, class_name: "Comment" has_one :last_comment, -> { order("id desc") }, class_name: "Comment" + scope :no_comments, -> { left_joins(:comments).where(comments: { id: nil }) } scope :with_special_comments, -> { joins(:comments).where(comments: { type: "SpecialComment" }) } scope :with_very_special_comments, -> { joins(:comments).where(comments: { type: "VerySpecialComment" }) } scope :with_post, ->(post_id) { joins(:comments).where(comments: { post_id: post_id }) } |