Fix through association with source/through scope which has joins

If source/through scope references other tables in where/order, we
should explicitly maintain joins in the scope, otherwise association
queries will fail with referenced unknown column.

Fixes #33525.
This commit is contained in:
Ryuta Kamizono 2020-05-21 23:58:19 +09:00
parent e4b6c719cd
commit 6a617cc61e
7 changed files with 30 additions and 2 deletions

View File

@ -134,6 +134,12 @@ module ActiveRecord
if scope_chain_item == chain_head.scope
scope.merge! item.except(:where, :includes, :unscope, :order)
elsif !item.references_values.empty?
join_dependency = item.construct_join_dependency(
item.eager_load_values | item.includes_values, Arel::Nodes::OuterJoin
)
scope.joins!(*item.joins_values, join_dependency)
scope.left_outer_joins!(*item.left_outer_joins_values)
end
reflection.all_includes do

View File

@ -33,6 +33,13 @@ module ActiveRecord
join_scope = reflection.join_scope(table, foreign_table, foreign_klass)
unless join_scope.references_values.empty?
join_dependency = join_scope.construct_join_dependency(
join_scope.eager_load_values | join_scope.includes_values, Arel::Nodes::OuterJoin
)
join_scope.joins!(join_dependency)
end
arel = join_scope.arel(alias_tracker.aliases)
nodes = arel.constraints.first

View File

@ -32,7 +32,7 @@ module ActiveRecord
reflection.chain.drop(1).each do |reflection|
relation = reflection.klass.scope_for_association
scope.merge!(
relation.except(:select, :create_with, :includes, :preload, :joins, :eager_load)
relation.except(:select, :create_with, :includes, :preload, :eager_load, :joins, :left_outer_joins)
)
end
scope

View File

@ -380,7 +380,7 @@ module ActiveRecord
def apply_join_dependency(eager_loading: group_values.empty?)
join_dependency = construct_join_dependency(
eager_load_values + includes_values, Arel::Nodes::OuterJoin
eager_load_values | includes_values, Arel::Nodes::OuterJoin
)
relation = except(:includes, :eager_load, :preload).joins!(join_dependency)

View File

@ -1057,10 +1057,18 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_with_source_scope
expected = [readers(:michael_welcome).becomes(LazyReader)]
assert_equal expected, Author.first.lazy_readers_skimmers_or_not
assert_equal expected, Author.preload(:lazy_readers_skimmers_or_not).first.lazy_readers_skimmers_or_not
assert_equal expected, Author.eager_load(:lazy_readers_skimmers_or_not).first.lazy_readers_skimmers_or_not
end
def test_has_many_through_with_join_scope
expected = [readers(:bob_welcome).becomes(LazyReader)]
assert_equal expected, Author.last.lazy_readers_skimmers_or_not_2
assert_equal expected, Author.preload(:lazy_readers_skimmers_or_not_2).last.lazy_readers_skimmers_or_not_2
assert_equal expected, Author.eager_load(:lazy_readers_skimmers_or_not_2).last.lazy_readers_skimmers_or_not_2
end
def test_has_many_through_polymorphic_with_rewhere
post = TaggedPost.create!(title: "Tagged", body: "Post")
tag = post.tags.create!(name: "Tag")

View File

@ -9,3 +9,9 @@ michael_authorless:
post_id: 3
person_id: 1
first_post_id: 3
bob_welcome:
id: 3
post_id: 8
person_id: 4
first_post_id: 10

View File

@ -177,6 +177,7 @@ class Author < ActiveRecord::Base
has_many :topics, primary_key: "name", foreign_key: "author_name"
has_many :lazy_readers_skimmers_or_not, through: :posts
has_many :lazy_readers_skimmers_or_not_2, through: :posts_with_no_comments, source: :lazy_readers_skimmers_or_not
attr_accessor :post_log
after_initialize :set_post_log