Skip to content

Commit

Permalink
Merge pull request #51078 from saleh-alhaddad/support_join_types_in_w…
Browse files Browse the repository at this point in the history
…here_associated

Fix override existing join types in the query in the `where.associated` method
  • Loading branch information
rafaelfranca committed Feb 21, 2024
2 parents 857f2e4 + 3400aac commit 3e42d79
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 1 deletion.
16 changes: 16 additions & 0 deletions activerecord/CHANGELOG.md
Expand Up @@ -162,6 +162,22 @@

*Jason Nochlin*

* The fix ensures that the association is joined using the appropriate join type
(either inner join or left outer join) based on the existing joins in the scope.

This prevents unintentional overrides of existing join types and ensures consistency in the generated SQL queries.

Example:



```ruby
# `associated` will use `LEFT JOIN` instead of using `JOIN`
Post.left_joins(:author).where.associated(:author)
```

*Saleh Alhaddad*

* Fix an issue where `ActiveRecord::Encryption` configurations are not ready before the loading
of Active Record models, when an application is eager loaded. As a result, encrypted attributes
could be misconfigured in some cases.
Expand Down
18 changes: 17 additions & 1 deletion activerecord/lib/active_record/relation/query_methods.rb
Expand Up @@ -72,10 +72,26 @@ def not(opts, *rest)
# # INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
# # INNER JOIN "comments" ON "comments"."post_id" = "posts"."id"
# # WHERE "authors"."id" IS NOT NULL AND "comments"."id" IS NOT NULL
#
# You can define join type in the scope and +associated+ will not use `JOIN` by default.
#
# Post.left_joins(:author).where.associated(:author)
# # SELECT "posts".* FROM "posts"
# # LEFT OUTER JOIN "authors" "authors"."id" = "posts"."author_id"
# # WHERE "authors"."id" IS NOT NULL
#
# Post.left_joins(:comments).where.associated(:author)
# # SELECT "posts".* FROM "posts"
# # INNER JOIN "authors" ON "authors"."id" = "posts"."author_id"
# # LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id"
# # WHERE "author"."id" IS NOT NULL
def associated(*associations)
associations.each do |association|
reflection = scope_association_reflection(association)
@scope.joins!(association)
unless @scope.joins_values.include?(reflection.name) || @scope.left_outer_joins_values.include?(reflection.name)
@scope.joins!(association)
end

if reflection.options[:class_name]
self.not(association => { reflection.association_primary_key => nil })
else
Expand Down
21 changes: 21 additions & 0 deletions activerecord/test/cases/relation/where_chain_test.rb
Expand Up @@ -92,6 +92,27 @@ def test_associated_with_enum_extended_late
assert_equal Author.find(2), Author.order(id: :desc).joins(:reading_listing).where.associated(:reading_listing).extending(Author::NamedExtension).first
end

def test_associated_with_add_joins_before
Comment.joins(:children).where.associated(:children).tap do |relation|
assert_includes relation, comments(:greetings)
assert_not_includes relation, comments(:more_greetings)
end
end

def test_associated_with_add_left_joins_before
Comment.left_joins(:children).where.associated(:children).tap do |relation|
assert_includes relation, comments(:greetings)
assert_not_includes relation, comments(:more_greetings)
end
end

def test_associated_with_add_left_outer_joins_before
Comment.left_outer_joins(:children).where.associated(:children).tap do |relation|
assert_includes relation, comments(:greetings)
assert_not_includes relation, comments(:more_greetings)
end
end

def test_missing_with_association
assert_predicate posts(:authorless).author, :blank?
assert_equal [posts(:authorless)], Post.where.missing(:author).to_a
Expand Down

0 comments on commit 3e42d79

Please sign in to comment.