From 13f1401a6cf0266a3b0a91b173f976db2d4e50f3 Mon Sep 17 00:00:00 2001 From: Benedikt Deicke Date: Tue, 3 Apr 2012 16:51:36 +0200 Subject: Removes caching from ActiveRecord::Core::ClassMethods#relation The #relation method gets called in four places and the return value was instantly cloned in three of them. The only place that did not clone was ActiveRecord::Scoping::Default::ClassMethods#unscoped. This introduced a bug described in #5667 and should really clone the relation, too. This means all four places would clone the relation, so it doesn't make a lot of sense caching it in the first place. The four places with calls to relations are: activerecord/lib/active_record/scoping/default.rb:110:in `block in build_default_scope'" activerecord/lib/active_record/scoping/default.rb:42:in `unscoped'" activerecord/lib/active_record/scoping/named.rb:38:in `scoped'" activerecord/lib/active_record/scoping/named.rb:52:in `scope_attributes'" Conflicts: activerecord/lib/active_record/core.rb --- activerecord/lib/active_record/scoping/named.rb | 4 ++-- activerecord/test/cases/relations_test.rb | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb index 9c50baa647..d6b0265fb3 100644 --- a/activerecord/lib/active_record/scoping/named.rb +++ b/activerecord/lib/active_record/scoping/named.rb @@ -34,7 +34,7 @@ module ActiveRecord if current_scope current_scope.clone else - scope = relation.clone + scope = relation scope.default_scoped = true scope end @@ -48,7 +48,7 @@ module ActiveRecord if current_scope current_scope.scope_for_create else - scope = relation.clone + scope = relation scope.default_scoped = true scope.scope_for_create end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index bf2807c384..4da9179bfb 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -1088,6 +1088,10 @@ class RelationTest < ActiveRecord::TestCase assert_equal 'honda', FastCar.unscoped { FastCar.order_using_old_style.limit(1).first.name} end + def test_unscoped_relation_clones + assert_not_equal CoolCar.unscoped.object_id, CoolCar.unscoped.object_id + end + def test_intersection_with_array relation = Author.where(:name => "David") rails_author = relation.first -- cgit v1.2.3 From 8491740ca5361ba9df20e1c8b906c709f5bfbc12 Mon Sep 17 00:00:00 2001 From: Benedikt Deicke Date: Tue, 3 Apr 2012 21:13:24 +0200 Subject: Adds test to check that circular preloading does not modify Model.unscoped (as described in #5667) Conflicts: activerecord/test/cases/associations/eager_test.rb --- activerecord/test/cases/associations/eager_test.rb | 24 ++++++++++++++++++++++ activerecord/test/cases/relations_test.rb | 4 ---- activerecord/test/models/comment.rb | 2 ++ 3 files changed, 26 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 1dc71ac4cc..4cc1378026 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -1095,4 +1095,28 @@ class EagerAssociationTest < ActiveRecord::TestCase Post.includes(:comments).order(nil).where(:comments => {:body => "Thank you for the welcome"}).first end end + + def test_deep_including_through_habtm + posts = Post.find(:all, :include => {:categories => :categorizations}, :order => "posts.id") + assert_no_queries { assert_equal 2, posts[0].categories[0].categorizations.length } + assert_no_queries { assert_equal 1, posts[0].categories[1].categorizations.length } + assert_no_queries { assert_equal 2, posts[1].categories[0].categorizations.length } + end + + test "scoping with a circular preload" do + assert_equal Comment.find(1), Comment.preload(:post => :comments).scoping { Comment.find(1) } + end + + test "circular preload does not modify unscoped" do + expected = FirstPost.unscoped.find(2) + FirstPost.preload(:comments => :first_post).find(1) + assert_equal expected, FirstPost.unscoped.find(2) + end + + test "preload ignores the scoping" do + assert_equal( + Comment.find(1).post, + Post.where('1 = 0').scoping { Comment.preload(:post).find(1).post } + ) + end end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 4da9179bfb..bf2807c384 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -1088,10 +1088,6 @@ class RelationTest < ActiveRecord::TestCase assert_equal 'honda', FastCar.unscoped { FastCar.order_using_old_style.limit(1).first.name} end - def test_unscoped_relation_clones - assert_not_equal CoolCar.unscoped.object_id, CoolCar.unscoped.object_id - end - def test_intersection_with_array relation = Author.where(:name => "David") rails_author = relation.first diff --git a/activerecord/test/models/comment.rb b/activerecord/test/models/comment.rb index 88b139d931..bca937a299 100644 --- a/activerecord/test/models/comment.rb +++ b/activerecord/test/models/comment.rb @@ -11,6 +11,8 @@ class Comment < ActiveRecord::Base belongs_to :post, :counter_cache => true has_many :ratings + belongs_to :first_post, :foreign_key => :post_id + has_many :children, :class_name => 'Comment', :foreign_key => :parent_id belongs_to :parent, :class_name => 'Comment', :counter_cache => :children_count -- cgit v1.2.3 From 2c21a2f7eb9f8cc50a3e957784105e187f63d5f3 Mon Sep 17 00:00:00 2001 From: Benedikt Deicke Date: Thu, 19 Apr 2012 10:04:24 +0200 Subject: Revert "Revert "Fix #5667. Preloading should ignore scoping."" This reverts commit 1166d49f62ccab789be208112163ad13183224e2. Conflicts: activerecord/test/cases/associations/eager_test.rb --- activerecord/CHANGELOG.md | 6 ++++++ .../lib/active_record/associations/preloader/association.rb | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 16c525d211..ffd74893be 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,9 @@ +## Rails 3.2.4 (unreleased) ## + +* Association preloading shouldn't be affected by the current scoping. + This could cause infinite recursion and potentially other problems. + See GH #5667. *Jon Leighton* + ## Rails 3.2.3 (unreleased) ## * Added find_or_create_by_{attribute}! dynamic method. *Andrew White* diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb index 779f8164cc..ab50e43ea0 100644 --- a/activerecord/lib/active_record/associations/preloader/association.rb +++ b/activerecord/lib/active_record/associations/preloader/association.rb @@ -77,7 +77,7 @@ module ActiveRecord # Some databases impose a limit on the number of ids in a list (in Oracle it's 1000) # Make several smaller queries if necessary or make one query if the adapter supports it sliced = owner_keys.each_slice(model.connection.in_clause_length || owner_keys.size) - records = sliced.map { |slice| records_for(slice) }.flatten + records = sliced.map { |slice| records_for(slice).to_a }.flatten end # Each record may have multiple owners, and vice-versa @@ -93,7 +93,8 @@ module ActiveRecord end def build_scope - scope = klass.scoped + scope = klass.unscoped + scope.default_scoped = true scope = scope.where(process_conditions(options[:conditions])) scope = scope.where(process_conditions(preload_options[:conditions])) -- cgit v1.2.3 From ebfa58abc895c0639211cb896a47b04e424322c8 Mon Sep 17 00:00:00 2001 From: Benedikt Deicke Date: Thu, 19 Apr 2012 10:07:28 +0200 Subject: Removes unneeded caching from ActiveRecord::Base.relation --- activerecord/lib/active_record/base.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 9746efc7d1..8c8717b759 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -450,12 +450,12 @@ module ActiveRecord #:nodoc: private def relation #:nodoc: - @relation ||= Relation.new(self, arel_table) + relation ||= Relation.new(self, arel_table) if finder_needs_type_condition? - @relation.where(type_condition).create_with(inheritance_column.to_sym => sti_name) + relation.where(type_condition).create_with(inheritance_column.to_sym => sti_name) else - @relation + relation end end end @@ -489,7 +489,6 @@ module ActiveRecord #:nodoc: @marked_for_destruction = false @previously_changed = {} @changed_attributes = {} - @relation = nil ensure_proper_type -- cgit v1.2.3