From 77360db5c42f93817b3907eca674e4f1adc4b0ee Mon Sep 17 00:00:00 2001 From: saleh-alhaddad Date: Sat, 17 Feb 2024 13:41:23 +0300 Subject: [PATCH] Fix an issue in the `where.associated` method --- activerecord/CHANGELOG.md | 12 +++++++++++ .../active_record/relation/query_methods.rb | 9 +++++++- .../test/cases/relation/where_chain_test.rb | 21 +++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 9f8beb5aad5c..2c3ba1478491 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -3,6 +3,18 @@ *Jason Nochlin* +* Fix an issue in the `where.associated` method only allows filtering associated records with an `inner join`, + which can potentially override existing join types in the query. + 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: + asoiciated will use left_joins alternative use deafult joins + ```ruby + 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. diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index b9dc7392323c..649d187809a9 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -72,10 +72,17 @@ 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 + # + # Additionally, you can define joine type in the scope and associated will not use default that is joins + # Example: Post.left_joins(:author).where.associated(:author) + # 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 diff --git a/activerecord/test/cases/relation/where_chain_test.rb b/activerecord/test/cases/relation/where_chain_test.rb index db0860f37831..e315fb41013e 100644 --- a/activerecord/test/cases/relation/where_chain_test.rb +++ b/activerecord/test/cases/relation/where_chain_test.rb @@ -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