Skip to content

Commit 3400aac

Browse files
saleh-alhaddadrafaelfranca
authored andcommitted
Fix an issue in the where.associated method
1 parent 9b343c2 commit 3400aac

File tree

3 files changed

+54
-1
lines changed

3 files changed

+54
-1
lines changed

activerecord/CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,22 @@
33

44
*Jason Nochlin*
55

6+
* The fix ensures that the association is joined using the appropriate join type
7+
(either inner join or left outer join) based on the existing joins in the scope.
8+
9+
This prevents unintentional overrides of existing join types and ensures consistency in the generated SQL queries.
10+
11+
Example:
12+
13+
14+
15+
```ruby
16+
# `associated` will use `LEFT JOIN` instead of using `JOIN`
17+
Post.left_joins(:author).where.associated(:author)
18+
```
19+
20+
*Saleh Alhaddad*
21+
622
* Fix an issue where `ActiveRecord::Encryption` configurations are not ready before the loading
723
of Active Record models, when an application is eager loaded. As a result, encrypted attributes
824
could be misconfigured in some cases.

activerecord/lib/active_record/relation/query_methods.rb

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,26 @@ def not(opts, *rest)
7272
# # INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
7373
# # INNER JOIN "comments" ON "comments"."post_id" = "posts"."id"
7474
# # WHERE "authors"."id" IS NOT NULL AND "comments"."id" IS NOT NULL
75+
#
76+
# You can define join type in the scope and +associated+ will not use `JOIN` by default.
77+
#
78+
# Post.left_joins(:author).where.associated(:author)
79+
# # SELECT "posts".* FROM "posts"
80+
# # LEFT OUTER JOIN "authors" "authors"."id" = "posts"."author_id"
81+
# # WHERE "authors"."id" IS NOT NULL
82+
#
83+
# Post.left_joins(:comments).where.associated(:author)
84+
# # SELECT "posts".* FROM "posts"
85+
# # INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
86+
# # LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id"
87+
# # WHERE "author"."id" IS NOT NULL
7588
def associated(*associations)
7689
associations.each do |association|
7790
reflection = scope_association_reflection(association)
78-
@scope.joins!(association)
91+
unless @scope.joins_values.include?(reflection.name) || @scope.left_outer_joins_values.include?(reflection.name)
92+
@scope.joins!(association)
93+
end
94+
7995
if reflection.options[:class_name]
8096
self.not(association => { reflection.association_primary_key => nil })
8197
else

activerecord/test/cases/relation/where_chain_test.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,27 @@ def test_associated_with_enum_extended_late
9292
assert_equal Author.find(2), Author.order(id: :desc).joins(:reading_listing).where.associated(:reading_listing).extending(Author::NamedExtension).first
9393
end
9494

95+
def test_associated_with_add_joins_before
96+
Comment.joins(:children).where.associated(:children).tap do |relation|
97+
assert_includes relation, comments(:greetings)
98+
assert_not_includes relation, comments(:more_greetings)
99+
end
100+
end
101+
102+
def test_associated_with_add_left_joins_before
103+
Comment.left_joins(:children).where.associated(:children).tap do |relation|
104+
assert_includes relation, comments(:greetings)
105+
assert_not_includes relation, comments(:more_greetings)
106+
end
107+
end
108+
109+
def test_associated_with_add_left_outer_joins_before
110+
Comment.left_outer_joins(:children).where.associated(:children).tap do |relation|
111+
assert_includes relation, comments(:greetings)
112+
assert_not_includes relation, comments(:more_greetings)
113+
end
114+
end
115+
95116
def test_missing_with_association
96117
assert_predicate posts(:authorless).author, :blank?
97118
assert_equal [posts(:authorless)], Post.where.missing(:author).to_a

0 commit comments

Comments
 (0)