aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record')
-rw-r--r--activerecord/lib/active_record/autosave_association.rb2
-rw-r--r--activerecord/lib/active_record/collection_cache_key.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb9
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb6
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb22
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb2
-rw-r--r--activerecord/lib/active_record/core.rb44
-rw-r--r--activerecord/lib/active_record/database_configurations.rb11
-rw-r--r--activerecord/lib/active_record/database_configurations/hash_config.rb2
-rw-r--r--activerecord/lib/active_record/inheritance.rb4
-rw-r--r--activerecord/lib/active_record/migration.rb2
-rw-r--r--activerecord/lib/active_record/persistence.rb18
-rw-r--r--activerecord/lib/active_record/railtie.rb10
-rw-r--r--activerecord/lib/active_record/relation.rb25
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb2
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb6
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder/array_handler.rb9
-rw-r--r--activerecord/lib/active_record/scoping.rb17
-rw-r--r--activerecord/lib/active_record/scoping/default.rb9
-rw-r--r--activerecord/lib/active_record/scoping/named.rb20
22 files changed, 143 insertions, 90 deletions
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index 783a8366ce..d77d76cb1e 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -149,7 +149,7 @@ module ActiveRecord
private
def define_non_cyclic_method(name, &block)
- return if method_defined?(name)
+ return if instance_methods(false).include?(name)
define_method(name) do |*args|
result = true; @_already_called ||= {}
# Loop prevention for validation of associations
diff --git a/activerecord/lib/active_record/collection_cache_key.rb b/activerecord/lib/active_record/collection_cache_key.rb
index dfba78614e..61581b0451 100644
--- a/activerecord/lib/active_record/collection_cache_key.rb
+++ b/activerecord/lib/active_record/collection_cache_key.rb
@@ -16,7 +16,7 @@ module ActiveRecord
collection = collection.send(:apply_join_dependency)
end
column_type = type_for_attribute(timestamp_column)
- column = connection.column_name_from_arel_node(collection.arel_attribute(timestamp_column))
+ column = connection.visitor.compile(collection.arel_attribute(timestamp_column))
select_values = "COUNT(*) AS #{connection.quote_column_name("size")}, MAX(%s) AS timestamp"
if collection.has_limit_or_offset?
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
index 7a9e7add24..ad148efcfe 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_limits.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+require "active_support/deprecation"
+
module ActiveRecord
module ConnectionAdapters # :nodoc:
module DatabaseLimits
@@ -12,11 +14,13 @@ module ActiveRecord
def column_name_length
64
end
+ deprecate :column_name_length
# Returns the maximum length of a table name.
def table_name_length
64
end
+ deprecate :table_name_length
# Returns the maximum allowed length for an index name. This
# limit is enforced by \Rails and is less than or equal to
@@ -36,16 +40,19 @@ module ActiveRecord
def columns_per_table
1024
end
+ deprecate :columns_per_table
# Returns the maximum number of indexes per table.
def indexes_per_table
16
end
+ deprecate :indexes_per_table
# Returns the maximum number of columns in a multicolumn index.
def columns_per_multicolumn_index
16
end
+ deprecate :columns_per_multicolumn_index
# Returns the maximum number of elements in an IN (x,y,z) clause.
# +nil+ means no limit.
@@ -57,11 +64,13 @@ module ActiveRecord
def sql_query_length
1048575
end
+ deprecate :sql_query_length
# Returns maximum number of joins in a single query.
def joins_per_query
256
end
+ deprecate :joins_per_query
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
index 1d36c3c8b1..fdc9ffa688 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -20,7 +20,7 @@ module ActiveRecord
raise "Passing bind parameters with an arel AST is forbidden. " \
"The values must be stored on the AST directly"
end
- sql, binds = visitor.accept(arel_or_sql_string.ast, collector).value
+ sql, binds = visitor.compile(arel_or_sql_string.ast, collector)
[sql.freeze, binds || []]
else
[arel_or_sql_string.dup.freeze, binds]
@@ -32,11 +32,11 @@ module ActiveRecord
# can be used to query the database repeatedly.
def cacheable_query(klass, arel) # :nodoc:
if prepared_statements
- sql, binds = visitor.accept(arel.ast, collector).value
+ sql, binds = visitor.compile(arel.ast, collector)
query = klass.query(sql)
else
collector = PartialQueryCollector.new
- parts, binds = visitor.accept(arel.ast, collector).value
+ parts, binds = visitor.compile(arel.ast, collector)
query = klass.partial_query(parts)
end
[query, binds]
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
index 84ea7a0c33..4a6e915b66 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -602,6 +602,7 @@ module ActiveRecord
# The +type+ and +options+ parameters will be ignored if present. It can be helpful
# to provide these in a migration's +change+ method so it can be reverted.
# In that case, +type+ and +options+ will be used by #add_column.
+ # Indexes on the column are automatically removed.
def remove_column(table_name, column_name, type = nil, options = {})
execute "ALTER TABLE #{quote_table_name(table_name)} #{remove_column_for_alter(table_name, column_name, type, options)}"
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index a62651daff..42ed9ce82d 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -360,6 +360,10 @@ module ActiveRecord
def enable_extension(name)
end
+ def advisory_locks_enabled? # :nodoc:
+ supports_advisory_locks? && @advisory_locks_enabled
+ end
+
# This is meant to be implemented by the adapters that support advisory
# locks
#
@@ -490,11 +494,7 @@ module ActiveRecord
end
def column_name_for_operation(operation, node) # :nodoc:
- column_name_from_arel_node(node)
- end
-
- def column_name_from_arel_node(node) # :nodoc:
- visitor.accept(node, Arel::Collectors::SQLString.new).value
+ visitor.compile(node)
end
def default_index_type?(index) # :nodoc:
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index ad045f85ef..5d4fc8324f 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -53,8 +53,8 @@ module ActiveRecord
@statements = StatementPool.new(self.class.type_cast_config_to_integer(config[:statement_limit]))
- if version < "5.1.10"
- raise "Your version of MySQL (#{version_string}) is too old. Active Record supports MySQL >= 5.1.10."
+ if version < "5.5.8"
+ raise "Your version of MySQL (#{version_string}) is too old. Active Record supports MySQL >= 5.5.8."
end
end
@@ -111,7 +111,15 @@ module ActiveRecord
end
def supports_advisory_locks?
- @advisory_locks_enabled
+ true
+ end
+
+ def supports_longer_index_key_prefix?
+ if mariadb?
+ version >= "10.2.2"
+ else
+ version >= "5.7.9"
+ end
end
def get_advisory_lock(lock_name, timeout = 0) # :nodoc:
@@ -241,7 +249,7 @@ module ActiveRecord
end
# Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
- # Charset defaults to utf8.
+ # Charset defaults to utf8mb4.
#
# Example:
# create_database 'charset_test', charset: 'latin1', collation: 'latin1_bin'
@@ -250,8 +258,12 @@ module ActiveRecord
def create_database(name, options = {})
if options[:collation]
execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT COLLATE #{quote_table_name(options[:collation])}"
+ elsif options[:charset]
+ execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET #{quote_table_name(options[:charset])}"
+ elsif supports_longer_index_key_prefix?
+ execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET `utf8mb4`"
else
- execute "CREATE DATABASE #{quote_table_name(name)} DEFAULT CHARACTER SET #{quote_table_name(options[:charset] || 'utf8')}"
+ raise "Configure a supported :charset and ensure innodb_large_prefix is enabled to support indexes on varchar(255) string columns."
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 30e651ee63..3ee344a249 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -298,7 +298,7 @@ module ActiveRecord
end
def supports_advisory_locks?
- @advisory_locks_enabled
+ true
end
def supports_explain?
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 9ec6ba14fd..392602bc0f 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -3,11 +3,14 @@
require "active_support/core_ext/hash/indifferent_access"
require "active_support/core_ext/string/filters"
require "concurrent/map"
+require "set"
module ActiveRecord
module Core
extend ActiveSupport::Concern
+ FILTERED = "[FILTERED]" # :nodoc:
+
included do
##
# :singleton-method:
@@ -123,6 +126,8 @@ module ActiveRecord
class_attribute :default_connection_handler, instance_writer: false
+ self.filter_attributes = []
+
def self.connection_handler
ActiveRecord::RuntimeRegistry.connection_handler || default_connection_handler
end
@@ -134,7 +139,7 @@ module ActiveRecord
self.default_connection_handler = ConnectionAdapters::ConnectionHandler.new
end
- module ClassMethods # :nodoc:
+ module ClassMethods
def initialize_find_by_cache # :nodoc:
@find_by_statement_cache = { true => Concurrent::Map.new, false => Concurrent::Map.new }
end
@@ -211,7 +216,7 @@ module ActiveRecord
generated_association_methods
end
- def generated_association_methods
+ def generated_association_methods # :nodoc:
@generated_association_methods ||= begin
mod = const_set(:GeneratedAssociationMethods, Module.new)
private_constant :GeneratedAssociationMethods
@@ -221,8 +226,22 @@ module ActiveRecord
end
end
+ # Returns columns which shouldn't be exposed while calling +#inspect+.
+ def filter_attributes
+ if defined?(@filter_attributes)
+ @filter_attributes
+ else
+ superclass.filter_attributes
+ end
+ end
+
+ # Specifies columns which shouldn't be exposed while calling +#inspect+.
+ def filter_attributes=(attributes_names)
+ @filter_attributes = attributes_names.map(&:to_s).to_set
+ end
+
# Returns a string like 'Post(id:integer, title:string, body:text)'
- def inspect
+ def inspect # :nodoc:
if self == Base
super
elsif abstract_class?
@@ -238,7 +257,7 @@ module ActiveRecord
end
# Overwrite the default class equality method to provide support for decorated models.
- def ===(object)
+ def ===(object) # :nodoc:
object.is_a?(self)
end
@@ -492,7 +511,11 @@ module ActiveRecord
inspection = if defined?(@attributes) && @attributes
self.class.attribute_names.collect do |name|
if has_attribute?(name)
- "#{name}: #{attribute_for_inspect(name)}"
+ if filter_attribute?(name)
+ "#{name}: #{ActiveRecord::Core::FILTERED}"
+ else
+ "#{name}: #{attribute_for_inspect(name)}"
+ end
end
end.compact.join(", ")
else
@@ -510,13 +533,16 @@ module ActiveRecord
if defined?(@attributes) && @attributes
column_names = self.class.column_names.select { |name| has_attribute?(name) || new_record? }
pp.seplist(column_names, proc { pp.text "," }) do |column_name|
- column_value = read_attribute(column_name)
pp.breakable " "
pp.group(1) do
pp.text column_name
pp.text ":"
pp.breakable
- pp.pp column_value
+ if filter_attribute?(column_name)
+ pp.text ActiveRecord::Core::FILTERED
+ else
+ pp.pp read_attribute(column_name)
+ end
end
end
else
@@ -567,5 +593,9 @@ module ActiveRecord
def custom_inspect_method_defined?
self.class.instance_method(:inspect).owner != ActiveRecord::Base.instance_method(:inspect).owner
end
+
+ def filter_attribute?(attribute_name)
+ self.class.filter_attributes.include?(attribute_name) && !read_attribute(attribute_name).nil?
+ end
end
end
diff --git a/activerecord/lib/active_record/database_configurations.rb b/activerecord/lib/active_record/database_configurations.rb
index a94f46d07f..9ff63c6e10 100644
--- a/activerecord/lib/active_record/database_configurations.rb
+++ b/activerecord/lib/active_record/database_configurations.rb
@@ -10,6 +10,7 @@ module ActiveRecord
# application's database configuration hash or url string.
class DatabaseConfigurations
attr_reader :configurations
+ delegate :any?, to: :configurations
def initialize(configurations = {})
@configurations = build_configs(configurations)
@@ -18,8 +19,8 @@ module ActiveRecord
# Collects the configs for the environment and optionally the specification
# name passed in. To include replica configurations pass `include_replicas: true`.
#
- # If a spec name is provided a single DatabaseConfiguration object will be
- # returned, otherwise an array of DatabaseConfiguration objects will be
+ # If a spec name is provided a single DatabaseConfig object will be
+ # returned, otherwise an array of DatabaseConfig objects will be
# returned that corresponds with the environment and type requested.
#
# Options:
@@ -53,7 +54,7 @@ module ActiveRecord
# Returns the config hash that corresponds with the environment
#
# If the application has multiple databases `default_hash` will
- # the first config hash for the environment.
+ # return the first config hash for the environment.
#
# { database: "my_db", adapter: "mysql2" }
def default_hash(env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call.to_s)
@@ -64,7 +65,7 @@ module ActiveRecord
# Returns a single DatabaseConfig object based on the requested environment.
#
- # If the application has multiple databases `select_db_config` will return
+ # If the application has multiple databases `find_db_config` will return
# the first DatabaseConfig for the environment.
def find_db_config(env)
configurations.find do |db_config|
@@ -73,7 +74,7 @@ module ActiveRecord
end
end
- # Returns the DatabaseConfig object as a Hash.
+ # Returns the DatabaseConfigurations object as a Hash.
def to_h
configs = configurations.reverse.inject({}) do |memo, db_config|
memo.merge(db_config.to_legacy_hash)
diff --git a/activerecord/lib/active_record/database_configurations/hash_config.rb b/activerecord/lib/active_record/database_configurations/hash_config.rb
index 18ed7c0466..13ffe566cf 100644
--- a/activerecord/lib/active_record/database_configurations/hash_config.rb
+++ b/activerecord/lib/active_record/database_configurations/hash_config.rb
@@ -12,7 +12,7 @@ module ActiveRecord
# Becomes:
#
# #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10
- # @env_name="development", @spec_name="primary", @config={"db_name"}>
+ # @env_name="development", @spec_name="primary", @config={"database"=>"db_name"}>
#
# Options are:
#
diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb
index 6891c575c7..b25057acda 100644
--- a/activerecord/lib/active_record/inheritance.rb
+++ b/activerecord/lib/active_record/inheritance.rb
@@ -55,6 +55,10 @@ module ActiveRecord
if has_attribute?(inheritance_column)
subclass = subclass_from_attributes(attributes)
+ if subclass.nil? && scope_attributes = current_scope&.scope_for_create
+ subclass = subclass_from_attributes(scope_attributes)
+ end
+
if subclass.nil? && base_class?
subclass = subclass_from_attributes(column_defaults)
end
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index d68ed3cad9..ea53324829 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -1357,7 +1357,7 @@ module ActiveRecord
end
def use_advisory_lock?
- Base.connection.supports_advisory_locks?
+ Base.connection.advisory_locks_enabled?
end
def with_advisory_lock
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 24af56cf0b..6eb2bfb452 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -70,17 +70,6 @@ module ActiveRecord
instantiate_instance_of(klass, attributes, column_types, &block)
end
- # Given a class, an attributes hash, +instantiate_instance_of+ returns a
- # new instance of the class. Accepts only keys as strings.
- #
- # This is private, don't call it. :)
- #
- # :nodoc:
- def instantiate_instance_of(klass, attributes, column_types = {}, &block)
- attributes = klass.attributes_builder.build_from_database(attributes, column_types)
- klass.allocate.init_from_db(attributes, &block)
- end
-
# Updates an object (or multiple objects) and saves it to the database, if validations pass.
# The resulting object is returned whether the object was saved successfully to the database or not.
#
@@ -216,6 +205,13 @@ module ActiveRecord
end
private
+ # Given a class, an attributes hash, +instantiate_instance_of+ returns a
+ # new instance of the class. Accepts only keys as strings.
+ def instantiate_instance_of(klass, attributes, column_types = {}, &block)
+ attributes = klass.attributes_builder.build_from_database(attributes, column_types)
+ klass.allocate.init_from_db(attributes, &block)
+ end
+
# Called by +instantiate+ to decide which class to use for a new
# record instance.
#
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index 7ece083fd4..b213754641 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -180,9 +180,7 @@ end_warning
end
initializer "active_record.set_executor_hooks" do
- ActiveSupport.on_load(:active_record) do
- ActiveRecord::QueryCache.install_executor_hooks
- end
+ ActiveRecord::QueryCache.install_executor_hooks
end
initializer "active_record.add_watchable_files" do |app|
@@ -235,5 +233,11 @@ MSG
end
end
end
+
+ initializer "active_record.set_filter_attributes" do
+ ActiveSupport.on_load(:active_record) do
+ self.filter_attributes += Rails.application.config.filter_parameters
+ end
+ end
end
end
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 29a3ceab7d..86e5d14c81 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -62,7 +62,7 @@ module ActiveRecord
# user = users.new { |user| user.name = 'Oscar' }
# user.name # => Oscar
def new(attributes = nil, &block)
- scoping { klass.new(scope_for_create(attributes), &block) }
+ scoping { klass.new(attributes, &block) }
end
alias build new
@@ -87,11 +87,7 @@ module ActiveRecord
# users.create(name: nil) # validation on name
# # => #<User id: nil, name: nil, ...>
def create(attributes = nil, &block)
- if attributes.is_a?(Array)
- attributes.collect { |attr| create(attr, &block) }
- else
- scoping { klass.create(scope_for_create(attributes), &block) }
- end
+ scoping { klass.create(attributes, &block) }
end
# Similar to #create, but calls
@@ -101,11 +97,7 @@ module ActiveRecord
# Expects arguments in the same format as
# {ActiveRecord::Base.create!}[rdoc-ref:Persistence::ClassMethods#create!].
def create!(attributes = nil, &block)
- if attributes.is_a?(Array)
- attributes.collect { |attr| create!(attr, &block) }
- else
- scoping { klass.create!(scope_for_create(attributes), &block) }
- end
+ scoping { klass.create!(attributes, &block) }
end
def first_or_create(attributes = nil, &block) # :nodoc:
@@ -315,10 +307,7 @@ module ActiveRecord
# Please check unscoped if you want to remove all previous scopes (including
# the default_scope) during the execution of a block.
def scoping
- previous, klass.current_scope = klass.current_scope(true), self unless @delegate_to_klass
- yield
- ensure
- klass.current_scope = previous unless @delegate_to_klass
+ @delegate_to_klass ? yield : klass._scoping(self) { yield }
end
def _exec_scope(*args, &block) # :nodoc:
@@ -554,10 +543,8 @@ module ActiveRecord
where_clause.to_h(relation_table_name)
end
- def scope_for_create(attributes = nil)
- scope = where_values_hash.merge!(create_with_value.stringify_keys)
- scope.merge!(attributes) if attributes
- scope
+ def scope_for_create
+ where_values_hash.merge!(create_with_value.stringify_keys)
end
# Returns true if relation needs eager loading.
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index c5562c1ff0..0fabfe5518 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -398,7 +398,7 @@ module ActiveRecord
def limited_ids_for(relation)
values = @klass.connection.columns_for_distinct(
- connection.column_name_from_arel_node(arel_attribute(primary_key)),
+ connection.visitor.compile(arel_attribute(primary_key)),
relation.order_values
)
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index f734cd0ad8..cb70b8bcde 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -57,10 +57,14 @@ module ActiveRecord
end
def build_bind_attribute(column_name, value)
- attr = Relation::QueryAttribute.new(column_name.to_s, value, table.type(column_name))
+ attr = build_query_attribute(column_name, value)
Arel::Nodes::BindParam.new(attr)
end
+ def build_query_attribute(column_name, value)
+ Relation::QueryAttribute.new(column_name.to_s, value, table.type(column_name))
+ end
+
protected
def expand_from_hash(attributes)
return ["1=0"] if attributes.empty?
diff --git a/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb
index e5191fa38a..0895b9fba6 100644
--- a/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb
@@ -21,10 +21,11 @@ module ActiveRecord
when 0 then NullPredicate
when 1 then predicate_builder.build(attribute, values.first)
else
- bind_values = values.map do |v|
- predicate_builder.build_bind_attribute(attribute.name, v)
- end
- attribute.in(bind_values)
+ values.map! do |v|
+ attr = predicate_builder.build_query_attribute(attribute.name, v)
+ attr.value_for_database if attr.boundable?
+ end.compact!
+ values.empty? ? NullPredicate : attribute.in(values)
end
unless nils.empty?
diff --git a/activerecord/lib/active_record/scoping.rb b/activerecord/lib/active_record/scoping.rb
index 01ac56570a..9eba1254a4 100644
--- a/activerecord/lib/active_record/scoping.rb
+++ b/activerecord/lib/active_record/scoping.rb
@@ -12,14 +12,6 @@ module ActiveRecord
end
module ClassMethods # :nodoc:
- def current_scope(skip_inherited_scope = false)
- ScopeRegistry.value_for(:current_scope, self, skip_inherited_scope)
- end
-
- def current_scope=(scope)
- ScopeRegistry.set_value_for(:current_scope, self, scope)
- end
-
# Collects attributes from scopes that should be applied when creating
# an AR instance for the particular class this is called on.
def scope_attributes
@@ -30,6 +22,15 @@ module ActiveRecord
def scope_attributes?
current_scope
end
+
+ private
+ def current_scope(skip_inherited_scope = false)
+ ScopeRegistry.value_for(:current_scope, self, skip_inherited_scope)
+ end
+
+ def current_scope=(scope)
+ ScopeRegistry.set_value_for(:current_scope, self, scope)
+ end
end
def populate_with_current_scope_attributes # :nodoc:
diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb
index 8c612df27a..6caf9b3251 100644
--- a/activerecord/lib/active_record/scoping/default.rb
+++ b/activerecord/lib/active_record/scoping/default.rb
@@ -31,7 +31,14 @@ module ActiveRecord
# Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
# }
def unscoped
- block_given? ? relation.scoping { yield } : relation
+ block_given? ? _scoping(relation) { yield } : relation
+ end
+
+ def _scoping(relation) # :nodoc:
+ previous, self.current_scope = current_scope(true), relation
+ yield
+ ensure
+ self.current_scope = previous
end
# Are there attributes associated with this scope?
diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb
index a784001587..573d97b819 100644
--- a/activerecord/lib/active_record/scoping/named.rb
+++ b/activerecord/lib/active_record/scoping/named.rb
@@ -24,13 +24,13 @@ module ActiveRecord
# You can define a scope that applies to all finders using
# {default_scope}[rdoc-ref:Scoping::Default::ClassMethods#default_scope].
def all
- current_scope = self.current_scope
+ scope = current_scope
- if current_scope
- if self == current_scope.klass
- current_scope.clone
+ if scope
+ if self == scope.klass
+ scope.clone
else
- relation.merge!(current_scope)
+ relation.merge!(scope)
end
else
default_scoped
@@ -38,9 +38,7 @@ module ActiveRecord
end
def scope_for_association(scope = relation) # :nodoc:
- current_scope = self.current_scope
-
- if current_scope && current_scope.empty_scope?
+ if current_scope&.empty_scope?
scope
else
default_scoped(scope)
@@ -182,15 +180,13 @@ module ActiveRecord
if body.respond_to?(:to_proc)
singleton_class.send(:define_method, name) do |*args|
- scope = all
- scope = scope._exec_scope(*args, &body)
+ scope = all._exec_scope(*args, &body)
scope = scope.extending(extension) if extension
scope
end
else
singleton_class.send(:define_method, name) do |*args|
- scope = all
- scope = scope.scoping { body.call(*args) || scope }
+ scope = body.call(*args) || all
scope = scope.extending(extension) if extension
scope
end