aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib')
-rw-r--r--activerecord/lib/active_record.rb4
-rw-r--r--activerecord/lib/active_record/associations.rb17
-rw-r--r--activerecord/lib/active_record/associations/alias_tracker.rb33
-rw-r--r--activerecord/lib/active_record/associations/association.rb4
-rw-r--r--activerecord/lib/active_record/associations/association_scope.rb67
-rw-r--r--activerecord/lib/active_record/associations/builder/association.rb5
-rw-r--r--activerecord/lib/active_record/associations/builder/collection_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb6
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb4
-rw-r--r--activerecord/lib/active_record/associations/join_dependency.rb14
-rw-r--r--activerecord/lib/active_record/associations/join_dependency/join_association.rb19
-rw-r--r--activerecord/lib/active_record/associations/join_dependency/join_base.rb2
-rw-r--r--activerecord/lib/active_record/associations/preloader.rb6
-rw-r--r--activerecord/lib/active_record/associations/preloader/association.rb54
-rw-r--r--activerecord/lib/active_record/associations/preloader/belongs_to.rb7
-rw-r--r--activerecord/lib/active_record/associations/preloader/collection_association.rb11
-rw-r--r--activerecord/lib/active_record/associations/preloader/has_many.rb7
-rw-r--r--activerecord/lib/active_record/associations/preloader/has_one.rb7
-rw-r--r--activerecord/lib/active_record/associations/preloader/singular_association.rb11
-rw-r--r--activerecord/lib/active_record/associations/preloader/through_association.rb46
-rw-r--r--activerecord/lib/active_record/attribute/user_provided_default.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb6
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb34
-rw-r--r--activerecord/lib/active_record/attribute_methods/primary_key.rb6
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb5
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb5
-rw-r--r--activerecord/lib/active_record/attribute_set.rb4
-rw-r--r--activerecord/lib/active_record/attribute_set/builder.rb2
-rw-r--r--activerecord/lib/active_record/attributes.rb2
-rw-r--r--activerecord/lib/active_record/base.rb16
-rw-r--r--activerecord/lib/active_record/callbacks.rb8
-rw-r--r--activerecord/lib/active_record/collection_cache_key.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb17
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb27
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb35
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb42
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/connection_specification.rb18
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb7
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb8
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb4
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/column.rb29
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid.rb42
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/referential_integrity.rb42
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb16
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb20
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb49
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb19
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb16
-rw-r--r--activerecord/lib/active_record/core.rb20
-rw-r--r--activerecord/lib/active_record/explain.rb2
-rw-r--r--activerecord/lib/active_record/explain_subscriber.rb2
-rw-r--r--activerecord/lib/active_record/fixtures.rb4
-rw-r--r--activerecord/lib/active_record/internal_metadata.rb4
-rw-r--r--activerecord/lib/active_record/locking/pessimistic.rb9
-rw-r--r--activerecord/lib/active_record/migration.rb9
-rw-r--r--activerecord/lib/active_record/migration/command_recorder.rb4
-rw-r--r--activerecord/lib/active_record/migration/compatibility.rb67
-rw-r--r--activerecord/lib/active_record/model_schema.rb32
-rw-r--r--activerecord/lib/active_record/persistence.rb96
-rw-r--r--activerecord/lib/active_record/querying.rb2
-rw-r--r--activerecord/lib/active_record/railtie.rb10
-rw-r--r--activerecord/lib/active_record/railties/controller_runtime.rb2
-rw-r--r--activerecord/lib/active_record/railties/databases.rake10
-rw-r--r--activerecord/lib/active_record/reflection.rb123
-rw-r--r--activerecord/lib/active_record/relation.rb109
-rw-r--r--activerecord/lib/active_record/relation/batches.rb17
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb9
-rw-r--r--activerecord/lib/active_record/relation/delegation.rb3
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb39
-rw-r--r--activerecord/lib/active_record/relation/merger.rb2
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb15
-rw-r--r--activerecord/lib/active_record/relation/query_attribute.rb2
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb66
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb2
-rw-r--r--activerecord/lib/active_record/sanitization.rb7
-rw-r--r--activerecord/lib/active_record/schema_migration.rb4
-rw-r--r--activerecord/lib/active_record/tasks/database_tasks.rb21
-rw-r--r--activerecord/lib/active_record/tasks/mysql_database_tasks.rb2
-rw-r--r--activerecord/lib/active_record/transactions.rb2
-rw-r--r--activerecord/lib/active_record/type.rb24
-rw-r--r--activerecord/lib/active_record/type_caster.rb4
-rw-r--r--activerecord/lib/active_record/validations.rb10
89 files changed, 744 insertions, 849 deletions
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb
index 0c19fed9e1..bf6dfd46e1 100644
--- a/activerecord/lib/active_record.rb
+++ b/activerecord/lib/active_record.rb
@@ -28,8 +28,8 @@ require "active_support/rails"
require "active_model"
require "arel"
-require_relative "active_record/version"
-require_relative "active_record/attribute_set"
+require "active_record/version"
+require "active_record/attribute_set"
module ActiveRecord
extend ActiveSupport::Autoload
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index a61c0336db..573f41d7ad 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -3,7 +3,7 @@
require "active_support/core_ext/enumerable"
require "active_support/core_ext/string/conversions"
require "active_support/core_ext/module/remove_method"
-require_relative "errors"
+require "active_record/errors"
module ActiveRecord
class AssociationNotFoundError < ConfigurationError #:nodoc:
@@ -557,9 +557,8 @@ module ActiveRecord
# has_many :birthday_events, ->(user) { where(starts_on: user.birthday) }, class_name: 'Event'
# end
#
- # Note: Joining, eager loading and preloading of these associations is not fully possible.
+ # Note: Joining, eager loading and preloading of these associations is not possible.
# These operations happen before instance creation and the scope will be called with a +nil+ argument.
- # This can lead to unexpected behavior and is deprecated.
#
# == Association callbacks
#
@@ -850,7 +849,7 @@ module ActiveRecord
# project.milestones # fetches milestones from the database
# project.milestones.size # uses the milestone cache
# project.milestones.empty? # uses the milestone cache
- # project.milestones(true).size # fetches milestones from the database
+ # project.milestones.reload.size # fetches milestones from the database
# project.milestones # uses the milestone cache
#
# == Eager loading of associations
@@ -1400,7 +1399,7 @@ module ActiveRecord
# has_many :tags, as: :taggable
# has_many :reports, -> { readonly }
# has_many :subscribers, through: :subscriptions, source: :user
- def has_many(name, scope = nil, options = {}, &extension)
+ def has_many(name, scope = nil, **options, &extension)
reflection = Builder::HasMany.build(self, name, scope, options, &extension)
Reflection.add_reflection self, name, reflection
end
@@ -1534,7 +1533,7 @@ module ActiveRecord
# has_one :club, through: :membership
# has_one :primary_address, -> { where(primary: true) }, through: :addressables, source: :addressable
# has_one :credit_card, required: true
- def has_one(name, scope = nil, options = {})
+ def has_one(name, scope = nil, **options)
reflection = Builder::HasOne.build(self, name, scope, options)
Reflection.add_reflection self, name, reflection
end
@@ -1678,7 +1677,7 @@ module ActiveRecord
# belongs_to :company, touch: :employees_last_updated_at
# belongs_to :user, optional: true
# belongs_to :account, default: -> { company.account }
- def belongs_to(name, scope = nil, options = {})
+ def belongs_to(name, scope = nil, **options)
reflection = Builder::BelongsTo.build(self, name, scope, options)
Reflection.add_reflection self, name, reflection
end
@@ -1848,7 +1847,7 @@ module ActiveRecord
builder = Builder::HasAndBelongsToMany.new name, self, options
- join_model = ActiveSupport::Deprecation.silence { builder.through_model }
+ join_model = builder.through_model
const_set join_model.name, join_model
private_constant join_model.name
@@ -1877,7 +1876,7 @@ module ActiveRecord
hm_options[k] = options[k] if options.key? k
end
- ActiveSupport::Deprecation.silence { has_many name, scope, hm_options, &extension }
+ has_many name, scope, hm_options, &extension
_reflections[name.to_s].parent_reflection = habtm_reflection
end
end
diff --git a/activerecord/lib/active_record/associations/alias_tracker.rb b/activerecord/lib/active_record/associations/alias_tracker.rb
index 096f016976..07bd0a273b 100644
--- a/activerecord/lib/active_record/associations/alias_tracker.rb
+++ b/activerecord/lib/active_record/associations/alias_tracker.rb
@@ -6,36 +6,34 @@ module ActiveRecord
module Associations
# Keeps track of table aliases for ActiveRecord::Associations::JoinDependency
class AliasTracker # :nodoc:
- def self.create(connection, initial_table)
- aliases = Hash.new(0)
- aliases[initial_table] = 1
- new(connection, aliases)
- end
-
- def self.create_with_joins(connection, initial_table, joins)
+ def self.create(connection, initial_table, joins)
if joins.empty?
- create(connection, initial_table)
+ aliases = Hash.new(0)
else
aliases = Hash.new { |h, k|
h[k] = initial_count_for(connection, k, joins)
}
- aliases[initial_table] = 1
- new(connection, aliases)
end
+ aliases[initial_table] = 1
+ new(connection, aliases)
end
def self.initial_count_for(connection, name, table_joins)
- # quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase
- quoted_name = connection.quote_table_name(name).downcase
+ quoted_name = nil
counts = table_joins.map do |join|
if join.is_a?(Arel::Nodes::StringJoin)
+ # quoted_name should be case ignored as some database adapters (Oracle) return quoted name in uppercase
+ quoted_name ||= connection.quote_table_name(name)
+
# Table names + table aliases
- join.left.downcase.scan(
- /join(?:\s+\w+)?\s+(\S+\s+)?#{quoted_name}\son/
+ join.left.scan(
+ /JOIN(?:\s+\w+)?\s+(?:\S+\s+)?(?:#{quoted_name}|#{name})\sON/i
).size
elsif join.respond_to? :left
- join.left.table_name == name ? 1 : 0
+ join.left.name == name ? 1 : 0
+ elsif join.is_a?(Hash)
+ join[name]
else
# this branch is reached by two tests:
#
@@ -79,10 +77,7 @@ module ActiveRecord
end
end
- # TODO Change this to private once we've dropped Ruby 2.2 support.
- # Workaround for Ruby 2.2 "private attribute?" warning.
- protected
- attr_reader :aliases
+ attr_reader :aliases
private
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb
index 268b022ab8..ca1f9f1650 100644
--- a/activerecord/lib/active_record/associations/association.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -130,8 +130,8 @@ module ActiveRecord
def extensions
extensions = klass.default_extensions | reflection.extensions
- if scope = reflection.scope
- extensions |= klass.unscoped.instance_exec(owner, &scope).extensions
+ if reflection.scope
+ extensions |= reflection.scope_for(klass.unscoped, owner).extensions
end
extensions
diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb
index 3d79e540b8..11967e0571 100644
--- a/activerecord/lib/active_record/associations/association_scope.rb
+++ b/activerecord/lib/active_record/associations/association_scope.rb
@@ -23,15 +23,10 @@ module ActiveRecord
reflection = association.reflection
scope = klass.unscoped
owner = association.owner
- alias_tracker = AliasTracker.create(klass.connection, klass.table_name)
- chain_head, chain_tail = get_chain(reflection, association, alias_tracker)
+ chain = get_chain(reflection, association, scope.alias_tracker)
scope.extending! reflection.extensions
- add_constraints(scope, owner, reflection, chain_head, chain_tail)
- end
-
- def join_type
- Arel::Nodes::InnerJoin
+ add_constraints(scope, owner, chain)
end
def self.get_bind_values(owner, chain)
@@ -59,14 +54,15 @@ module ActiveRecord
private
def join(table, constraint)
- table.create_join(table, table.create_on(constraint), join_type)
+ table.create_join(table, table.create_on(constraint))
end
- def last_chain_scope(scope, table, reflection, owner)
+ def last_chain_scope(scope, reflection, owner)
join_keys = reflection.join_keys
key = join_keys.key
foreign_key = join_keys.foreign_key
+ table = reflection.aliased_table
value = transform_value(owner[foreign_key])
scope = apply_scope(scope, table, key, value)
@@ -82,11 +78,13 @@ module ActiveRecord
value_transformation.call(value)
end
- def next_chain_scope(scope, table, reflection, foreign_table, next_reflection)
+ def next_chain_scope(scope, reflection, next_reflection)
join_keys = reflection.join_keys
key = join_keys.key
foreign_key = join_keys.foreign_key
+ table = reflection.aliased_table
+ foreign_table = next_reflection.aliased_table
constraint = table[key].eq(foreign_table[foreign_key])
if reflection.type
@@ -98,12 +96,11 @@ module ActiveRecord
end
class ReflectionProxy < SimpleDelegator # :nodoc:
- attr_accessor :next
- attr_reader :alias_name
+ attr_reader :aliased_table
- def initialize(reflection, alias_name)
+ def initialize(reflection, aliased_table)
super(reflection)
- @alias_name = alias_name
+ @aliased_table = aliased_table
end
def all_includes; nil; end
@@ -111,42 +108,33 @@ module ActiveRecord
def get_chain(reflection, association, tracker)
name = reflection.name
- runtime_reflection = Reflection::RuntimeReflection.new(reflection, association)
- previous_reflection = runtime_reflection
+ chain = [Reflection::RuntimeReflection.new(reflection, association)]
reflection.chain.drop(1).each do |refl|
- alias_name = tracker.aliased_table_for(
+ aliased_table = tracker.aliased_table_for(
refl.table_name,
refl.alias_candidate(name),
refl.klass.type_caster
)
- proxy = ReflectionProxy.new(refl, alias_name)
- previous_reflection.next = proxy
- previous_reflection = proxy
+ chain << ReflectionProxy.new(refl, aliased_table)
end
- [runtime_reflection, previous_reflection]
+ chain
end
- def add_constraints(scope, owner, refl, chain_head, chain_tail)
- owner_reflection = chain_tail
- table = owner_reflection.alias_name
- scope = last_chain_scope(scope, table, owner_reflection, owner)
-
- reflection = chain_head
- while reflection
- table = reflection.alias_name
- next_reflection = reflection.next
+ def add_constraints(scope, owner, chain)
+ scope = last_chain_scope(scope, chain.last, owner)
- unless reflection == chain_tail
- foreign_table = next_reflection.alias_name
- scope = next_chain_scope(scope, table, reflection, foreign_table, next_reflection)
- end
+ chain.each_cons(2) do |reflection, next_reflection|
+ scope = next_chain_scope(scope, reflection, next_reflection)
+ end
+ chain_head = chain.first
+ chain.reverse_each do |reflection|
# Exclude the scope of the association itself, because that
# was already merged in the #scope method.
reflection.constraints.each do |scope_chain_item|
- item = eval_scope(reflection, table, scope_chain_item, owner)
+ item = eval_scope(reflection, scope_chain_item, owner)
- if scope_chain_item == refl.scope
+ if scope_chain_item == chain_head.scope
scope.merge! item.except(:where, :includes)
end
@@ -158,8 +146,6 @@ module ActiveRecord
scope.where_clause += item.where_clause
scope.order_values |= item.order_values
end
-
- reflection = next_reflection
end
scope
@@ -173,8 +159,9 @@ module ActiveRecord
end
end
- def eval_scope(reflection, table, scope, owner)
- reflection.build_scope(table).instance_exec(owner, &scope)
+ def eval_scope(reflection, scope, owner)
+ relation = reflection.build_scope(reflection.aliased_table)
+ relation.instance_exec(owner, &scope) || relation
end
end
end
diff --git a/activerecord/lib/active_record/associations/builder/association.rb b/activerecord/lib/active_record/associations/builder/association.rb
index 496b16b58f..ca3032d967 100644
--- a/activerecord/lib/active_record/associations/builder/association.rb
+++ b/activerecord/lib/active_record/associations/builder/association.rb
@@ -38,11 +38,6 @@ module ActiveRecord::Associations::Builder # :nodoc:
def self.create_reflection(model, name, scope, options, extension = nil)
raise ArgumentError, "association names must be a Symbol" unless name.kind_of?(Symbol)
- if scope.is_a?(Hash)
- options = scope
- scope = nil
- end
-
validate_options(options)
scope = build_scope(scope, extension)
diff --git a/activerecord/lib/active_record/associations/builder/collection_association.rb b/activerecord/lib/active_record/associations/builder/collection_association.rb
index 753fde5146..35a72c3850 100644
--- a/activerecord/lib/active_record/associations/builder/collection_association.rb
+++ b/activerecord/lib/active_record/associations/builder/collection_association.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require_relative "../../associations"
+require "active_record/associations"
module ActiveRecord::Associations::Builder # :nodoc:
class CollectionAssociation < Association #:nodoc:
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index ceedf150e3..ed215fb22c 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -181,8 +181,6 @@ module ActiveRecord
# are actually removed from the database, that depends precisely on
# +delete_records+. They are in any case removed from the collection.
def delete(*records)
- return if records.empty?
- records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
delete_or_destroy(records, options[:dependent])
end
@@ -192,8 +190,6 @@ module ActiveRecord
# Note that this method removes records from the database ignoring the
# +:dependent+ option.
def destroy(*records)
- return if records.empty?
- records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
delete_or_destroy(records, :destroy)
end
@@ -376,6 +372,8 @@ module ActiveRecord
end
def delete_or_destroy(records, method)
+ return if records.empty?
+ records = find(records) if records.any? { |record| record.kind_of?(Integer) || record.kind_of?(String) }
records = records.flatten
records.each { |record| raise_on_type_mismatch!(record) }
existing_records = records.reject(&:new_record?)
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index 412e89255d..07f7303f8d 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -1072,7 +1072,6 @@ module ActiveRecord
end
# Reloads the collection from the database. Returns +self+.
- # Equivalent to <tt>collection(true)</tt>.
#
# class Person < ActiveRecord::Base
# has_many :pets
@@ -1086,9 +1085,6 @@ module ActiveRecord
#
# person.pets.reload # fetches pets from the database
# # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
- #
- # person.pets(true) # fetches pets from the database
- # # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
def reload
proxy_association.reload
reset_scope
diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb
index ac37c3c015..df4bf07999 100644
--- a/activerecord/lib/active_record/associations/join_dependency.rb
+++ b/activerecord/lib/active_record/associations/join_dependency.rb
@@ -43,8 +43,6 @@ module ActiveRecord
Column = Struct.new(:name, :alias)
end
- attr_reader :alias_tracker, :base_klass, :join_root
-
def self.make_tree(associations)
hash = {}
walk_tree associations, hash
@@ -90,8 +88,8 @@ module ActiveRecord
# associations # => [:appointments]
# joins # => []
#
- def initialize(base, table, associations, joins, eager_loading: true)
- @alias_tracker = AliasTracker.create_with_joins(base.connection, base.table_name, joins)
+ def initialize(base, table, associations, alias_tracker, eager_loading: true)
+ @alias_tracker = alias_tracker
@eager_loading = eager_loading
tree = self.class.make_tree associations
@join_root = JoinBase.new(base, table, build(tree, base))
@@ -158,6 +156,9 @@ module ActiveRecord
parents.values
end
+ protected
+ attr_reader :alias_tracker, :base_klass, :join_root
+
private
def make_constraints(parent, child, tables, join_type)
@@ -224,7 +225,7 @@ module ActiveRecord
raise EagerLoadPolymorphicError.new(reflection)
end
- JoinAssociation.new reflection, build(right, reflection.klass)
+ JoinAssociation.new(reflection, build(right, reflection.klass), alias_tracker)
end.compact
end
@@ -256,7 +257,8 @@ module ActiveRecord
else
model = construct_model(ar_parent, node, row, model_cache, id, aliases)
- if node.reflection.scope_for(node.base_klass).readonly_value
+ if node.reflection.scope &&
+ node.reflection.scope_for(node.base_klass.unscoped).readonly_value
model.readonly!
end
diff --git a/activerecord/lib/active_record/associations/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/join_dependency/join_association.rb
index a526468bf6..221c791bf8 100644
--- a/activerecord/lib/active_record/associations/join_dependency/join_association.rb
+++ b/activerecord/lib/active_record/associations/join_dependency/join_association.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require_relative "join_part"
+require "active_record/associations/join_dependency/join_part"
module ActiveRecord
module Associations
@@ -11,11 +11,12 @@ module ActiveRecord
attr_accessor :tables
- def initialize(reflection, children)
+ def initialize(reflection, children, alias_tracker)
super(reflection.klass, children)
- @reflection = reflection
- @tables = nil
+ @alias_tracker = alias_tracker
+ @reflection = reflection
+ @tables = nil
end
def match?(other)
@@ -38,11 +39,12 @@ module ActiveRecord
joins << table.create_join(table, table.create_on(constraint), join_type)
join_scope = reflection.join_scope(table, foreign_klass)
+ arel = join_scope.arel(alias_tracker.aliases)
- if join_scope.arel.constraints.any?
- joins.concat join_scope.arel.join_sources
+ if arel.constraints.any?
+ joins.concat arel.join_sources
right = joins.last.right
- right.expr = right.expr.and(join_scope.arel.constraints)
+ right.expr = right.expr.and(arel.constraints)
end
# The current table in this iteration becomes the foreign table in the next
@@ -55,6 +57,9 @@ module ActiveRecord
def table
tables.first
end
+
+ protected
+ attr_reader :alias_tracker
end
end
end
diff --git a/activerecord/lib/active_record/associations/join_dependency/join_base.rb b/activerecord/lib/active_record/associations/join_dependency/join_base.rb
index 8a8fa8993b..988b4e8fa2 100644
--- a/activerecord/lib/active_record/associations/join_dependency/join_base.rb
+++ b/activerecord/lib/active_record/associations/join_dependency/join_base.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require_relative "join_part"
+require "active_record/associations/join_dependency/join_part"
module ActiveRecord
module Associations
diff --git a/activerecord/lib/active_record/associations/preloader.rb b/activerecord/lib/active_record/associations/preloader.rb
index 62caf02a2c..e1754d4a19 100644
--- a/activerecord/lib/active_record/associations/preloader.rb
+++ b/activerecord/lib/active_record/associations/preloader.rb
@@ -91,13 +91,13 @@ module ActiveRecord
# { author: :avatar }
# [ :books, { author: :avatar } ]
def preload(records, associations, preload_scope = nil)
- records = Array.wrap(records).compact.uniq
- associations = Array.wrap(associations)
+ records = records.compact
if records.empty?
[]
else
- associations.flat_map { |association|
+ records.uniq!
+ Array.wrap(associations).flat_map { |association|
preloaders_on association, records, preload_scope
}
end
diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb
index 4915a37f06..607d376a08 100644
--- a/activerecord/lib/active_record/associations/preloader/association.rb
+++ b/activerecord/lib/active_record/associations/preloader/association.rb
@@ -17,26 +17,20 @@ module ActiveRecord
end
def run(preloader)
- preload(preloader)
- end
-
- def preload(preloader)
- raise NotImplementedError
- end
-
- # The name of the key on the associated records
- def association_key_name
- raise NotImplementedError
- end
-
- # The name of the key on the model which declares the association
- def owner_key_name
- raise NotImplementedError
+ associated_records_by_owner(preloader).each do |owner, records|
+ associate_records_to_owner(owner, records)
+ end
end
private
- def options
- reflection.options
+ # The name of the key on the associated records
+ def association_key_name
+ reflection.join_primary_key(klass)
+ end
+
+ # The name of the key on the model which declares the association
+ def owner_key_name
+ reflection.join_foreign_key
end
def associated_records_by_owner(preloader)
@@ -51,28 +45,30 @@ module ActiveRecord
end
end
+ def associate_records_to_owner(owner, records)
+ raise NotImplementedError
+ end
+
def owner_keys
- unless defined?(@owner_keys)
- @owner_keys = owners.map do |owner|
- owner[owner_key_name]
- end
- @owner_keys.uniq!
- @owner_keys.compact!
- end
- @owner_keys
+ @owner_keys ||= owners_by_key.keys
end
def owners_by_key
unless defined?(@owners_by_key)
@owners_by_key = owners.each_with_object({}) do |owner, h|
- h[convert_key(owner[owner_key_name])] = owner
+ key = convert_key(owner[owner_key_name])
+ h[key] = owner if key
end
end
@owners_by_key
end
def key_conversion_required?
- @key_conversion_required ||= association_key_type != owner_key_type
+ unless defined?(@key_conversion_required)
+ @key_conversion_required = (association_key_type != owner_key_type)
+ end
+
+ @key_conversion_required
end
def convert_key(key)
@@ -113,7 +109,7 @@ module ActiveRecord
end
def reflection_scope
- @reflection_scope ||= reflection.scope_for(klass)
+ @reflection_scope ||= reflection.scope ? reflection.scope_for(klass.unscoped) : klass.unscoped
end
def build_scope
@@ -123,7 +119,7 @@ module ActiveRecord
scope.where!(reflection.type => model.base_class.sti_name)
end
- scope.merge!(reflection_scope)
+ scope.merge!(reflection_scope) if reflection.scope
scope.merge!(preload_scope) if preload_scope
scope
end
diff --git a/activerecord/lib/active_record/associations/preloader/belongs_to.rb b/activerecord/lib/active_record/associations/preloader/belongs_to.rb
index ae9695f26a..a8e3340b23 100644
--- a/activerecord/lib/active_record/associations/preloader/belongs_to.rb
+++ b/activerecord/lib/active_record/associations/preloader/belongs_to.rb
@@ -4,13 +4,6 @@ module ActiveRecord
module Associations
class Preloader
class BelongsTo < SingularAssociation #:nodoc:
- def association_key_name
- options[:primary_key] || klass && klass.primary_key
- end
-
- def owner_key_name
- reflection.foreign_key
- end
end
end
end
diff --git a/activerecord/lib/active_record/associations/preloader/collection_association.rb b/activerecord/lib/active_record/associations/preloader/collection_association.rb
index fb920a642c..fc2029f54a 100644
--- a/activerecord/lib/active_record/associations/preloader/collection_association.rb
+++ b/activerecord/lib/active_record/associations/preloader/collection_association.rb
@@ -5,13 +5,10 @@ module ActiveRecord
class Preloader
class CollectionAssociation < Association #:nodoc:
private
-
- def preload(preloader)
- associated_records_by_owner(preloader).each do |owner, records|
- association = owner.association(reflection.name)
- association.loaded!
- association.target.concat(records)
- end
+ def associate_records_to_owner(owner, records)
+ association = owner.association(reflection.name)
+ association.loaded!
+ association.target.concat(records)
end
end
end
diff --git a/activerecord/lib/active_record/associations/preloader/has_many.rb b/activerecord/lib/active_record/associations/preloader/has_many.rb
index 29a1ce099d..72f55bc43f 100644
--- a/activerecord/lib/active_record/associations/preloader/has_many.rb
+++ b/activerecord/lib/active_record/associations/preloader/has_many.rb
@@ -4,13 +4,6 @@ module ActiveRecord
module Associations
class Preloader
class HasMany < CollectionAssociation #:nodoc:
- def association_key_name
- reflection.foreign_key
- end
-
- def owner_key_name
- reflection.active_record_primary_key
- end
end
end
end
diff --git a/activerecord/lib/active_record/associations/preloader/has_one.rb b/activerecord/lib/active_record/associations/preloader/has_one.rb
index d87abf630f..e339b65fb5 100644
--- a/activerecord/lib/active_record/associations/preloader/has_one.rb
+++ b/activerecord/lib/active_record/associations/preloader/has_one.rb
@@ -4,13 +4,6 @@ module ActiveRecord
module Associations
class Preloader
class HasOne < SingularAssociation #:nodoc:
- def association_key_name
- reflection.foreign_key
- end
-
- def owner_key_name
- reflection.active_record_primary_key
- end
end
end
end
diff --git a/activerecord/lib/active_record/associations/preloader/singular_association.rb b/activerecord/lib/active_record/associations/preloader/singular_association.rb
index 266b5f6b1c..30a92411e3 100644
--- a/activerecord/lib/active_record/associations/preloader/singular_association.rb
+++ b/activerecord/lib/active_record/associations/preloader/singular_association.rb
@@ -5,14 +5,9 @@ module ActiveRecord
class Preloader
class SingularAssociation < Association #:nodoc:
private
-
- def preload(preloader)
- associated_records_by_owner(preloader).each do |owner, associated_records|
- record = associated_records.first
-
- association = owner.association(reflection.name)
- association.target = record
- end
+ def associate_records_to_owner(owner, records)
+ association = owner.association(reflection.name)
+ association.target = records.first
end
end
end
diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb
index de4b847a41..fa32cc5553 100644
--- a/activerecord/lib/active_record/associations/preloader/through_association.rb
+++ b/activerecord/lib/active_record/associations/preloader/through_association.rb
@@ -28,6 +28,8 @@ module ActiveRecord
middle_records = through_records.flat_map(&:last)
+ reflection_scope = reflection_scope() if reflection.scope
+
preloaders = preloader.preload(middle_records,
source_reflection.name,
reflection_scope)
@@ -49,7 +51,7 @@ module ActiveRecord
}.compact
# Respect the order on `reflection_scope` if it exists, else use the natural order.
- if reflection_scope.values[:order].present?
+ if reflection_scope && !reflection_scope.order_values.empty?
@id_map ||= id_to_index_map @preloaded_records
rhs_records.sort_by { |rhs| @id_map[rhs] }
else
@@ -67,10 +69,7 @@ module ActiveRecord
id_map
end
- def reset_association(owners, association_name, through_scope)
- should_reset = (through_scope != through_reflection.klass.unscoped) ||
- (options[:source_type] && through_reflection.collection?)
-
+ def reset_association(owners, association_name, should_reset)
# Don't cache the association - we would only be caching a subset
if should_reset
owners.each { |owner|
@@ -81,29 +80,40 @@ module ActiveRecord
def through_scope
scope = through_reflection.klass.unscoped
- values = reflection_scope.values
+ options = reflection.options
if options[:source_type]
scope.where! reflection.foreign_type => options[:source_type]
- else
- unless reflection_scope.where_clause.empty?
- scope.includes_values = Array(values[:includes] || options[:source])
- scope.where_clause = reflection_scope.where_clause
- if joins = values[:joins]
- scope.joins!(source_reflection.name => joins)
- end
- if left_outer_joins = values[:left_outer_joins]
- scope.left_outer_joins!(source_reflection.name => left_outer_joins)
- end
+ elsif !reflection_scope.where_clause.empty?
+ scope.where_clause = reflection_scope.where_clause
+ values = reflection_scope.values
+
+ if includes = values[:includes]
+ scope.includes!(source_reflection.name => includes)
+ else
+ scope.includes!(source_reflection.name)
+ end
+
+ if values[:references] && !values[:references].empty?
+ scope.references!(values[:references])
+ else
+ scope.references!(source_reflection.table_name)
+ end
+
+ if joins = values[:joins]
+ scope.joins!(source_reflection.name => joins)
+ end
+
+ if left_outer_joins = values[:left_outer_joins]
+ scope.left_outer_joins!(source_reflection.name => left_outer_joins)
end
- scope.references! values[:references]
if scope.eager_loading? && order_values = values[:order]
scope = scope.order(order_values)
end
end
- scope
+ scope unless scope.empty_scope?
end
end
end
diff --git a/activerecord/lib/active_record/attribute/user_provided_default.rb b/activerecord/lib/active_record/attribute/user_provided_default.rb
index 690a931615..f746960fac 100644
--- a/activerecord/lib/active_record/attribute/user_provided_default.rb
+++ b/activerecord/lib/active_record/attribute/user_provided_default.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require_relative "../attribute"
+require "active_record/attribute"
module ActiveRecord
class Attribute # :nodoc:
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index e4ca6c8408..891e556dc4 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -33,7 +33,9 @@ module ActiveRecord
BLACKLISTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
- class GeneratedAttributeMethods < Module; end # :nodoc:
+ class GeneratedAttributeMethods < Module #:nodoc:
+ include Mutex_m
+ end
module ClassMethods
def inherited(child_class) #:nodoc:
@@ -42,7 +44,7 @@ module ActiveRecord
end
def initialize_generated_modules # :nodoc:
- @generated_attribute_methods = GeneratedAttributeMethods.new { extend Mutex_m }
+ @generated_attribute_methods = GeneratedAttributeMethods.new
@attribute_methods_generated = false
include @generated_attribute_methods
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb
index 4f957ac3ca..79110d04f4 100644
--- a/activerecord/lib/active_record/attribute_methods/dirty.rb
+++ b/activerecord/lib/active_record/attribute_methods/dirty.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "active_support/core_ext/module/attribute_accessors"
-require_relative "../attribute_mutation_tracker"
+require "active_record/attribute_mutation_tracker"
module ActiveRecord
module AttributeMethods
@@ -34,7 +34,7 @@ module ActiveRecord
def reload(*)
super.tap do
@mutations_before_last_save = nil
- clear_mutation_trackers
+ @mutations_from_database = nil
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
end
end
@@ -44,22 +44,21 @@ module ActiveRecord
@attributes = self.class._default_attributes.map do |attr|
attr.with_value_from_user(@attributes.fetch_value(attr.name))
end
- clear_mutation_trackers
+ @mutations_from_database = nil
end
def changes_applied # :nodoc:
- @mutations_before_last_save = mutation_tracker
- @mutations_from_database = AttributeMutationTracker.new(@attributes)
+ @mutations_before_last_save = mutations_from_database
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
forget_attribute_assignments
- clear_mutation_trackers
+ @mutations_from_database = nil
end
def clear_changes_information # :nodoc:
@mutations_before_last_save = nil
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
forget_attribute_assignments
- clear_mutation_trackers
+ @mutations_from_database = nil
end
def clear_attribute_changes(attr_names) # :nodoc:
@@ -75,7 +74,7 @@ module ActiveRecord
if defined?(@cached_changed_attributes)
@cached_changed_attributes
else
- super.reverse_merge(mutation_tracker.changed_values).freeze
+ super.reverse_merge(mutations_from_database.changed_values).freeze
end
end
@@ -90,7 +89,7 @@ module ActiveRecord
end
def attribute_changed_in_place?(attr_name) # :nodoc:
- mutation_tracker.changed_in_place?(attr_name)
+ mutations_from_database.changed_in_place?(attr_name)
end
# Did this attribute change when we last saved? This method can be invoked
@@ -183,26 +182,18 @@ module ActiveRecord
result
end
- def mutation_tracker
- unless defined?(@mutation_tracker)
- @mutation_tracker = nil
- end
- @mutation_tracker ||= AttributeMutationTracker.new(@attributes)
- end
-
def mutations_from_database
unless defined?(@mutations_from_database)
@mutations_from_database = nil
end
- @mutations_from_database ||= mutation_tracker
+ @mutations_from_database ||= AttributeMutationTracker.new(@attributes)
end
def changes_include?(attr_name)
- super || mutation_tracker.changed?(attr_name)
+ super || mutations_from_database.changed?(attr_name)
end
def clear_attribute_change(attr_name)
- mutation_tracker.forget_change(attr_name)
mutations_from_database.forget_change(attr_name)
end
@@ -227,11 +218,6 @@ module ActiveRecord
@attributes = @attributes.map(&:forgetting_assignment)
end
- def clear_mutation_trackers
- @mutation_tracker = nil
- @mutations_from_database = nil
- end
-
def mutations_before_last_save
@mutations_before_last_save ||= NullMutationTracker.instance
end
diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb
index 63c059e291..d8fc046e10 100644
--- a/activerecord/lib/active_record/attribute_methods/primary_key.rb
+++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb
@@ -17,13 +17,15 @@ module ActiveRecord
# Returns the primary key value.
def id
sync_with_transaction_state
- _read_attribute(self.class.primary_key) if self.class.primary_key
+ primary_key = self.class.primary_key
+ _read_attribute(primary_key) if primary_key
end
# Sets the primary key value.
def id=(value)
sync_with_transaction_state
- _write_attribute(self.class.primary_key, value) if self.class.primary_key
+ primary_key = self.class.primary_key
+ _write_attribute(primary_key, value) if primary_key
end
# Queries the primary key value.
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index b070235684..4077250583 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -58,8 +58,9 @@ module ActiveRecord
attr_name.to_s
end
- name = self.class.primary_key if name == "id".freeze && self.class.primary_key
- sync_with_transaction_state if name == self.class.primary_key
+ primary_key = self.class.primary_key
+ name = primary_key if name == "id".freeze && primary_key
+ sync_with_transaction_state if name == primary_key
_read_attribute(name, &block)
end
diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
index 37891ce2ef..bb0ec6a8c3 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -39,8 +39,9 @@ module ActiveRecord
attr_name.to_s
end
- name = self.class.primary_key if name == "id".freeze && self.class.primary_key
- sync_with_transaction_state if name == self.class.primary_key
+ primary_key = self.class.primary_key
+ name = primary_key if name == "id".freeze && primary_key
+ sync_with_transaction_state if name == primary_key
_write_attribute(name, value)
end
diff --git a/activerecord/lib/active_record/attribute_set.rb b/activerecord/lib/active_record/attribute_set.rb
index 492067e2b3..9785666e77 100644
--- a/activerecord/lib/active_record/attribute_set.rb
+++ b/activerecord/lib/active_record/attribute_set.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-require_relative "attribute_set/builder"
-require_relative "attribute_set/yaml_encoder"
+require "active_record/attribute_set/builder"
+require "active_record/attribute_set/yaml_encoder"
module ActiveRecord
class AttributeSet # :nodoc:
diff --git a/activerecord/lib/active_record/attribute_set/builder.rb b/activerecord/lib/active_record/attribute_set/builder.rb
index e3a9c7fdb3..349cc7e403 100644
--- a/activerecord/lib/active_record/attribute_set/builder.rb
+++ b/activerecord/lib/active_record/attribute_set/builder.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require_relative "../attribute"
+require "active_record/attribute"
module ActiveRecord
class AttributeSet # :nodoc:
diff --git a/activerecord/lib/active_record/attributes.rb b/activerecord/lib/active_record/attributes.rb
index afb559db71..9224d58928 100644
--- a/activerecord/lib/active_record/attributes.rb
+++ b/activerecord/lib/active_record/attributes.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require_relative "attribute/user_provided_default"
+require "active_record/attribute/user_provided_default"
module ActiveRecord
# See ActiveRecord::Attributes::ClassMethods for documentation
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 541ff51fbe..b7ad944cec 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -15,14 +15,14 @@ require "active_support/core_ext/kernel/singleton_class"
require "active_support/core_ext/module/introspection"
require "active_support/core_ext/object/duplicable"
require "active_support/core_ext/class/subclasses"
-require_relative "attribute_decorators"
-require_relative "define_callbacks"
-require_relative "errors"
-require_relative "log_subscriber"
-require_relative "explain_subscriber"
-require_relative "relation/delegation"
-require_relative "attributes"
-require_relative "type_caster"
+require "active_record/attribute_decorators"
+require "active_record/define_callbacks"
+require "active_record/errors"
+require "active_record/log_subscriber"
+require "active_record/explain_subscriber"
+require "active_record/relation/delegation"
+require "active_record/attributes"
+require "active_record/type_caster"
module ActiveRecord #:nodoc:
# = Active Record
diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb
index a2439e6ec7..9ab2780760 100644
--- a/activerecord/lib/active_record/callbacks.rb
+++ b/activerecord/lib/active_record/callbacks.rb
@@ -98,9 +98,9 @@ module ActiveRecord
# == Types of callbacks
#
# There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects,
- # inline methods (using a proc), and inline eval methods (using a string). Method references and callback objects
+ # inline methods (using a proc). Method references and callback objects
# are the recommended approaches, inline methods using a proc are sometimes appropriate (such as for
- # creating mix-ins), and inline eval methods are deprecated.
+ # creating mix-ins).
#
# The method reference callbacks work by specifying a protected or private method available in the object, like this:
#
@@ -240,7 +240,7 @@ module ActiveRecord
#
# private
#
- # def log_chidren
+ # def log_children
# # Child processing
# end
#
@@ -265,7 +265,7 @@ module ActiveRecord
#
# private
#
- # def log_chidren
+ # def log_children
# # Child processing
# end
#
diff --git a/activerecord/lib/active_record/collection_cache_key.rb b/activerecord/lib/active_record/collection_cache_key.rb
index b1937a3c68..f3e6516414 100644
--- a/activerecord/lib/active_record/collection_cache_key.rb
+++ b/activerecord/lib/active_record/collection_cache_key.rb
@@ -13,7 +13,7 @@ module ActiveRecord
end
else
column_type = type_for_attribute(timestamp_column.to_s)
- column = "#{connection.quote_table_name(collection.table_name)}.#{connection.quote_column_name(timestamp_column)}"
+ column = connection.column_name_from_arel_node(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/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index 9ad04c3216..92e46ccf9f 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -11,19 +11,6 @@ module ActiveRecord
def quote(value)
value = id_value_for_database(value) if value.is_a?(Base)
- if value.respond_to?(:quoted_id)
- at = value.method(:quoted_id).source_location
- at &&= " at %s:%d" % at
-
- owner = value.method(:quoted_id).owner.to_s
- klass = value.class.to_s
- klass += "(#{owner})" unless owner == klass
-
- ActiveSupport::Deprecation.warn \
- "Defining #quoted_id is deprecated and will be ignored in Rails 5.2. (defined on #{klass}#{at})"
- return value.quoted_id
- end
-
if value.respond_to?(:value_for_database)
value = value.value_for_database
end
@@ -37,10 +24,6 @@ module ActiveRecord
def type_cast(value, column = nil)
value = id_value_for_database(value) if value.is_a?(Base)
- if value.respond_to?(:quoted_id) && value.respond_to?(:id)
- return value.id
- end
-
if column
value = type_cast_from_column(column, value)
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
index 8bf3879a4c..bd05fb8f6e 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb
@@ -62,7 +62,7 @@ module ActiveRecord
end
def visit_PrimaryKeyDefinition(o)
- "PRIMARY KEY (#{o.name.join(', ')})"
+ "PRIMARY KEY (#{o.name.map { |name| quote_column_name(name) }.join(', ')})"
end
def visit_ForeignKeyDefinition(o)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
index 788a455773..be2f625d74 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb
@@ -396,6 +396,9 @@ module ActiveRecord
alias :belongs_to :references
def new_column_definition(name, type, **options) # :nodoc:
+ if integer_like_primary_key?(type, options)
+ type = integer_like_primary_key_type(type, options)
+ end
type = aliased_types(type.to_s, type)
options[:primary_key] ||= type == :primary_key
options[:null] = false if options[:primary_key]
@@ -410,6 +413,14 @@ module ActiveRecord
def aliased_types(name, fallback)
"timestamp" == name ? :datetime : fallback
end
+
+ def integer_like_primary_key?(type, options)
+ options[:primary_key] && [:integer, :bigint].include?(type) && !options.key?(:default)
+ end
+
+ def integer_like_primary_key_type(type, options)
+ type
+ end
end
class AlterTable # :nodoc:
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 f57c7a5d4d..6698047ad6 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
-require_relative "../../migration/join_table"
+require "active_record/migration/join_table"
require "active_support/core_ext/string/access"
-require "digest"
+require "digest/sha2"
module ActiveRecord
module ConnectionAdapters # :nodoc:
@@ -79,7 +79,7 @@ module ActiveRecord
end
# Returns an array of indexes for the given table.
- def indexes(table_name, name = nil)
+ def indexes(table_name)
raise NotImplementedError, "#indexes is not implemented"
end
@@ -522,6 +522,8 @@ module ActiveRecord
# Specifies the precision for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
# * <tt>:scale</tt> -
# Specifies the scale for the <tt>:decimal</tt> and <tt>:numeric</tt> columns.
+ # * <tt>:comment</tt> -
+ # Specifies the comment for the column. This option is ignored by some backends.
#
# Note: The precision is the total number of significant digits,
# and the scale is the number of digits that can be stored following
@@ -804,12 +806,7 @@ module ActiveRecord
end
# Verifies the existence of an index with a given name.
- def index_name_exists?(table_name, index_name, default = nil)
- unless default.nil?
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- Passing default to #index_name_exists? is deprecated without replacement.
- MSG
- end
+ def index_name_exists?(table_name, index_name)
index_name = index_name.to_s
indexes(table_name).detect { |i| i.name == index_name }
end
@@ -1018,16 +1015,6 @@ module ActiveRecord
insert_versions_sql(versions) if versions.any?
end
- def initialize_schema_migrations_table # :nodoc:
- ActiveRecord::SchemaMigration.create_table
- end
- deprecate :initialize_schema_migrations_table
-
- def initialize_internal_metadata_table # :nodoc:
- ActiveRecord::InternalMetadata.create_table
- end
- deprecate :initialize_internal_metadata_table
-
def internal_string_options_for_primary_key # :nodoc:
{ primary_key: true }
end
@@ -1172,7 +1159,7 @@ module ActiveRecord
end
# Changes the comment for a column or removes it if +nil+.
- def change_column_comment(table_name, column_name, comment) #:nodoc:
+ def change_column_comment(table_name, column_name, comment)
raise NotImplementedError, "#{self.class} does not support changing column comments"
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index 47881e3305..e3aab8dad8 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -1,11 +1,11 @@
# frozen_string_literal: true
-require_relative "../type"
-require_relative "determine_if_preparable_visitor"
-require_relative "schema_cache"
-require_relative "sql_type_metadata"
-require_relative "abstract/schema_dumper"
-require_relative "abstract/schema_creation"
+require "active_record/type"
+require "active_record/connection_adapters/determine_if_preparable_visitor"
+require "active_record/connection_adapters/schema_cache"
+require "active_record/connection_adapters/sql_type_metadata"
+require "active_record/connection_adapters/abstract/schema_dumper"
+require "active_record/connection_adapters/abstract/schema_creation"
require "arel/collectors/bind"
require "arel/collectors/composite"
require "arel/collectors/sql_string"
@@ -196,16 +196,6 @@ module ActiveRecord
self.class::ADAPTER_NAME
end
- def supports_migrations? # :nodoc:
- true
- end
- deprecate :supports_migrations?
-
- def supports_primary_key? # :nodoc:
- true
- end
- deprecate :supports_primary_key?
-
# Does this adapter support DDL rollbacks in transactions? That is, would
# CREATE TABLE or ALTER TABLE get rolled back by a transaction?
def supports_ddl_transactions?
@@ -402,10 +392,7 @@ module ActiveRecord
# Checks whether the connection to the database is still active (i.e. not stale).
# This is done under the hood by calling #active?. If the connection
# is no longer active, then this method will reconnect to the database.
- def verify!(*ignored)
- if ignored.size > 0
- ActiveSupport::Deprecation.warn("Passing arguments to #verify method of the connection has no effect and has been deprecated. Please remove all arguments from the #verify method call.")
- end
+ def verify!
reconnect! unless active?
end
@@ -442,7 +429,11 @@ module ActiveRecord
end
def column_name_for_operation(operation, node) # :nodoc:
- visitor.accept(node, collector).value
+ column_name_from_arel_node(node)
+ end
+
+ def column_name_from_arel_node(node) # :nodoc:
+ visitor.accept(node, Arel::Collectors::SQLString.new).value
end
def default_index_type?(index) # :nodoc:
@@ -474,6 +465,8 @@ module ActiveRecord
m.alias_type %r(number)i, "decimal"
m.alias_type %r(double)i, "float"
+ m.register_type %r(^json)i, Type::Json.new
+
m.register_type(%r(decimal)i) do |sql_type|
scale = extract_scale(sql_type)
precision = extract_precision(sql_type)
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 7cd086084a..bfec6fb784 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -1,15 +1,15 @@
# frozen_string_literal: true
-require_relative "abstract_adapter"
-require_relative "statement_pool"
-require_relative "mysql/column"
-require_relative "mysql/explain_pretty_printer"
-require_relative "mysql/quoting"
-require_relative "mysql/schema_creation"
-require_relative "mysql/schema_definitions"
-require_relative "mysql/schema_dumper"
-require_relative "mysql/schema_statements"
-require_relative "mysql/type_metadata"
+require "active_record/connection_adapters/abstract_adapter"
+require "active_record/connection_adapters/statement_pool"
+require "active_record/connection_adapters/mysql/column"
+require "active_record/connection_adapters/mysql/explain_pretty_printer"
+require "active_record/connection_adapters/mysql/quoting"
+require "active_record/connection_adapters/mysql/schema_creation"
+require "active_record/connection_adapters/mysql/schema_definitions"
+require "active_record/connection_adapters/mysql/schema_dumper"
+require "active_record/connection_adapters/mysql/schema_statements"
+require "active_record/connection_adapters/mysql/type_metadata"
require "active_support/core_ext/string/strip"
@@ -284,7 +284,7 @@ module ActiveRecord
def table_comment(table_name) # :nodoc:
scope = quoted_scope(table_name)
- query_value(<<-SQL.strip_heredoc, "SCHEMA")
+ query_value(<<-SQL.strip_heredoc, "SCHEMA").presence
SELECT table_comment
FROM information_schema.tables
WHERE table_schema = #{scope[:schema]}
@@ -311,6 +311,11 @@ module ActiveRecord
execute("ALTER TABLE #{quote_table_name(table_name)} #{sqls}")
end
+ def change_table_comment(table_name, comment) #:nodoc:
+ comment = "" if comment.nil?
+ execute("ALTER TABLE #{quote_table_name(table_name)} COMMENT #{quote(comment)}")
+ end
+
# Renames a table.
#
# Example:
@@ -351,18 +356,19 @@ module ActiveRecord
def change_column_default(table_name, column_name, default_or_changes) #:nodoc:
default = extract_new_default_value(default_or_changes)
- column = column_for(table_name, column_name)
- change_column table_name, column_name, column.sql_type, default: default
+ change_column table_name, column_name, nil, default: default
end
def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
- column = column_for(table_name, column_name)
-
unless null || default.nil?
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
end
- change_column table_name, column_name, column.sql_type, null: null
+ change_column table_name, column_name, nil, null: null
+ end
+
+ def change_column_comment(table_name, column_name, comment) #:nodoc:
+ change_column table_name, column_name, nil, comment: comment
end
def change_column(table_name, column_name, type, options = {}) #:nodoc:
@@ -557,7 +563,6 @@ module ActiveRecord
m.register_type %r(longblob)i, Type::Binary.new(limit: 2**32 - 1)
m.register_type %r(^float)i, Type::Float.new(limit: 24)
m.register_type %r(^double)i, Type::Float.new(limit: 53)
- m.register_type %r(^json)i, Type::Json.new
register_integer_type m, %r(^bigint)i, limit: 8
register_integer_type m, %r(^int)i, limit: 4
@@ -668,6 +673,7 @@ module ActiveRecord
def change_column_sql(table_name, column_name, type, options = {})
column = column_for(table_name, column_name)
+ type ||= column.sql_type
unless options.key?(:default)
options[:default] = column.default
@@ -716,7 +722,7 @@ module ActiveRecord
def remove_index_sql(table_name, options = {})
index_name = index_name_for_remove(table_name, options)
- "DROP INDEX #{index_name}"
+ "DROP INDEX #{quote_column_name(index_name)}"
end
def add_timestamps_sql(table_name, options = {})
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index 16273fb5f1..28d949b503 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -15,7 +15,7 @@ module ActiveRecord
# +default+ is the type-casted default value, such as +new+ in <tt>sales_stage varchar(20) default 'new'</tt>.
# +sql_type_metadata+ is various information about the type of the column
# +null+ determines if this column allows +NULL+ values.
- def initialize(name, default, sql_type_metadata = nil, null = true, table_name = nil, default_function = nil, collation = nil, comment: nil)
+ def initialize(name, default, sql_type_metadata = nil, null = true, table_name = nil, default_function = nil, collation = nil, comment: nil, **)
@name = name.freeze
@table_name = table_name
@sql_type_metadata = sql_type_metadata
diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
index 29542f917e..508132accb 100644
--- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb
+++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
@@ -183,13 +183,25 @@ module ActiveRecord
raise(AdapterNotSpecified, "database configuration does not specify adapter") unless spec.key?(:adapter)
+ # Require the adapter itself and give useful feedback about
+ # 1. Missing adapter gems and
+ # 2. Adapter gems' missing dependencies.
path_to_adapter = "active_record/connection_adapters/#{spec[:adapter]}_adapter"
begin
require path_to_adapter
- rescue Gem::LoadError => e
- raise Gem::LoadError, "Specified '#{spec[:adapter]}' for database adapter, but the gem is not loaded. Add `gem '#{e.name}'` to your Gemfile (and ensure its version is at the minimum required by ActiveRecord)."
rescue LoadError => e
- raise LoadError, "Could not load '#{path_to_adapter}'. Make sure that the adapter in config/database.yml is valid. If you use an adapter other than 'mysql2', 'postgresql' or 'sqlite3' add the necessary adapter gem to the Gemfile.", e.backtrace
+ # We couldn't require the adapter itself. Raise an exception that
+ # points out config typos and missing gems.
+ if e.path == path_to_adapter
+ # We can assume that a non-builtin adapter was specified, so it's
+ # either misspelled or missing from Gemfile.
+ raise e.class, "Could not load the '#{spec[:adapter]}' Active Record adapter. Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary adapter gem to your Gemfile.", e.backtrace
+
+ # Bubbled up from the adapter require. Prefix the exception message
+ # with some guidance about how to address it and reraise.
+ else
+ raise e.class, "Error loading the '#{spec[:adapter]}' Active Record adapter. Missing a gem it depends on? #{e.message}", e.backtrace
+ end
end
adapter_method = "#{spec[:adapter]}_connection"
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb
index b22a2e4da7..da25e4863c 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_definitions.rb
@@ -4,11 +4,6 @@ module ActiveRecord
module ConnectionAdapters
module MySQL
module ColumnMethods
- def primary_key(name, type = :primary_key, **options)
- options[:auto_increment] = true if [:integer, :bigint].include?(type) && !options.key?(:default)
- super
- end
-
def blob(*args, **options)
args.each { |name| column(name, :blob, options) }
end
@@ -68,7 +63,6 @@ module ActiveRecord
when :primary_key
type = :integer
options[:limit] ||= 8
- options[:auto_increment] = true
options[:primary_key] = true
when /\Aunsigned_(?<type>.+)\z/
type = $~[:type].to_sym
@@ -82,6 +76,11 @@ module ActiveRecord
def aliased_types(name, fallback)
fallback
end
+
+ def integer_like_primary_key_type(type, options)
+ options[:auto_increment] = true
+ type
+ end
end
class Table < ActiveRecord::ConnectionAdapters::Table
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
index 95eb77aea4..d23178e43c 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_dumper.rb
@@ -8,6 +8,7 @@ module ActiveRecord
def prepare_column_options(column)
spec = super
spec[:unsigned] = "true" if column.unsigned?
+ spec[:auto_increment] = "true" if column.auto_increment?
if @connection.supports_virtual_columns? && column.virtual?
spec[:as] = extract_expression_for_virtual_column(column)
@@ -18,6 +19,12 @@ module ActiveRecord
spec
end
+ def column_spec_for_primary_key(column)
+ spec = super
+ spec.delete(:auto_increment) if column.type == :integer && column.auto_increment?
+ spec
+ end
+
def default_primary_key?(column)
super && column.auto_increment? && !column.unsigned?
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb
index 759493e3bd..a15c7d1787 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql/schema_statements.rb
@@ -5,13 +5,7 @@ module ActiveRecord
module MySQL
module SchemaStatements # :nodoc:
# Returns an array of indexes for the given table.
- def indexes(table_name, name = nil)
- if name
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- Passing name to #indexes is deprecated without replacement.
- MSG
- end
-
+ def indexes(table_name)
indexes = []
current_index = nil
execute_and_free("SHOW KEYS FROM #{quote_table_name(table_name)}", "SCHEMA") do |result|
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index 2c2321872d..8de582fee1 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-require_relative "abstract_mysql_adapter"
-require_relative "mysql/database_statements"
+require "active_record/connection_adapters/abstract_mysql_adapter"
+require "active_record/connection_adapters/mysql/database_statements"
gem "mysql2", ">= 0.3.18", "< 0.5"
require "mysql2"
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb
index 1b67cee24b..469ef3f5a0 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb
@@ -7,11 +7,38 @@ module ActiveRecord
delegate :array, :oid, :fmod, to: :sql_type_metadata
alias :array? :array
+ def initialize(*, max_identifier_length: 63, **)
+ super
+ @max_identifier_length = max_identifier_length
+ end
+
def serial?
return unless default_function
- %r{\Anextval\('"?#{table_name}_#{name}_seq"?'::regclass\)\z} === default_function
+ if %r{\Anextval\('"?(?<sequence_name>.+_(?<suffix>seq\d*))"?'::regclass\)\z} =~ default_function
+ sequence_name_from_parts(table_name, name, suffix) == sequence_name
+ end
end
+
+ protected
+ attr_reader :max_identifier_length
+
+ private
+ def sequence_name_from_parts(table_name, column_name, suffix)
+ over_length = [table_name, column_name, suffix].map(&:length).sum + 2 - max_identifier_length
+
+ if over_length > 0
+ column_name_length = [(max_identifier_length - suffix.length - 2) / 2, column_name.length].min
+ over_length -= column_name.length - column_name_length
+ column_name = column_name[0, column_name_length - [over_length, 0].min]
+ end
+
+ if over_length > 0
+ table_name = table_name[0, table_name.length - over_length]
+ end
+
+ "#{table_name}_#{column_name}_#{suffix}"
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
index b28418d74f..542ca75d3e 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
@@ -1,27 +1,27 @@
# frozen_string_literal: true
-require_relative "oid/array"
-require_relative "oid/bit"
-require_relative "oid/bit_varying"
-require_relative "oid/bytea"
-require_relative "oid/cidr"
-require_relative "oid/date_time"
-require_relative "oid/decimal"
-require_relative "oid/enum"
-require_relative "oid/hstore"
-require_relative "oid/inet"
-require_relative "oid/jsonb"
-require_relative "oid/money"
-require_relative "oid/oid"
-require_relative "oid/point"
-require_relative "oid/legacy_point"
-require_relative "oid/range"
-require_relative "oid/specialized_string"
-require_relative "oid/uuid"
-require_relative "oid/vector"
-require_relative "oid/xml"
+require "active_record/connection_adapters/postgresql/oid/array"
+require "active_record/connection_adapters/postgresql/oid/bit"
+require "active_record/connection_adapters/postgresql/oid/bit_varying"
+require "active_record/connection_adapters/postgresql/oid/bytea"
+require "active_record/connection_adapters/postgresql/oid/cidr"
+require "active_record/connection_adapters/postgresql/oid/date_time"
+require "active_record/connection_adapters/postgresql/oid/decimal"
+require "active_record/connection_adapters/postgresql/oid/enum"
+require "active_record/connection_adapters/postgresql/oid/hstore"
+require "active_record/connection_adapters/postgresql/oid/inet"
+require "active_record/connection_adapters/postgresql/oid/jsonb"
+require "active_record/connection_adapters/postgresql/oid/money"
+require "active_record/connection_adapters/postgresql/oid/oid"
+require "active_record/connection_adapters/postgresql/oid/point"
+require "active_record/connection_adapters/postgresql/oid/legacy_point"
+require "active_record/connection_adapters/postgresql/oid/range"
+require "active_record/connection_adapters/postgresql/oid/specialized_string"
+require "active_record/connection_adapters/postgresql/oid/uuid"
+require "active_record/connection_adapters/postgresql/oid/vector"
+require "active_record/connection_adapters/postgresql/oid/xml"
-require_relative "oid/type_map_initializer"
+require "active_record/connection_adapters/postgresql/oid/type_map_initializer"
module ActiveRecord
module ConnectionAdapters
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb
index 7d5d7d91e6..a89aa5ea09 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb
@@ -35,7 +35,7 @@ module ActiveRecord
if value.is_a?(::Range)
from = type_cast_single_for_database(value.begin)
to = type_cast_single_for_database(value.end)
- "[#{from},#{to}#{value.exclude_end? ? ')' : ']'}"
+ ::Range.new(from, to, value.exclude_end?)
else
super
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
index fc458d0c73..9fdeab06c1 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb
@@ -64,7 +64,7 @@ module ActiveRecord
def quote_default_expression(value, column) # :nodoc:
if value.is_a?(Proc)
value.call
- elsif column.type == :uuid && /\(\)/.match?(value)
+ elsif column.type == :uuid && value.is_a?(String) && /\(\)/.match?(value)
value # Does not quote function default values for UUID columns
elsif column.respond_to?(:array?)
value = type_cast_from_column(column, value)
@@ -101,6 +101,8 @@ module ActiveRecord
end
when OID::Array::Data
_quote(encode_array(value))
+ when Range
+ _quote(encode_range(value))
else
super
end
@@ -117,6 +119,8 @@ module ActiveRecord
value.to_s
when OID::Array::Data
encode_array(value)
+ when Range
+ encode_range(value)
else
super
end
@@ -133,6 +137,10 @@ module ActiveRecord
result
end
+ def encode_range(range)
+ "[#{type_cast(range.first)},#{type_cast(range.last)}#{range.exclude_end? ? ')' : ']'}"
+ end
+
def determine_encoding_of_strings_in_array(value)
case value
when ::Array then determine_encoding_of_strings_in_array(value.first)
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/referential_integrity.rb b/activerecord/lib/active_record/connection_adapters/postgresql/referential_integrity.rb
index 386d22a9bd..8df91c988b 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/referential_integrity.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/referential_integrity.rb
@@ -4,26 +4,21 @@ module ActiveRecord
module ConnectionAdapters
module PostgreSQL
module ReferentialIntegrity # :nodoc:
- def supports_disable_referential_integrity? # :nodoc:
- true
- end
-
def disable_referential_integrity # :nodoc:
- if supports_disable_referential_integrity?
- original_exception = nil
+ original_exception = nil
- begin
- transaction(requires_new: true) do
- execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
- end
- rescue ActiveRecord::ActiveRecordError => e
- original_exception = e
+ begin
+ transaction(requires_new: true) do
+ execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
end
+ rescue ActiveRecord::ActiveRecordError => e
+ original_exception = e
+ end
- begin
- yield
- rescue ActiveRecord::InvalidForeignKey => e
- warn <<-WARNING
+ begin
+ yield
+ rescue ActiveRecord::InvalidForeignKey => e
+ warn <<-WARNING
WARNING: Rails was not able to disable referential integrity.
This is most likely caused due to missing permissions.
@@ -32,17 +27,14 @@ Rails needs superuser privileges to disable referential integrity.
cause: #{original_exception.try(:message)}
WARNING
- raise e
- end
+ raise e
+ end
- begin
- transaction(requires_new: true) do
- execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
- end
- rescue ActiveRecord::ActiveRecordError
+ begin
+ transaction(requires_new: true) do
+ execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
end
- else
- yield
+ rescue ActiveRecord::ActiveRecordError
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb
index f1489e4d69..75622eb304 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb
@@ -44,15 +44,8 @@ module ActiveRecord
# a record (as primary keys cannot be +nil+). This might be done via the
# +SecureRandom.uuid+ method and a +before_save+ callback, for instance.
def primary_key(name, type = :primary_key, **options)
- options[:auto_increment] = true if [:integer, :bigint].include?(type) && !options.key?(:default)
if type == :uuid
options[:default] = options.fetch(:default, "gen_random_uuid()")
- elsif options.delete(:auto_increment) == true && %i(integer bigint).include?(type)
- type = if type == :bigint || options[:limit] == 8
- :bigserial
- else
- :serial
- end
end
super
@@ -185,6 +178,15 @@ module ActiveRecord
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
include ColumnMethods
+
+ private
+ def integer_like_primary_key_type(type, options)
+ if type == :bigint || options[:limit] == 8
+ :bigserial
+ else
+ :serial
+ end
+ end
end
class Table < ActiveRecord::ConnectionAdapters::Table
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
index 9e2f61e6ce..846e721983 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
-require "active_support/core_ext/string/strip"
-
module ActiveRecord
module ConnectionAdapters
module PostgreSQL
@@ -66,12 +64,7 @@ module ActiveRecord
end
# Verifies existence of an index with a given name.
- def index_name_exists?(table_name, index_name, default = nil)
- unless default.nil?
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- Passing default to #index_name_exists? is deprecated without replacement.
- MSG
- end
+ def index_name_exists?(table_name, index_name)
table = quoted_scope(table_name)
index = quoted_scope(index_name)
@@ -89,13 +82,7 @@ module ActiveRecord
end
# Returns an array of indexes for the given table.
- def indexes(table_name, name = nil) # :nodoc:
- if name
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- Passing name to #indexes is deprecated without replacement.
- MSG
- end
-
+ def indexes(table_name) # :nodoc:
scope = quoted_scope(table_name)
result = query(<<-SQL, "SCHEMA")
@@ -617,7 +604,8 @@ module ActiveRecord
table_name,
default_function,
collation,
- comment: comment.presence
+ comment: comment.presence,
+ max_identifier_length: max_identifier_length
)
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 4d37a292d6..2c3c1df2a9 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -4,20 +4,20 @@
gem "pg", "~> 0.18"
require "pg"
-require_relative "abstract_adapter"
-require_relative "statement_pool"
-require_relative "postgresql/column"
-require_relative "postgresql/database_statements"
-require_relative "postgresql/explain_pretty_printer"
-require_relative "postgresql/oid"
-require_relative "postgresql/quoting"
-require_relative "postgresql/referential_integrity"
-require_relative "postgresql/schema_creation"
-require_relative "postgresql/schema_definitions"
-require_relative "postgresql/schema_dumper"
-require_relative "postgresql/schema_statements"
-require_relative "postgresql/type_metadata"
-require_relative "postgresql/utils"
+require "active_record/connection_adapters/abstract_adapter"
+require "active_record/connection_adapters/statement_pool"
+require "active_record/connection_adapters/postgresql/column"
+require "active_record/connection_adapters/postgresql/database_statements"
+require "active_record/connection_adapters/postgresql/explain_pretty_printer"
+require "active_record/connection_adapters/postgresql/oid"
+require "active_record/connection_adapters/postgresql/quoting"
+require "active_record/connection_adapters/postgresql/referential_integrity"
+require "active_record/connection_adapters/postgresql/schema_creation"
+require "active_record/connection_adapters/postgresql/schema_definitions"
+require "active_record/connection_adapters/postgresql/schema_dumper"
+require "active_record/connection_adapters/postgresql/schema_statements"
+require "active_record/connection_adapters/postgresql/type_metadata"
+require "active_record/connection_adapters/postgresql/utils"
module ActiveRecord
module ConnectionHandling # :nodoc:
@@ -312,14 +312,14 @@ module ActiveRecord
def get_advisory_lock(lock_id) # :nodoc:
unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
- raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
+ raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer")
end
query_value("SELECT pg_try_advisory_lock(#{lock_id})")
end
def release_advisory_lock(lock_id) # :nodoc:
unless lock_id.is_a?(Integer) && lock_id.bit_length <= 63
- raise(ArgumentError, "Postgres requires advisory lock ids to be a signed 64 bit integer")
+ raise(ArgumentError, "PostgreSQL requires advisory lock ids to be a signed 64 bit integer")
end
query_value("SELECT pg_advisory_unlock(#{lock_id})")
end
@@ -337,25 +337,20 @@ module ActiveRecord
end
def extension_enabled?(name)
- if supports_extensions?
- res = exec_query("SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled", "SCHEMA")
- res.cast_values.first
- end
+ res = exec_query("SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled", "SCHEMA")
+ res.cast_values.first
end
def extensions
- if supports_extensions?
- exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
- else
- super
- end
+ exec_query("SELECT extname FROM pg_extension", "SCHEMA").cast_values
end
# Returns the configured supported identifier length supported by PostgreSQL
- def table_alias_length
+ def max_identifier_length
@max_identifier_length ||= query_value("SHOW max_identifier_length", "SCHEMA").to_i
end
- alias index_name_length table_alias_length
+ alias table_alias_length max_identifier_length
+ alias index_name_length max_identifier_length
# Set the authorized user for this session
def session_auth=(user)
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb
index 501f17dbad..c9855019c1 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb
@@ -3,27 +3,16 @@
module ActiveRecord
module ConnectionAdapters
module SQLite3
- module ColumnMethods
- def primary_key(name, type = :primary_key, **options)
- if %i(integer bigint).include?(type) && (options.delete(:auto_increment) == true || !options.key?(:default))
- type = :primary_key
- end
-
- super
- end
- end
-
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
- include ColumnMethods
-
def references(*args, **options)
super(*args, type: :integer, **options)
end
alias :belongs_to :references
- end
- class Table < ActiveRecord::ConnectionAdapters::Table
- include ColumnMethods
+ private
+ def integer_like_primary_key_type(type, options)
+ :primary_key
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb
index a512702b7b..f0d702136d 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3/schema_statements.rb
@@ -5,13 +5,7 @@ module ActiveRecord
module SQLite3
module SchemaStatements # :nodoc:
# Returns an array of indexes for the given table.
- def indexes(table_name, name = nil)
- if name
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- Passing name to #indexes is deprecated without replacement.
- MSG
- end
-
+ def indexes(table_name)
exec_query("PRAGMA index_list(#{quote_table_name(table_name)})", "SCHEMA").map do |row|
index_sql = query_value(<<-SQL, "SCHEMA")
SELECT sql
@@ -39,10 +33,6 @@ module ActiveRecord
end
end
- def update_table_definition(table_name, base)
- SQLite3::Table.new(table_name, base)
- end
-
def create_schema_dumper(options)
SQLite3::SchemaDumper.create(self, options)
end
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
index 6fdd666486..670afa3684 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb
@@ -1,13 +1,13 @@
# frozen_string_literal: true
-require_relative "abstract_adapter"
-require_relative "statement_pool"
-require_relative "sqlite3/explain_pretty_printer"
-require_relative "sqlite3/quoting"
-require_relative "sqlite3/schema_creation"
-require_relative "sqlite3/schema_definitions"
-require_relative "sqlite3/schema_dumper"
-require_relative "sqlite3/schema_statements"
+require "active_record/connection_adapters/abstract_adapter"
+require "active_record/connection_adapters/statement_pool"
+require "active_record/connection_adapters/sqlite3/explain_pretty_printer"
+require "active_record/connection_adapters/sqlite3/quoting"
+require "active_record/connection_adapters/sqlite3/schema_creation"
+require "active_record/connection_adapters/sqlite3/schema_definitions"
+require "active_record/connection_adapters/sqlite3/schema_dumper"
+require "active_record/connection_adapters/sqlite3/schema_statements"
gem "sqlite3", "~> 1.3.6"
require "sqlite3"
diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb
index 945c4eca78..0f7a503c90 100644
--- a/activerecord/lib/active_record/core.rb
+++ b/activerecord/lib/active_record/core.rb
@@ -76,26 +76,6 @@ module ActiveRecord
# scope being ignored is error-worthy, rather than a warning.
mattr_accessor :error_on_ignored_order, instance_writer: false, default: false
- def self.error_on_ignored_order_or_limit
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- The flag error_on_ignored_order_or_limit is deprecated. Limits are
- now supported. Please use error_on_ignored_order instead.
- MSG
- error_on_ignored_order
- end
-
- def error_on_ignored_order_or_limit
- self.class.error_on_ignored_order_or_limit
- end
-
- def self.error_on_ignored_order_or_limit=(value)
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- The flag error_on_ignored_order_or_limit is deprecated. Limits are
- now supported. Please use error_on_ignored_order= instead.
- MSG
- self.error_on_ignored_order = value
- end
-
##
# :singleton-method:
# Specify whether or not to use timestamps for migration versions
diff --git a/activerecord/lib/active_record/explain.rb b/activerecord/lib/active_record/explain.rb
index 8bb54a24b7..7ccb938888 100644
--- a/activerecord/lib/active_record/explain.rb
+++ b/activerecord/lib/active_record/explain.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require_relative "explain_registry"
+require "active_record/explain_registry"
module ActiveRecord
module Explain
diff --git a/activerecord/lib/active_record/explain_subscriber.rb b/activerecord/lib/active_record/explain_subscriber.rb
index 9252fa3fed..a86217abc0 100644
--- a/activerecord/lib/active_record/explain_subscriber.rb
+++ b/activerecord/lib/active_record/explain_subscriber.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "active_support/notifications"
-require_relative "explain_registry"
+require "active_record/explain_registry"
module ActiveRecord
class ExplainSubscriber # :nodoc:
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 12169fffa9..86f13d75d5 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -6,8 +6,8 @@ require "zlib"
require "set"
require "active_support/dependencies"
require "active_support/core_ext/digest/uuid"
-require_relative "fixture_set/file"
-require_relative "errors"
+require "active_record/fixture_set/file"
+require "active_record/errors"
module ActiveRecord
class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc:
diff --git a/activerecord/lib/active_record/internal_metadata.rb b/activerecord/lib/active_record/internal_metadata.rb
index 14795cc815..5a65edf27e 100644
--- a/activerecord/lib/active_record/internal_metadata.rb
+++ b/activerecord/lib/active_record/internal_metadata.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-require_relative "scoping/default"
-require_relative "scoping/named"
+require "active_record/scoping/default"
+require "active_record/scoping/named"
module ActiveRecord
# This class is used to create a table that keeps track of values and keys such
diff --git a/activerecord/lib/active_record/locking/pessimistic.rb b/activerecord/lib/active_record/locking/pessimistic.rb
index 72bccd4906..bb85c47e06 100644
--- a/activerecord/lib/active_record/locking/pessimistic.rb
+++ b/activerecord/lib/active_record/locking/pessimistic.rb
@@ -63,12 +63,13 @@ module ActiveRecord
def lock!(lock = true)
if persisted?
if changed?
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- Locking a record with unpersisted changes is deprecated and will raise an
- exception in Rails 5.2. Use `save` to persist the changes, or `reload` to
- discard them explicitly.
+ raise(<<-MSG.squish)
+ Locking a record with unpersisted changes is not supported. Use
+ `save` to persist the changes, or `reload` to discard them
+ explicitly.
MSG
end
+
reload(lock: lock)
end
self
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index 52ca4671c2..1485687053 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -354,9 +354,9 @@ module ActiveRecord
# to match the structure of your database.
#
# To roll the database back to a previous migration version, use
- # <tt>rails db:migrate VERSION=X</tt> where <tt>X</tt> is the version to which
+ # <tt>rails db:rollback VERSION=X</tt> where <tt>X</tt> is the version to which
# you wish to downgrade. Alternatively, you can also use the STEP option if you
- # wish to rollback last few migrations. <tt>rails db:migrate STEP=2</tt> will rollback
+ # wish to rollback last few migrations. <tt>rails db:rollback STEP=2</tt> will rollback
# the latest two migrations.
#
# If any of the migrations throw an <tt>ActiveRecord::IrreversibleMigration</tt> exception,
@@ -1026,11 +1026,6 @@ module ActiveRecord
new(:up, migrations(migrations_paths), nil)
end
- def schema_migrations_table_name
- SchemaMigration.table_name
- end
- deprecate :schema_migrations_table_name
-
def get_all_versions(connection = Base.connection)
if SchemaMigration.table_exists?
SchemaMigration.all_versions.map(&:to_i)
diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb
index a3a5e0fa16..ac7d506fd1 100644
--- a/activerecord/lib/active_record/migration/command_recorder.rb
+++ b/activerecord/lib/active_record/migration/command_recorder.rb
@@ -161,8 +161,8 @@ module ActiveRecord
table, columns, options = *args
options ||= {}
- index_name = options[:name]
- options_hash = index_name ? { name: index_name } : { column: columns }
+ options_hash = options.slice(:name, :algorithm)
+ options_hash[:column] = columns if !options_hash[:name]
[:remove_index, [table, options_hash]]
end
diff --git a/activerecord/lib/active_record/migration/compatibility.rb b/activerecord/lib/active_record/migration/compatibility.rb
index 784292f3f9..c979aaf0a0 100644
--- a/activerecord/lib/active_record/migration/compatibility.rb
+++ b/activerecord/lib/active_record/migration/compatibility.rb
@@ -20,6 +20,11 @@ module ActiveRecord
class V5_0 < V5_1
module TableDefinition
+ def primary_key(name, type = :primary_key, **options)
+ type = :integer if type == :primary_key
+ super
+ end
+
def references(*args, **options)
super(*args, type: :integer, **options)
end
@@ -47,11 +52,8 @@ module ActiveRecord
end
if block_given?
- super(table_name, options) do |t|
- class << t
- prepend TableDefinition
- end
- yield t
+ super do |t|
+ yield compatible_table_definition(t)
end
else
super
@@ -60,21 +62,46 @@ module ActiveRecord
def change_table(table_name, options = {})
if block_given?
- super(table_name, options) do |t|
- class << t
- prepend TableDefinition
- end
- yield t
+ super do |t|
+ yield compatible_table_definition(t)
+ end
+ else
+ super
+ end
+ end
+
+ def create_join_table(table_1, table_2, column_options: {}, **options)
+ column_options.reverse_merge!(type: :integer)
+
+ if block_given?
+ super do |t|
+ yield compatible_table_definition(t)
end
else
super
end
end
+ def add_column(table_name, column_name, type, options = {})
+ if type == :primary_key
+ type = :integer
+ options[:primary_key] = true
+ end
+ super
+ end
+
def add_reference(table_name, ref_name, **options)
super(table_name, ref_name, type: :integer, **options)
end
alias :add_belongs_to :add_reference
+
+ private
+ def compatible_table_definition(t)
+ class << t
+ prepend TableDefinition
+ end
+ t
+ end
end
class V4_2 < V5_0
@@ -93,11 +120,8 @@ module ActiveRecord
def create_table(table_name, options = {})
if block_given?
- super(table_name, options) do |t|
- class << t
- prepend TableDefinition
- end
- yield t
+ super do |t|
+ yield compatible_table_definition(t)
end
else
super
@@ -106,11 +130,8 @@ module ActiveRecord
def change_table(table_name, options = {})
if block_given?
- super(table_name, options) do |t|
- class << t
- prepend TableDefinition
- end
- yield t
+ super do |t|
+ yield compatible_table_definition(t)
end
else
super
@@ -146,6 +167,12 @@ module ActiveRecord
end
private
+ def compatible_table_definition(t)
+ class << t
+ prepend TableDefinition
+ end
+ super
+ end
def index_name_for_remove(table_name, options = {})
index_name = index_name(table_name, options)
diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb
index 34c0ef4e75..bed9400f51 100644
--- a/activerecord/lib/active_record/model_schema.rb
+++ b/activerecord/lib/active_record/model_schema.rb
@@ -115,20 +115,6 @@ module ActiveRecord
# If true, the default table name for a Product class will be "products". If false, it would just be "product".
# See table_name for the full rules on table/class naming. This is true, by default.
- ##
- # :singleton-method: ignored_columns
- # :call-seq: ignored_columns
- #
- # The list of columns names the model should ignore. Ignored columns won't have attribute
- # accessors defined, and won't be referenced in SQL queries.
-
- ##
- # :singleton-method: ignored_columns=
- # :call-seq: ignored_columns=(columns)
- #
- # Sets the columns names the model should ignore. Ignored columns won't have attribute
- # accessors defined, and won't be referenced in SQL queries.
-
included do
mattr_accessor :primary_key_prefix_type, instance_writer: false
@@ -138,9 +124,9 @@ module ActiveRecord
class_attribute :internal_metadata_table_name, instance_accessor: false, default: "ar_internal_metadata"
class_attribute :protected_environments, instance_accessor: false, default: [ "production" ]
class_attribute :pluralize_table_names, instance_writer: false, default: true
- class_attribute :ignored_columns, instance_accessor: false, default: [].freeze
self.inheritance_column = "type"
+ self.ignored_columns = [].freeze
delegate :type_for_attribute, to: :class
@@ -271,6 +257,22 @@ module ActiveRecord
@explicit_inheritance_column = true
end
+ # The list of columns names the model should ignore. Ignored columns won't have attribute
+ # accessors defined, and won't be referenced in SQL queries.
+ def ignored_columns
+ if defined?(@ignored_columns)
+ @ignored_columns
+ else
+ superclass.ignored_columns
+ end
+ end
+
+ # Sets the columns names the model should ignore. Ignored columns won't have attribute
+ # accessors defined, and won't be referenced in SQL queries.
+ def ignored_columns=(columns)
+ @ignored_columns = columns.map(&:to_s)
+ end
+
def sequence_name
if base_class == self
@sequence_name ||= reset_sequence_name
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index fbbf9082cc..a57c60ffac 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -71,6 +71,100 @@ module ActiveRecord
klass.allocate.init_with("attributes" => attributes, "new_record" => false, &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.
+ #
+ # ==== Parameters
+ #
+ # * +id+ - This should be the id or an array of ids to be updated.
+ # * +attributes+ - This should be a hash of attributes or an array of hashes.
+ #
+ # ==== Examples
+ #
+ # # Updates one record
+ # Person.update(15, user_name: "Samuel", group: "expert")
+ #
+ # # Updates multiple records
+ # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
+ # Person.update(people.keys, people.values)
+ #
+ # # Updates multiple records from the result of a relation
+ # people = Person.where(group: "expert")
+ # people.update(group: "masters")
+ #
+ # Note: Updating a large number of records will run an UPDATE
+ # query for each record, which may cause a performance issue.
+ # When running callbacks is not needed for each record update,
+ # it is preferred to use {update_all}[rdoc-ref:Relation#update_all]
+ # for updating all records in a single query.
+ def update(id = :all, attributes)
+ if id.is_a?(Array)
+ id.map.with_index { |one_id, idx| update(one_id, attributes[idx]) }.compact
+ elsif id == :all
+ all.each { |record| record.update(attributes) }
+ else
+ if ActiveRecord::Base === id
+ raise ArgumentError,
+ "You are passing an instance of ActiveRecord::Base to `update`. " \
+ "Please pass the id of the object by calling `.id`."
+ end
+ object = find(id)
+ object.update(attributes)
+ object
+ end
+ rescue RecordNotFound
+ end
+
+ # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
+ # therefore all callbacks and filters are fired off before the object is deleted. This method is
+ # less efficient than #delete but allows cleanup methods and other actions to be run.
+ #
+ # This essentially finds the object (or multiple objects) with the given id, creates a new object
+ # from the attributes, and then calls destroy on it.
+ #
+ # ==== Parameters
+ #
+ # * +id+ - This should be the id or an array of ids to be destroyed.
+ #
+ # ==== Examples
+ #
+ # # Destroy a single object
+ # Todo.destroy(1)
+ #
+ # # Destroy multiple objects
+ # todos = [1,2,3]
+ # Todo.destroy(todos)
+ def destroy(id)
+ if id.is_a?(Array)
+ id.map { |one_id| destroy(one_id) }.compact
+ else
+ find(id).destroy
+ end
+ rescue RecordNotFound
+ end
+
+ # Deletes the row with a primary key matching the +id+ argument, using a
+ # SQL +DELETE+ statement, and returns the number of rows deleted. Active
+ # Record objects are not instantiated, so the object's callbacks are not
+ # executed, including any <tt>:dependent</tt> association options.
+ #
+ # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
+ #
+ # Note: Although it is often much faster than the alternative, #destroy,
+ # skipping callbacks might bypass business logic in your application
+ # that ensures referential integrity or performs other essential jobs.
+ #
+ # ==== Examples
+ #
+ # # Delete a single row
+ # Todo.delete(1)
+ #
+ # # Delete multiple rows
+ # Todo.delete([2,3,4])
+ def delete(id_or_array)
+ where(primary_key => id_or_array).delete_all
+ end
+
private
# Called by +instantiate+ to decide which class to use for a new
# record instance.
@@ -228,7 +322,7 @@ module ActiveRecord
def becomes(klass)
became = klass.new
became.instance_variable_set("@attributes", @attributes)
- became.instance_variable_set("@mutation_tracker", @mutation_tracker) if defined?(@mutation_tracker)
+ became.instance_variable_set("@mutations_from_database", @mutations_from_database) if defined?(@mutations_from_database)
became.instance_variable_set("@changed_attributes", attributes_changed_by_setter)
became.instance_variable_set("@new_record", new_record?)
became.instance_variable_set("@destroyed", destroyed?)
diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb
index f780538319..3996d5661f 100644
--- a/activerecord/lib/active_record/querying.rb
+++ b/activerecord/lib/active_record/querying.rb
@@ -7,7 +7,7 @@ module ActiveRecord
delegate :first_or_create, :first_or_create!, :first_or_initialize, to: :all
delegate :find_or_create_by, :find_or_create_by!, :find_or_initialize_by, to: :all
delegate :find_by, :find_by!, to: :all
- delegate :destroy, :destroy_all, :delete, :delete_all, :update, :update_all, to: :all
+ delegate :destroy_all, :delete_all, :update_all, to: :all
delegate :find_each, :find_in_batches, :in_batches, to: :all
delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins, :left_joins, :left_outer_joins, :or,
:where, :rewhere, :preload, :eager_load, :includes, :from, :lock, :readonly, :extending,
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index ead42d64ec..812e1d7a00 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -53,8 +53,8 @@ module ActiveRecord
# to avoid cross references when loading a constant for the
# first time. Also, make it output to STDERR.
console do |app|
- require_relative "railties/console_sandbox" if app.sandbox?
- require_relative "base"
+ require "active_record/railties/console_sandbox" if app.sandbox?
+ require "active_record/base"
unless ActiveSupport::Logger.logger_outputs_to?(Rails.logger, STDERR, STDOUT)
console = ActiveSupport::Logger.new(STDERR)
Rails.logger.extend ActiveSupport::Logger.broadcast console
@@ -62,7 +62,7 @@ module ActiveRecord
end
runner do
- require_relative "base"
+ require "active_record/base"
end
initializer "active_record.initialize_timezone" do
@@ -106,7 +106,7 @@ module ActiveRecord
initializer "active_record.warn_on_records_fetched_greater_than" do
if config.active_record.warn_on_records_fetched_greater_than
ActiveSupport.on_load(:active_record) do
- require_relative "relation/record_fetch_warning"
+ require "active_record/relation/record_fetch_warning"
end
end
end
@@ -146,7 +146,7 @@ end_warning
# Expose database runtime to controller for logging.
initializer "active_record.log_runtime" do
- require_relative "railties/controller_runtime"
+ require "active_record/railties/controller_runtime"
ActiveSupport.on_load(:action_controller) do
include ActiveRecord::Railties::ControllerRuntime
end
diff --git a/activerecord/lib/active_record/railties/controller_runtime.rb b/activerecord/lib/active_record/railties/controller_runtime.rb
index 3cf66980a5..2ae733f657 100644
--- a/activerecord/lib/active_record/railties/controller_runtime.rb
+++ b/activerecord/lib/active_record/railties/controller_runtime.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require "active_support/core_ext/module/attr_internal"
-require_relative "../log_subscriber"
+require "active_record/log_subscriber"
module ActiveRecord
module Railties # :nodoc:
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 691b3612d8..723272b4b2 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -189,7 +189,7 @@ db_namespace = namespace :db do
namespace :fixtures do
desc "Loads fixtures into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures."
task load: [:environment, :load_config] do
- require_relative "../fixtures"
+ require "active_record/fixtures"
base_dir = ActiveRecord::Tasks::DatabaseTasks.fixtures_path
@@ -211,7 +211,7 @@ db_namespace = namespace :db do
# desc "Search for a fixture given a LABEL or ID. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures."
task identify: [:environment, :load_config] do
- require_relative "../fixtures"
+ require "active_record/fixtures"
label, id = ENV["LABEL"], ENV["ID"]
raise "LABEL or ID required" if label.blank? && id.blank?
@@ -237,7 +237,7 @@ db_namespace = namespace :db do
namespace :schema do
desc "Creates a db/schema.rb file that is portable against any DB supported by Active Record"
task dump: [:environment, :load_config] do
- require_relative "../schema_dumper"
+ require "active_record/schema_dumper"
filename = ENV["SCHEMA"] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "schema.rb")
File.open(filename, "w:utf-8") do |file|
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
@@ -313,7 +313,7 @@ db_namespace = namespace :db do
begin
should_reconnect = ActiveRecord::Base.connection_pool.active_connection?
ActiveRecord::Schema.verbose = false
- ActiveRecord::Tasks::DatabaseTasks.load_schema ActiveRecord::Base.configurations["test"], :ruby, ENV["SCHEMA"]
+ ActiveRecord::Tasks::DatabaseTasks.load_schema ActiveRecord::Base.configurations["test"], :ruby, ENV["SCHEMA"], "test"
ensure
if should_reconnect
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[ActiveRecord::Tasks::DatabaseTasks.env])
@@ -323,7 +323,7 @@ db_namespace = namespace :db do
# desc "Recreate the test database from an existent structure.sql file"
task load_structure: %w(db:test:purge) do
- ActiveRecord::Tasks::DatabaseTasks.load_schema ActiveRecord::Base.configurations["test"], :sql, ENV["SCHEMA"]
+ ActiveRecord::Tasks::DatabaseTasks.load_schema ActiveRecord::Base.configurations["test"], :sql, ENV["SCHEMA"], "test"
end
# desc "Empty the test database"
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index 00ff20f4d7..8877f762b2 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require "active_support/core_ext/string/filters"
-require "active_support/deprecation"
require "concurrent/map"
module ActiveRecord
@@ -138,7 +137,7 @@ module ActiveRecord
# HasAndBelongsToManyReflection
# ThroughReflection
# PolymorphicReflection
- # RuntimeReflection
+ # RuntimeReflection
class AbstractReflection # :nodoc:
def through_reflection?
false
@@ -154,14 +153,6 @@ module ActiveRecord
klass.new(attributes, &block)
end
- def quoted_table_name
- klass.quoted_table_name
- end
-
- def primary_key_type
- klass.type_for_attribute(klass.primary_key)
- end
-
# Returns the class name for the macro.
#
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
@@ -182,11 +173,6 @@ module ActiveRecord
scope ? [scope] : []
end
- def scope_chain
- chain.map(&:scopes)
- end
- deprecate :scope_chain
-
def build_join_constraint(table, foreign_table)
key = join_keys.key
foreign_key = join_keys.foreign_key
@@ -214,7 +200,7 @@ module ActiveRecord
def join_scopes(table, predicate_builder) # :nodoc:
if scope
- [build_scope(table, predicate_builder).instance_exec(&scope)]
+ [scope_for(build_scope(table, predicate_builder))]
else
[]
end
@@ -300,13 +286,17 @@ module ActiveRecord
end
def get_join_keys(association_klass)
- JoinKeys.new(join_pk(association_klass), join_foreign_key)
+ JoinKeys.new(join_primary_key(association_klass), join_foreign_key)
end
def build_scope(table, predicate_builder = predicate_builder(table))
Relation.create(klass, table, predicate_builder)
end
+ def join_primary_key(_)
+ foreign_key
+ end
+
def join_foreign_key
active_record_primary_key
end
@@ -321,10 +311,6 @@ module ActiveRecord
PredicateBuilder.new(TableMetadata.new(klass, table))
end
- def join_pk(_)
- foreign_key
- end
-
def primary_key(klass)
klass.primary_key || raise(UnknownPrimaryKey.new(klass))
end
@@ -373,6 +359,17 @@ module ActiveRecord
#
# <tt>composed_of :balance, class_name: 'Money'</tt> returns the Money class
# <tt>has_many :clients</tt> returns the Client class
+ #
+ # class Company < ActiveRecord::Base
+ # has_many :clients
+ # end
+ #
+ # Company.reflect_on_association(:clients).klass
+ # # => Client
+ #
+ # <b>Note:</b> Do not call +klass.new+ or +klass.create+ to instantiate
+ # a new association object. Use +build_association+ or +create_association+
+ # instead. This allows plugins to hook into association object creation.
def klass
@klass ||= compute_class(class_name)
end
@@ -391,8 +388,8 @@ module ActiveRecord
active_record == other_aggregation.active_record
end
- def scope_for(klass)
- scope ? klass.unscoped.instance_exec(nil, &scope) : klass.unscoped
+ def scope_for(relation, owner = nil)
+ relation.instance_exec(owner, &scope) || relation
end
private
@@ -413,22 +410,6 @@ module ActiveRecord
# Holds all the metadata about an association as it was specified in the
# Active Record class.
class AssociationReflection < MacroReflection #:nodoc:
- # Returns the target association's class.
- #
- # class Author < ActiveRecord::Base
- # has_many :books
- # end
- #
- # Author.reflect_on_association(:books).klass
- # # => Book
- #
- # <b>Note:</b> Do not call +klass.new+ or +klass.create+ to instantiate
- # a new association object. Use +build_association+ or +create_association+
- # instead. This allows plugins to hook into association object creation.
- def klass
- @klass ||= compute_class(class_name)
- end
-
def compute_class(name)
active_record.send(:compute_type, name)
end
@@ -438,21 +419,13 @@ module ActiveRecord
def initialize(name, scope, options, active_record)
super
- @automatic_inverse_of = nil
@type = options[:as] && (options[:foreign_type] || "#{options[:as]}_type")
- @foreign_type = options[:foreign_type] || "#{name}_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
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
- Passing a class to the `class_name` is deprecated and will raise
- an ArgumentError in Rails 5.2. It eagerloads more classes than
- necessary and potentially creates circular dependencies.
-
- Please pass the class name as a string:
- `#{macro} :#{name}, class_name: '#{options[:class_name]}'`
- MSG
+ raise ArgumentError, "A class was passed to `:class_name` but we are expecting a string."
end
end
@@ -622,12 +595,14 @@ module ActiveRecord
# If it cannot find a suitable inverse association name, it returns
# +nil+.
def inverse_name
- options.fetch(:inverse_of) do
- @automatic_inverse_of ||= automatic_inverse_of
+ unless defined?(@inverse_name)
+ @inverse_name = options.fetch(:inverse_of) { automatic_inverse_of }
end
+
+ @inverse_name
end
- # returns either false or the inverse association name that it finds.
+ # returns either +nil+ or the inverse association name that it finds.
def automatic_inverse_of
if can_find_inverse_of_automatically?(self)
inverse_name = ActiveSupport::Inflector.underscore(options[:as] || active_record.name.demodulize).to_sym
@@ -644,8 +619,6 @@ module ActiveRecord
return inverse_name
end
end
-
- false
end
# Checks if the inverse reflection that is returned from the
@@ -749,6 +722,10 @@ module ActiveRecord
end
end
+ def join_primary_key(klass)
+ polymorphic? ? association_primary_key(klass) : association_primary_key
+ end
+
def join_foreign_key
foreign_key
end
@@ -758,10 +735,6 @@ module ActiveRecord
def calculate_constructable(macro, options)
!polymorphic?
end
-
- def join_pk(klass)
- polymorphic? ? association_primary_key(klass) : association_primary_key
- end
end
class HasAndBelongsToManyReflection < AssociationReflection # :nodoc:
@@ -1029,6 +1002,8 @@ module ActiveRecord
end
class PolymorphicReflection < AbstractReflection # :nodoc:
+ delegate :klass, :scope, :plural_name, :type, :get_join_keys, to: :@reflection
+
def initialize(reflection, previous_reflection)
@reflection = reflection
@previous_reflection = previous_reflection
@@ -1044,30 +1019,10 @@ module ActiveRecord
scopes << @previous_reflection.source_type_scope
end
- def klass
- @reflection.klass
- end
-
- def scope
- @reflection.scope
- end
-
- def plural_name
- @reflection.plural_name
- end
-
- def type
- @reflection.type
- end
-
def constraints
@reflection.constraints + [source_type_info]
end
- def get_join_keys(association_klass)
- @reflection.get_join_keys(association_klass)
- end
-
private
def source_type_info
type = @previous_reflection.foreign_type
@@ -1076,8 +1031,8 @@ module ActiveRecord
end
end
- class RuntimeReflection < PolymorphicReflection # :nodoc:
- attr_accessor :next
+ class RuntimeReflection < AbstractReflection # :nodoc:
+ delegate :scope, :type, :constraints, :get_join_keys, to: :@reflection
def initialize(reflection, association)
@reflection = reflection
@@ -1088,12 +1043,8 @@ module ActiveRecord
@association.klass
end
- def constraints
- @reflection.constraints
- end
-
- def alias_name
- Arel::Table.new(table_name, type_caster: klass.type_caster)
+ def aliased_table
+ @aliased_table ||= Arel::Table.new(table_name, type_caster: klass.type_caster)
end
def all_includes; yield; end
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 012bc838b1..997cfe4b5e 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -63,7 +63,7 @@ module ActiveRecord
@klass.connection.insert(
im,
- "SQL",
+ "#{@klass} Create",
primary_key || false,
primary_key_value,
nil,
@@ -86,7 +86,7 @@ module ActiveRecord
@klass.connection.update(
um,
- "SQL",
+ "#{@klass} Update",
)
end
@@ -373,51 +373,7 @@ module ActiveRecord
stmt.wheres = arel.constraints
end
- @klass.connection.update stmt, "SQL"
- 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.
- #
- # ==== Parameters
- #
- # * +id+ - This should be the id or an array of ids to be updated.
- # * +attributes+ - This should be a hash of attributes or an array of hashes.
- #
- # ==== Examples
- #
- # # Updates one record
- # Person.update(15, user_name: 'Samuel', group: 'expert')
- #
- # # Updates multiple records
- # people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
- # Person.update(people.keys, people.values)
- #
- # # Updates multiple records from the result of a relation
- # people = Person.where(group: 'expert')
- # people.update(group: 'masters')
- #
- # Note: Updating a large number of records will run an
- # UPDATE query for each record, which may cause a performance
- # issue. When running callbacks is not needed for each record update,
- # it is preferred to use #update_all for updating all records
- # in a single query.
- def update(id = :all, attributes)
- if id.is_a?(Array)
- id.map.with_index { |one_id, idx| update(one_id, attributes[idx]) }
- elsif id == :all
- records.each { |record| record.update(attributes) }
- else
- if ActiveRecord::Base === id
- raise ArgumentError, <<-MSG.squish
- You are passing an instance of ActiveRecord::Base to `update`.
- Please pass the id of the object by calling `.id`.
- MSG
- end
- object = find(id)
- object.update(attributes)
- object
- end
+ @klass.connection.update stmt, "#{@klass} Update All"
end
# Destroys the records by instantiating each
@@ -440,33 +396,6 @@ module ActiveRecord
records.each(&:destroy).tap { reset }
end
- # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
- # therefore all callbacks and filters are fired off before the object is deleted. This method is
- # less efficient than #delete but allows cleanup methods and other actions to be run.
- #
- # This essentially finds the object (or multiple objects) with the given id, creates a new object
- # from the attributes, and then calls destroy on it.
- #
- # ==== Parameters
- #
- # * +id+ - Can be either an Integer or an Array of Integers.
- #
- # ==== Examples
- #
- # # Destroy a single object
- # Todo.destroy(1)
- #
- # # Destroy multiple objects
- # todos = [1,2,3]
- # Todo.destroy(todos)
- def destroy(id)
- if id.is_a?(Array)
- id.map { |one_id| destroy(one_id) }
- else
- find(id).destroy
- end
- end
-
# Deletes the records without instantiating the records
# first, and hence not calling the {#destroy}[rdoc-ref:Persistence#destroy]
# method nor invoking callbacks.
@@ -503,35 +432,12 @@ module ActiveRecord
stmt.wheres = arel.constraints
end
- affected = @klass.connection.delete(stmt, "SQL")
+ affected = @klass.connection.delete(stmt, "#{@klass} Destroy")
reset
affected
end
- # Deletes the row with a primary key matching the +id+ argument, using a
- # SQL +DELETE+ statement, and returns the number of rows deleted. Active
- # Record objects are not instantiated, so the object's callbacks are not
- # executed, including any <tt>:dependent</tt> association options.
- #
- # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
- #
- # Note: Although it is often much faster than the alternative,
- # #destroy, skipping callbacks might bypass business logic in
- # your application that ensures referential integrity or performs other
- # essential jobs.
- #
- # ==== Examples
- #
- # # Delete a single row
- # Todo.delete(1)
- #
- # # Delete multiple rows
- # Todo.delete([2,3,4])
- def delete(id_or_array)
- where(primary_key => id_or_array).delete_all
- end
-
# Causes the records to be loaded from the database if they have not
# been loaded already. You can use this if for some reason you need
# to explicitly load some records before actually using them. The
@@ -580,7 +486,7 @@ module ActiveRecord
#
# User.where(name: 'Oscar').where_values_hash
# # => {name: "Oscar"}
- def where_values_hash(relation_table_name = table_name)
+ def where_values_hash(relation_table_name = klass.table_name)
where_clause.to_h(relation_table_name)
end
@@ -645,6 +551,11 @@ module ActiveRecord
limit_value || offset_value
end
+ def alias_tracker(joins = [], aliases = nil) # :nodoc:
+ joins += [aliases] if aliases
+ ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins)
+ end
+
protected
def load_records(records)
diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb
index fa19c679cf..561869017a 100644
--- a/activerecord/lib/active_record/relation/batches.rb
+++ b/activerecord/lib/active_record/relation/batches.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require_relative "batches/batch_enumerator"
+require "active_record/relation/batches/batch_enumerator"
module ActiveRecord
module Batches
@@ -251,20 +251,27 @@ module ActiveRecord
end
end
- batch_relation = relation.where(arel_attribute(primary_key).gt(primary_key_offset))
+ attr = Relation::QueryAttribute.new(primary_key, primary_key_offset, klass.type_for_attribute(primary_key))
+ batch_relation = relation.where(arel_attribute(primary_key).gt(Arel::Nodes::BindParam.new(attr)))
end
end
private
def apply_limits(relation, start, finish)
- relation = relation.where(arel_attribute(primary_key).gteq(start)) if start
- relation = relation.where(arel_attribute(primary_key).lteq(finish)) if finish
+ if start
+ attr = Relation::QueryAttribute.new(primary_key, start, klass.type_for_attribute(primary_key))
+ relation = relation.where(arel_attribute(primary_key).gteq(Arel::Nodes::BindParam.new(attr)))
+ end
+ if finish
+ attr = Relation::QueryAttribute.new(primary_key, finish, klass.type_for_attribute(primary_key))
+ relation = relation.where(arel_attribute(primary_key).lteq(Arel::Nodes::BindParam.new(attr)))
+ end
relation
end
def batch_order
- "#{quoted_table_name}.#{quoted_primary_key} ASC"
+ arel_attribute(primary_key).asc
end
def act_on_ignored_order(error_on_ignore)
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 42d43224fa..116bddce85 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -130,7 +130,7 @@ module ActiveRecord
# end
def calculate(operation, column_name)
if has_include?(column_name)
- relation = construct_relation_for_association_calculations
+ relation = apply_join_dependency(construct_join_dependency)
relation.distinct! if operation.to_s.downcase == "count"
relation.calculate(operation, column_name)
@@ -180,7 +180,8 @@ module ActiveRecord
end
if has_include?(column_names.first)
- construct_relation_for_association_calculations.pluck(*column_names)
+ relation = apply_join_dependency(construct_join_dependency)
+ relation.pluck(*column_names)
else
relation = spawn
relation.select_values = column_names.map { |cn|
@@ -215,7 +216,7 @@ module ActiveRecord
if operation == "count"
column_name ||= select_for_count
if column_name == :all
- if distinct && !(has_limit_or_offset? && order_values.any?)
+ if distinct && (group_values.any? || !(has_limit_or_offset? && order_values.any?))
column_name = primary_key
end
elsif column_name =~ /\s*DISTINCT[\s(]+/i
@@ -391,7 +392,7 @@ module ActiveRecord
def build_count_subquery(relation, column_name, distinct)
relation.select_values = [
if column_name == :all
- distinct ? table[Arel.star] : Arel.sql("1")
+ distinct ? table[Arel.star] : Arel.sql(FinderMethods::ONE_AS_ONE)
else
column_alias = Arel.sql("count_column")
aggregate_column(column_name).as(column_alias)
diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb
index c5354bf4e9..48af777b69 100644
--- a/activerecord/lib/active_record/relation/delegation.rb
+++ b/activerecord/lib/active_record/relation/delegation.rb
@@ -43,8 +43,7 @@ module ActiveRecord
:to_sentence, :to_formatted_s, :as_json,
:shuffle, :split, :slice, :index, :rindex, to: :records
- delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
- :connection, :columns_hash, to: :klass
+ delegate :primary_key, :connection, to: :klass
module ClassSpecificRelation # :nodoc:
extend ActiveSupport::Concern
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 2aed941916..707245bab2 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -310,12 +310,12 @@ module ActiveRecord
return false if !conditions || limit_value == 0
- relation = self unless eager_loading?
- relation ||= apply_join_dependency(self, construct_join_dependency(eager_loading: false))
-
- return false if ActiveRecord::NullRelation === relation
+ if eager_loading?
+ relation = apply_join_dependency(construct_join_dependency(eager_loading: false))
+ return relation.exists?(conditions)
+ end
- relation = construct_relation_for_exists(relation, conditions)
+ relation = construct_relation_for_exists(conditions)
skip_query_cache_if_necessary { connection.select_value(relation.arel, "#{name} Exists") } ? true : false
rescue ::RangeError
@@ -366,17 +366,16 @@ module ActiveRecord
# preexisting join in joins_values to categorizations (by way of
# the `has_many :through` for categories).
#
- join_dependency = construct_join_dependency(joins_values)
+ join_dependency = construct_join_dependency
- aliases = join_dependency.aliases
- relation = select aliases.columns
- relation = apply_join_dependency(relation, join_dependency)
+ relation = apply_join_dependency(join_dependency)
+ relation._select!(join_dependency.aliases.columns)
yield relation, join_dependency
end
- def construct_relation_for_exists(relation, conditions)
- relation = relation.except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
+ def construct_relation_for_exists(conditions)
+ relation = except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
case conditions
when Array, Hash
@@ -388,17 +387,15 @@ module ActiveRecord
relation
end
- def construct_join_dependency(joins = [], eager_loading: true)
+ def construct_join_dependency(eager_loading: true)
including = eager_load_values + includes_values
- ActiveRecord::Associations::JoinDependency.new(klass, table, including, joins, eager_loading: eager_loading)
- end
-
- def construct_relation_for_association_calculations
- apply_join_dependency(self, construct_join_dependency(joins_values))
+ ActiveRecord::Associations::JoinDependency.new(
+ klass, table, including, alias_tracker(joins_values), eager_loading: eager_loading
+ )
end
- def apply_join_dependency(relation, join_dependency)
- relation = relation.except(:includes, :eager_load, :preload).joins!(join_dependency)
+ def apply_join_dependency(join_dependency)
+ relation = except(:includes, :eager_load, :preload).joins!(join_dependency)
if using_limitable_reflections?(join_dependency.reflections)
relation
@@ -413,7 +410,9 @@ module ActiveRecord
def limited_ids_for(relation)
values = @klass.connection.columns_for_distinct(
- "#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
+ connection.column_name_from_arel_node(arel_attribute(primary_key)),
+ relation.order_values
+ )
relation = relation.except(:select).select(values).distinct!
diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb
index 03824ffff9..ebc72d28fd 100644
--- a/activerecord/lib/active_record/relation/merger.rb
+++ b/activerecord/lib/active_record/relation/merger.rb
@@ -122,7 +122,7 @@ module ActiveRecord
end
join_dependency = ActiveRecord::Associations::JoinDependency.new(
- other.klass, other.table, joins_dependency, []
+ other.klass, other.table, joins_dependency, other.alias_tracker
)
relation.joins! rest
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index 5c42414072..885c26d7aa 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -13,6 +13,7 @@ module ActiveRecord
register_handler(Range, RangeHandler.new(self))
register_handler(Relation, RelationHandler.new)
register_handler(Array, ArrayHandler.new(self))
+ register_handler(Set, ArrayHandler.new(self))
end
def build_from_hash(attributes)
@@ -123,11 +124,11 @@ module ActiveRecord
end
end
-require_relative "predicate_builder/array_handler"
-require_relative "predicate_builder/base_handler"
-require_relative "predicate_builder/basic_object_handler"
-require_relative "predicate_builder/range_handler"
-require_relative "predicate_builder/relation_handler"
+require "active_record/relation/predicate_builder/array_handler"
+require "active_record/relation/predicate_builder/base_handler"
+require "active_record/relation/predicate_builder/basic_object_handler"
+require "active_record/relation/predicate_builder/range_handler"
+require "active_record/relation/predicate_builder/relation_handler"
-require_relative "predicate_builder/association_query_value"
-require_relative "predicate_builder/polymorphic_array_value"
+require "active_record/relation/predicate_builder/association_query_value"
+require "active_record/relation/predicate_builder/polymorphic_array_value"
diff --git a/activerecord/lib/active_record/relation/query_attribute.rb b/activerecord/lib/active_record/relation/query_attribute.rb
index 5a9a7fd432..fad08e2613 100644
--- a/activerecord/lib/active_record/relation/query_attribute.rb
+++ b/activerecord/lib/active_record/relation/query_attribute.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require_relative "../attribute"
+require "active_record/attribute"
module ActiveRecord
class Relation
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index bdc5c27328..897ff5c8af 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -1,9 +1,9 @@
# frozen_string_literal: true
-require_relative "from_clause"
-require_relative "query_attribute"
-require_relative "where_clause"
-require_relative "where_clause_factory"
+require "active_record/relation/from_clause"
+require "active_record/relation/query_attribute"
+require "active_record/relation/where_clause"
+require "active_record/relation/where_clause_factory"
require "active_model/forbidden_attributes_protection"
module ActiveRecord
@@ -441,7 +441,7 @@ module ActiveRecord
# => SELECT "users".* FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"
#
def left_outer_joins(*args)
- check_if_method_has_arguments!(:left_outer_joins, args)
+ check_if_method_has_arguments!(__callee__, args)
args.compact!
args.flatten!
@@ -898,8 +898,8 @@ module ActiveRecord
end
# Returns the Arel object associated with the relation.
- def arel # :nodoc:
- @arel ||= build_arel
+ def arel(aliases = nil) # :nodoc:
+ @arel ||= build_arel(aliases)
end
protected
@@ -921,11 +921,11 @@ module ActiveRecord
raise ImmutableRelation if defined?(@arel) && @arel
end
- def build_arel
+ def build_arel(aliases)
arel = Arel::SelectManager.new(table)
- build_joins(arel, joins_values.flatten) unless joins_values.empty?
- build_left_outer_joins(arel, left_outer_joins_values.flatten) unless left_outer_joins_values.empty?
+ aliases = build_joins(arel, joins_values.flatten, aliases) unless joins_values.empty?
+ build_left_outer_joins(arel, left_outer_joins_values.flatten, aliases) unless left_outer_joins_values.empty?
arel.where(where_clause.ast) unless where_clause.empty?
arel.having(having_clause.ast) unless having_clause.empty?
@@ -970,7 +970,7 @@ module ActiveRecord
end
end
- def build_left_outer_joins(manager, outer_joins)
+ def build_left_outer_joins(manager, outer_joins, aliases)
buckets = outer_joins.group_by do |join|
case join
when Hash, Symbol, Array
@@ -980,10 +980,10 @@ module ActiveRecord
end
end
- build_join_query(manager, buckets, Arel::Nodes::OuterJoin)
+ build_join_query(manager, buckets, Arel::Nodes::OuterJoin, aliases)
end
- def build_joins(manager, joins)
+ def build_joins(manager, joins, aliases)
buckets = joins.group_by do |join|
case join
when String
@@ -999,10 +999,10 @@ module ActiveRecord
end
end
- build_join_query(manager, buckets, Arel::Nodes::InnerJoin)
+ build_join_query(manager, buckets, Arel::Nodes::InnerJoin, aliases)
end
- def build_join_query(manager, buckets, join_type)
+ def build_join_query(manager, buckets, join_type, aliases)
buckets.default = []
association_joins = buckets[:association_join]
@@ -1011,9 +1011,10 @@ module ActiveRecord
string_joins = buckets[:string_join].map(&:strip).uniq
join_list = join_nodes + convert_join_strings_to_ast(manager, string_joins)
+ alias_tracker = alias_tracker(join_list, aliases)
join_dependency = ActiveRecord::Associations::JoinDependency.new(
- klass, table, association_joins, join_list
+ klass, table, association_joins, alias_tracker
)
joins = join_dependency.join_constraints(stashed_association_joins, join_type)
@@ -1021,7 +1022,7 @@ module ActiveRecord
manager.join_sources.concat(join_list)
- manager
+ alias_tracker.aliases
end
def convert_join_strings_to_ast(table, joins)
@@ -1172,21 +1173,24 @@ module ActiveRecord
end
alias having_clause_factory where_clause_factory
+ DEFAULT_VALUES = {
+ create_with: FROZEN_EMPTY_HASH,
+ readonly: false,
+ where: Relation::WhereClause.empty,
+ having: Relation::WhereClause.empty,
+ from: Relation::FromClause.empty
+ }
+
+ Relation::MULTI_VALUE_METHODS.each do |value|
+ DEFAULT_VALUES[value] ||= FROZEN_EMPTY_ARRAY
+ end
+
+ Relation::SINGLE_VALUE_METHODS.each do |value|
+ DEFAULT_VALUES[value] = nil if DEFAULT_VALUES[value].nil?
+ end
+
def default_value_for(name)
- case name
- when :create_with
- FROZEN_EMPTY_HASH
- when :readonly
- false
- when :where, :having
- Relation::WhereClause.empty
- when :from
- Relation::FromClause.empty
- when *Relation::MULTI_VALUE_METHODS
- FROZEN_EMPTY_ARRAY
- when *Relation::SINGLE_VALUE_METHODS
- nil
- else
+ DEFAULT_VALUES.fetch(name) do
raise ArgumentError, "unknown relation value #{name.inspect}"
end
end
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
index 424894f835..617d8de8b2 100644
--- a/activerecord/lib/active_record/relation/spawn_methods.rb
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -2,7 +2,7 @@
require "active_support/core_ext/hash/except"
require "active_support/core_ext/hash/slice"
-require_relative "merger"
+require "active_record/relation/merger"
module ActiveRecord
module SpawnMethods
diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb
index 91a4f1fad6..1c3099f55c 100644
--- a/activerecord/lib/active_record/sanitization.rb
+++ b/activerecord/lib/active_record/sanitization.rb
@@ -30,8 +30,6 @@ module ActiveRecord
end
end
alias :sanitize_sql :sanitize_sql_for_conditions
- alias :sanitize_conditions :sanitize_sql
- deprecate sanitize_conditions: :sanitize_sql
# Accepts an array, hash, or string of SQL conditions and sanitizes
# them into a valid SQL fragment for a SET clause.
@@ -207,10 +205,5 @@ module ActiveRecord
end
end
end
-
- def quoted_id # :nodoc:
- self.class.connection.quote(@attributes[self.class.primary_key].value_for_database)
- end
- deprecate :quoted_id
end
end
diff --git a/activerecord/lib/active_record/schema_migration.rb b/activerecord/lib/active_record/schema_migration.rb
index 339a5334a8..f2d8b038fa 100644
--- a/activerecord/lib/active_record/schema_migration.rb
+++ b/activerecord/lib/active_record/schema_migration.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-require_relative "scoping/default"
-require_relative "scoping/named"
+require "active_record/scoping/default"
+require "active_record/scoping/named"
module ActiveRecord
# This class is used to create a table that keeps track of which migrations
diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb
index 0f3f84ca08..ff388ff1f6 100644
--- a/activerecord/lib/active_record/tasks/database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/database_tasks.rb
@@ -225,22 +225,22 @@ module ActiveRecord
class_for_adapter(configuration["adapter"]).new(*arguments).structure_load(filename, structure_load_flags)
end
- def load_schema(configuration, format = ActiveRecord::Base.schema_format, file = nil) # :nodoc:
+ def load_schema(configuration, format = ActiveRecord::Base.schema_format, file = nil, environment = env) # :nodoc:
file ||= schema_file(format)
+ check_schema_file(file)
+ ActiveRecord::Base.establish_connection(configuration)
+
case format
when :ruby
- check_schema_file(file)
- ActiveRecord::Base.establish_connection(configuration)
load(file)
when :sql
- check_schema_file(file)
structure_load(configuration, file)
else
raise ArgumentError, "unknown format #{format.inspect}"
end
ActiveRecord::InternalMetadata.create_table
- ActiveRecord::InternalMetadata[:environment] = ActiveRecord::Migrator.current_environment
+ ActiveRecord::InternalMetadata[:environment] = environment
end
def schema_file(format = ActiveRecord::Base.schema_format)
@@ -253,8 +253,8 @@ module ActiveRecord
end
def load_schema_current(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
- each_current_configuration(environment) { |configuration|
- load_schema configuration, format, file
+ each_current_configuration(environment) { |configuration, configuration_environment|
+ load_schema configuration, format, file, configuration_environment
}
ActiveRecord::Base.establish_connection(environment.to_sym)
end
@@ -301,9 +301,10 @@ module ActiveRecord
environments = [environment]
environments << "test" if environment == "development"
- configurations = ActiveRecord::Base.configurations.values_at(*environments)
- configurations.compact.each do |configuration|
- yield configuration unless configuration["database"].blank?
+ ActiveRecord::Base.configurations.slice(*environments).each do |configuration_environment, configuration|
+ next unless configuration["database"]
+
+ yield configuration, configuration_environment
end
end
diff --git a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
index 84265aa9e3..2f91884f59 100644
--- a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
@@ -101,7 +101,7 @@ module ActiveRecord
def error_class
if configuration["adapter"].include?("jdbc")
- require_relative "../railties/jdbcmysql_error"
+ require "active_record/railties/jdbcmysql_error"
ArJdbcMySQL::Error
elsif defined?(Mysql2)
Mysql2::Error
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index 6761f2da25..97cba5d1c7 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -285,7 +285,7 @@ module ActiveRecord
fire_on = Array(options[:on])
assert_valid_transaction_action(fire_on)
options[:if] = Array(options[:if])
- options[:if].unshift("transaction_include_any_action?(#{fire_on})")
+ options[:if].unshift(-> { transaction_include_any_action?(fire_on) })
end
end
diff --git a/activerecord/lib/active_record/type.rb b/activerecord/lib/active_record/type.rb
index fa22df92b8..c303186ef2 100644
--- a/activerecord/lib/active_record/type.rb
+++ b/activerecord/lib/active_record/type.rb
@@ -2,21 +2,21 @@
require "active_model/type"
-require_relative "type/internal/timezone"
+require "active_record/type/internal/timezone"
-require_relative "type/date"
-require_relative "type/date_time"
-require_relative "type/decimal_without_scale"
-require_relative "type/json"
-require_relative "type/time"
-require_relative "type/text"
-require_relative "type/unsigned_integer"
+require "active_record/type/date"
+require "active_record/type/date_time"
+require "active_record/type/decimal_without_scale"
+require "active_record/type/json"
+require "active_record/type/time"
+require "active_record/type/text"
+require "active_record/type/unsigned_integer"
-require_relative "type/serialized"
-require_relative "type/adapter_specific_registry"
+require "active_record/type/serialized"
+require "active_record/type/adapter_specific_registry"
-require_relative "type/type_map"
-require_relative "type/hash_lookup_type_map"
+require "active_record/type/type_map"
+require "active_record/type/hash_lookup_type_map"
module ActiveRecord
module Type
diff --git a/activerecord/lib/active_record/type_caster.rb b/activerecord/lib/active_record/type_caster.rb
index ed2e4fb79c..2e5f45fa3d 100644
--- a/activerecord/lib/active_record/type_caster.rb
+++ b/activerecord/lib/active_record/type_caster.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-require_relative "type_caster/map"
-require_relative "type_caster/connection"
+require "active_record/type_caster/map"
+require "active_record/type_caster/connection"
module ActiveRecord
module TypeCaster # :nodoc:
diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
index 3f5c879f2f..ca27a3f0ab 100644
--- a/activerecord/lib/active_record/validations.rb
+++ b/activerecord/lib/active_record/validations.rb
@@ -86,8 +86,8 @@ module ActiveRecord
end
end
-require_relative "validations/associated"
-require_relative "validations/uniqueness"
-require_relative "validations/presence"
-require_relative "validations/absence"
-require_relative "validations/length"
+require "active_record/validations/associated"
+require "active_record/validations/uniqueness"
+require "active_record/validations/presence"
+require "active_record/validations/absence"
+require "active_record/validations/length"