提交 d333d852 编写于 作者: R Ryuta Kamizono

Don't cache `find_by` statements on STI subclasses

Caching `find_by` statements on STI subclasses is unsafe, since
`type IN (?,?,?,?)` part is dynamic, and we don't have SQL statements
cache invalidation when a STI subclass is created or removed for now.
上级 b78e3b5e
...@@ -182,7 +182,8 @@ def find(*ids) # :nodoc: ...@@ -182,7 +182,8 @@ def find(*ids) # :nodoc:
end end
def find_by(*args) # :nodoc: def find_by(*args) # :nodoc:
return super if scope_attributes? || reflect_on_all_aggregations.any? return super if scope_attributes? || reflect_on_all_aggregations.any? ||
columns_hash.key?(inheritance_column) && !base_class?
hash = args.first hash = args.first
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
require "cases/helper" require "cases/helper"
require "models/topic" require "models/topic"
require "models/reply"
require "models/author" require "models/author"
require "models/post" require "models/post"
...@@ -53,12 +54,17 @@ def test_statement_cache_with_query_cache ...@@ -53,12 +54,17 @@ def test_statement_cache_with_query_cache
@connection.disable_query_cache! @connection.disable_query_cache!
end end
def test_statement_cache_with_find def test_statement_cache_with_find_by
@connection.clear_cache! @connection.clear_cache!
topics = Topic.where(id: 1).limit(1) assert_equal 1, Topic.find_by!(id: 1).id
assert_equal 1, Topic.find(1).id assert_equal 2, Reply.find_by!(id: 2).id
assert_includes statement_cache, to_sql_key(topics.arel)
topic_sql = cached_statement(Topic, [:id])
assert_includes statement_cache, to_sql_key(topic_sql)
e = assert_raise { cached_statement(Reply, [:id]) }
assert_equal "Reply has no cached statement by [:id]", e.message
end end
def test_statement_cache_with_in_clause def test_statement_cache_with_in_clause
...@@ -139,6 +145,13 @@ def to_sql_key(arel) ...@@ -139,6 +145,13 @@ def to_sql_key(arel)
@connection.respond_to?(:sql_key, true) ? @connection.send(:sql_key, sql) : sql @connection.respond_to?(:sql_key, true) ? @connection.send(:sql_key, sql) : sql
end end
def cached_statement(klass, key)
cache = klass.send(:cached_find_by_statement, key) do
raise "#{klass} has no cached statement by #{key.inspect}"
end
cache.send(:query_builder).instance_variable_get(:@sql)
end
def statement_cache def statement_cache
@connection.instance_variable_get(:@statements).send(:cache) @connection.instance_variable_get(:@statements).send(:cache)
end end
......
...@@ -514,10 +514,12 @@ def test_instantiation_doesnt_try_to_require_corresponding_file ...@@ -514,10 +514,12 @@ def test_instantiation_doesnt_try_to_require_corresponding_file
# Should fail without FirmOnTheFly in the type condition. # Should fail without FirmOnTheFly in the type condition.
assert_raise(ActiveRecord::RecordNotFound) { Firm.find(foo.id) } assert_raise(ActiveRecord::RecordNotFound) { Firm.find(foo.id) }
assert_raise(ActiveRecord::RecordNotFound) { Firm.find_by!(id: foo.id) }
# Nest FirmOnTheFly in the test case where Dependencies won't see it. # Nest FirmOnTheFly in the test case where Dependencies won't see it.
self.class.const_set :FirmOnTheFly, Class.new(Firm) self.class.const_set :FirmOnTheFly, Class.new(Firm)
assert_raise(ActiveRecord::SubclassNotFound) { Firm.find(foo.id) } assert_raise(ActiveRecord::SubclassNotFound) { Firm.find(foo.id) }
assert_raise(ActiveRecord::SubclassNotFound) { Firm.find_by!(id: foo.id) }
# Nest FirmOnTheFly in Firm where Dependencies will see it. # Nest FirmOnTheFly in Firm where Dependencies will see it.
# This is analogous to nesting models in a migration. # This is analogous to nesting models in a migration.
...@@ -526,6 +528,7 @@ def test_instantiation_doesnt_try_to_require_corresponding_file ...@@ -526,6 +528,7 @@ def test_instantiation_doesnt_try_to_require_corresponding_file
# And instantiate will find the existing constant rather than trying # And instantiate will find the existing constant rather than trying
# to require firm_on_the_fly. # to require firm_on_the_fly.
assert_nothing_raised { assert_kind_of Firm::FirmOnTheFly, Firm.find(foo.id) } assert_nothing_raised { assert_kind_of Firm::FirmOnTheFly, Firm.find(foo.id) }
assert_nothing_raised { assert_kind_of Firm::FirmOnTheFly, Firm.find_by!(id: foo.id) }
end end
end end
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册