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

Reset statement cache for association if table_name is changed

Related to #27953.

All statement caches should be owned by the klass, otherwise cannot be
aware of table_name is changed.

Fixes #36453.
This commit is contained in:
Ryuta Kamizono 2020-05-29 15:07:11 +09:00
parent 01cffc3789
commit b7c846afaf
4 changed files with 24 additions and 14 deletions

View file

@ -214,14 +214,13 @@ module ActiveRecord
scope = self.scope
return scope.to_a if skip_statement_cache?(scope)
conn = klass.connection
sc = reflection.association_scope_cache(conn, owner) do |params|
sc = reflection.association_scope_cache(klass, owner) do |params|
as = AssociationScope.create { params.bind }
target_scope.merge!(as.scope(self))
end
binds = AssociationScope.get_bind_values(owner, reflection.chain)
sc.execute(binds, conn) { |record| set_inverse_instance(record) } || []
sc.execute(binds, klass.connection) { |record| set_inverse_instance(record) } || []
end
# The scope for this association.

View file

@ -297,12 +297,12 @@ module ActiveRecord
false
end
private
def cached_find_by_statement(key, &block)
cache = @find_by_statement_cache[connection.prepared_statements]
cache.compute_if_absent(key) { StatementCache.create(connection, &block) }
end
def cached_find_by_statement(key, &block) # :nodoc:
cache = @find_by_statement_cache[connection.prepared_statements]
cache.compute_if_absent(key) { StatementCache.create(connection, &block) }
end
private
def relation
relation = Relation.create(self)

View file

@ -1,7 +1,6 @@
# frozen_string_literal: true
require "active_support/core_ext/string/filters"
require "concurrent/map"
module ActiveRecord
# = Active Record Reflection
@ -436,19 +435,18 @@ module ActiveRecord
@type = options[:as] && (options[:foreign_type] || "#{options[:as]}_type")
@foreign_type = options[:polymorphic] && (options[:foreign_type] || "#{name}_type")
@constructable = calculate_constructable(macro, options)
@association_scope_cache = Concurrent::Map.new
if options[:class_name] && options[:class_name].class == Class
raise ArgumentError, "A class was passed to `:class_name` but we are expecting a string."
end
end
def association_scope_cache(conn, owner, &block)
key = conn.prepared_statements
def association_scope_cache(klass, owner, &block)
key = self
if polymorphic?
key = [key, owner._read_attribute(@foreign_type)]
end
@association_scope_cache.compute_if_absent(key) { StatementCache.create(conn, &block) }
klass.cached_find_by_statement(key, &block)
end
def constructable? # :nodoc:
@ -514,7 +512,7 @@ module ActiveRecord
# This is for clearing cache on the reflection. Useful for tests that need to compare
# SQL queries on associations.
def clear_association_scope_cache # :nodoc:
@association_scope_cache.clear
klass.initialize_find_by_cache
end
def nested?

View file

@ -136,5 +136,18 @@ module ActiveRecord
ensure
Book.table_name = :books
end
def test_find_association_does_not_use_statement_cache_if_table_name_is_changed
salty = Liquid.create(name: "salty")
molecule = salty.molecules.create(name: "dioxane")
assert_equal salty, molecule.liquid
Liquid.table_name = :birds
assert_nil molecule.reload_liquid
ensure
Liquid.table_name = :liquid
end
end
end