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:
parent
01cffc3789
commit
b7c846afaf
4 changed files with 24 additions and 14 deletions
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue