1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

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.
This commit is contained in:
Ryuta Kamizono 2019-02-27 02:31:39 +09:00
parent b78e3b5e21
commit d333d85254
3 changed files with 22 additions and 5 deletions

View file

@ -182,7 +182,8 @@ module ActiveRecord
end
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

View file

@ -2,6 +2,7 @@
require "cases/helper"
require "models/topic"
require "models/reply"
require "models/author"
require "models/post"
@ -53,12 +54,17 @@ if ActiveRecord::Base.connection.prepared_statements
@connection.disable_query_cache!
end
def test_statement_cache_with_find
def test_statement_cache_with_find_by
@connection.clear_cache!
topics = Topic.where(id: 1).limit(1)
assert_equal 1, Topic.find(1).id
assert_includes statement_cache, to_sql_key(topics.arel)
assert_equal 1, Topic.find_by!(id: 1).id
assert_equal 2, Reply.find_by!(id: 2).id
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
def test_statement_cache_with_in_clause
@ -139,6 +145,13 @@ if ActiveRecord::Base.connection.prepared_statements
@connection.respond_to?(:sql_key, true) ? @connection.send(:sql_key, sql) : sql
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
@connection.instance_variable_get(:@statements).send(:cache)
end

View file

@ -514,10 +514,12 @@ class InheritanceComputeTypeTest < ActiveRecord::TestCase
# Should fail without FirmOnTheFly in the type condition.
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.
self.class.const_set :FirmOnTheFly, Class.new(Firm)
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.
# This is analogous to nesting models in a migration.
@ -526,6 +528,7 @@ class InheritanceComputeTypeTest < ActiveRecord::TestCase
# And instantiate will find the existing constant rather than trying
# 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_by!(id: foo.id) }
end
end