aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib')
-rw-r--r--activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb2
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/query.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb5
-rw-r--r--activerecord/lib/active_record/connection_adapters/connection_specification.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb3
-rw-r--r--activerecord/lib/active_record/database_configurations.rb25
-rw-r--r--activerecord/lib/active_record/errors.rb6
-rw-r--r--activerecord/lib/active_record/explain_subscriber.rb2
-rw-r--r--activerecord/lib/active_record/fixture_set/file.rb6
-rw-r--r--activerecord/lib/active_record/fixtures.rb45
-rw-r--r--activerecord/lib/active_record/persistence.rb10
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb46
-rw-r--r--activerecord/lib/active_record/tasks/database_tasks.rb1
-rw-r--r--activerecord/lib/active_record/tasks/mysql_database_tasks.rb6
-rw-r--r--activerecord/lib/active_record/tasks/postgresql_database_tasks.rb6
-rw-r--r--activerecord/lib/arel/visitors/mysql.rb8
-rw-r--r--activerecord/lib/arel/visitors/oracle.rb2
18 files changed, 129 insertions, 50 deletions
diff --git a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
index 6ad4c75fb5..2072f93194 100644
--- a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
+++ b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb
@@ -62,7 +62,7 @@ module ActiveRecord::Associations::Builder # :nodoc:
def middle_reflection(join_model)
middle_name = [lhs_model.name.downcase.pluralize,
- association_name].join("_").gsub("::", "_").to_sym
+ association_name.to_s].sort.join("_").gsub("::", "_").to_sym
middle_options = middle_options join_model
HasMany.create_reflection(lhs_model,
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index 0db0ad8595..404a7c02ba 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -100,7 +100,7 @@ module ActiveRecord
# converting them into an array and iterating through them using
# Array#select.
#
- # person.pets.select { |pet| pet.name =~ /oo/ }
+ # person.pets.select { |pet| /oo/.match?(pet.name) }
# # => [
# # #<Pet id: 2, name: "Spook", person_id: 1>,
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
diff --git a/activerecord/lib/active_record/attribute_methods/query.rb b/activerecord/lib/active_record/attribute_methods/query.rb
index 0cf67644af..5a21e36cc7 100644
--- a/activerecord/lib/active_record/attribute_methods/query.rb
+++ b/activerecord/lib/active_record/attribute_methods/query.rb
@@ -17,7 +17,7 @@ module ActiveRecord
when false, nil then false
else
if !type_for_attribute(attr_name) { false }
- if Numeric === value || value !~ /[^0-9]/
+ if Numeric === value || !value.match?(/[^0-9]/)
!value.to_i.zero?
else
return false if ActiveModel::Type::Boolean::FALSE_VALUES.include?(value)
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 ef1eef6b69..0fe16270ed 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -572,7 +572,8 @@ module ActiveRecord
end
end
- # See https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html
+ # See https://dev.mysql.com/doc/refman/5.7/en/server-error-reference.html
+ ER_DB_CREATE_EXISTS = 1007
ER_DUP_ENTRY = 1062
ER_NOT_NULL_VIOLATION = 1048
ER_NO_REFERENCED_ROW = 1216
@@ -592,6 +593,8 @@ module ActiveRecord
def translate_exception(exception, message:, sql:, binds:)
case error_number(exception)
+ when ER_DB_CREATE_EXISTS
+ DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
when ER_DUP_ENTRY
RecordNotUnique.new(message, sql: sql, binds: binds)
when ER_NO_REFERENCED_ROW, ER_ROW_IS_REFERENCED, ER_ROW_IS_REFERENCED_2, ER_NO_REFERENCED_ROW_2
diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
index 0732b69f81..20041f0c85 100644
--- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb
+++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
@@ -275,7 +275,7 @@ module ActiveRecord
# hash and merges with the rest of the hash.
# Connection details inside of the "url" key win any merge conflicts
def resolve_hash_connection(spec)
- if spec["url"] && spec["url"] !~ /^jdbc:/
+ if spec["url"] && !spec["url"].match?(/^jdbc:/)
connection_hash = resolve_url_connection(spec.delete("url"))
spec.merge!(connection_hash)
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 0a7c6d8ac4..f33ba87435 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -467,6 +467,7 @@ module ActiveRecord
UNIQUE_VIOLATION = "23505"
SERIALIZATION_FAILURE = "40001"
DEADLOCK_DETECTED = "40P01"
+ DUPLICATE_DATABASE = "42P04"
LOCK_NOT_AVAILABLE = "55P03"
QUERY_CANCELED = "57014"
@@ -488,6 +489,8 @@ module ActiveRecord
SerializationFailure.new(message, sql: sql, binds: binds)
when DEADLOCK_DETECTED
Deadlocked.new(message, sql: sql, binds: binds)
+ when DUPLICATE_DATABASE
+ DatabaseAlreadyExists.new(message, sql: sql, binds: binds)
when LOCK_NOT_AVAILABLE
LockWaitTimeout.new(message, sql: sql, binds: binds)
when QUERY_CANCELED
diff --git a/activerecord/lib/active_record/database_configurations.rb b/activerecord/lib/active_record/database_configurations.rb
index 8baa0f5af6..fcf13a910c 100644
--- a/activerecord/lib/active_record/database_configurations.rb
+++ b/activerecord/lib/active_record/database_configurations.rb
@@ -91,6 +91,19 @@ module ActiveRecord
end
alias :blank? :empty?
+ def each
+ throw_getter_deprecation(:each)
+ configurations.each { |config|
+ yield [config.env_name, config.config]
+ }
+ end
+
+ def first
+ throw_getter_deprecation(:first)
+ config = configurations.first
+ [config.env_name, config.config]
+ end
+
private
def env_with_configs(env = nil)
if env
@@ -168,17 +181,21 @@ module ActiveRecord
end
def environment_url_config(env, spec_name, config)
- url = ENV["DATABASE_URL"]
+ url = environment_value_for(spec_name)
return unless url
ActiveRecord::DatabaseConfigurations::UrlConfig.new(env, spec_name, url, config)
end
+ def environment_value_for(spec_name)
+ spec_env_key = "#{spec_name.upcase}_DATABASE_URL"
+ url = ENV[spec_env_key]
+ url ||= ENV["DATABASE_URL"] if spec_name == "primary"
+ url
+ end
+
def method_missing(method, *args, &blk)
case method
- when :each, :first
- throw_getter_deprecation(method)
- configurations.send(method, *args, &blk)
when :fetch
throw_getter_deprecation(method)
configs_for(env_name: args.first)
diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb
index 20cc987d6e..509f21c9a5 100644
--- a/activerecord/lib/active_record/errors.rb
+++ b/activerecord/lib/active_record/errors.rb
@@ -187,6 +187,10 @@ module ActiveRecord
class NoDatabaseError < StatementInvalid
end
+ # Raised when creating a database if it exists.
+ class DatabaseAlreadyExists < StatementInvalid
+ end
+
# Raised when PostgreSQL returns 'cached plan must not change result type' and
# we cannot retry gracefully (e.g. inside a transaction)
class PreparedStatementCacheExpired < StatementInvalid
@@ -334,7 +338,7 @@ module ActiveRecord
# See the following:
#
# * https://www.postgresql.org/docs/current/static/transaction-iso.html
- # * https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html#error_er_lock_deadlock
+ # * https://dev.mysql.com/doc/refman/5.7/en/server-error-reference.html#error_er_lock_deadlock
class TransactionRollbackError < StatementInvalid
end
diff --git a/activerecord/lib/active_record/explain_subscriber.rb b/activerecord/lib/active_record/explain_subscriber.rb
index a86217abc0..ce209092f5 100644
--- a/activerecord/lib/active_record/explain_subscriber.rb
+++ b/activerecord/lib/active_record/explain_subscriber.rb
@@ -26,7 +26,7 @@ module ActiveRecord
payload[:exception] ||
payload[:cached] ||
IGNORED_PAYLOADS.include?(payload[:name]) ||
- payload[:sql] !~ EXPLAINED_SQLS
+ !payload[:sql].match?(EXPLAINED_SQLS)
end
ActiveSupport::Notifications.subscribe("sql.active_record", new)
diff --git a/activerecord/lib/active_record/fixture_set/file.rb b/activerecord/lib/active_record/fixture_set/file.rb
index f1ea0e022f..b2030a5bb9 100644
--- a/activerecord/lib/active_record/fixture_set/file.rb
+++ b/activerecord/lib/active_record/fixture_set/file.rb
@@ -29,6 +29,10 @@ module ActiveRecord
config_row["model_class"]
end
+ def ignored_fixtures
+ config_row["ignore"]
+ end
+
private
def rows
@rows ||= raw_rows.reject { |fixture_name, _| fixture_name == "_fixture" }
@@ -40,7 +44,7 @@ module ActiveRecord
if row
row.last
else
- { 'model_class': nil }
+ { 'model_class': nil, 'ignore': nil }
end
end
end
diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb
index 046ed0e95c..3df4b3c87f 100644
--- a/activerecord/lib/active_record/fixtures.rb
+++ b/activerecord/lib/active_record/fixtures.rb
@@ -420,6 +420,29 @@ module ActiveRecord
#
# Any fixture labeled "DEFAULTS" is safely ignored.
#
+ # Besides using "DEFAULTS", you can also specify what fixtures will
+ # be ignored by setting "ignore" in "_fixture" section.
+ #
+ # # users.yml
+ # _fixture:
+ # ignore:
+ # - base
+ # # or use "ignore: base" when there is only one fixture needs to be ignored.
+ #
+ # base: &base
+ # admin: false
+ # introduction: "This is a default description"
+ #
+ # admin:
+ # <<: *base
+ # admin: true
+ #
+ # visitor:
+ # <<: *base
+ #
+ # In the above example, 'base' will be ignored when creating fixtures.
+ # This can be used for common attributes inheriting.
+ #
# == Configure the fixture model class
#
# It's possible to set the fixture's model class directly in the YAML file.
@@ -614,7 +637,7 @@ module ActiveRecord
end
end
- attr_reader :table_name, :name, :fixtures, :model_class, :config
+ attr_reader :table_name, :name, :fixtures, :model_class, :ignored_fixtures, :config
def initialize(_, name, class_name, path, config = ActiveRecord::Base)
@name = name
@@ -647,8 +670,8 @@ module ActiveRecord
# Returns a hash of rows to be inserted. The key is the table, the value is
# a list of rows to insert to that table.
def table_rows
- # allow a standard key to be used for doing defaults in YAML
- fixtures.delete("DEFAULTS")
+ # allow specifying fixtures to be ignored by setting `ignore` in `_fixture` section
+ fixtures.except!(*ignored_fixtures)
TableRows.new(
table_name,
@@ -667,6 +690,21 @@ module ActiveRecord
end
end
+ def ignored_fixtures=(base)
+ @ignored_fixtures =
+ case base
+ when Array
+ base
+ when String
+ [base]
+ else
+ []
+ end
+
+ @ignored_fixtures << "DEFAULTS" unless @ignored_fixtures.include?("DEFAULTS")
+ @ignored_fixtures.compact
+ end
+
# Loads the fixtures from the YAML file at +path+.
# If the file sets the +model_class+ and current instance value is not set,
# it uses the file value.
@@ -678,6 +716,7 @@ module ActiveRecord
yaml_files.each_with_object({}) do |file, fixtures|
FixtureSet::File.open(file) do |fh|
self.model_class ||= fh.model_class if fh.model_class
+ self.ignored_fixtures ||= fh.ignored_fixtures
fh.each do |fixture_name, row|
fixtures[fixture_name] = ActiveRecord::Fixture.new(row, model_class)
end
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index 323b01ab2d..4dfe8655fe 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -566,10 +566,10 @@ module ActiveRecord
def becomes(klass)
became = klass.allocate
became.send(:initialize)
- became.instance_variable_set("@attributes", @attributes)
- became.instance_variable_set("@mutations_from_database", @mutations_from_database ||= nil)
- became.instance_variable_set("@new_record", new_record?)
- became.instance_variable_set("@destroyed", destroyed?)
+ became.instance_variable_set(:@attributes, @attributes)
+ became.instance_variable_set(:@mutations_from_database, @mutations_from_database ||= nil)
+ became.instance_variable_set(:@new_record, new_record?)
+ became.instance_variable_set(:@destroyed, destroyed?)
became.errors.copy!(errors)
became
end
@@ -809,7 +809,7 @@ module ActiveRecord
self.class.unscoped { self.class.find(id) }
end
- @attributes = fresh_object.instance_variable_get("@attributes")
+ @attributes = fresh_object.instance_variable_get(:@attributes)
@new_record = false
self
end
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 6957ba052b..29e2e2cedc 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -1095,26 +1095,44 @@ module ActiveRecord
end
def build_left_outer_joins(manager, outer_joins, aliases)
- buckets = { association_join: valid_association_list(outer_joins) }
+ buckets = Hash.new { |h, k| h[k] = [] }
+ buckets[:association_join] = valid_association_list(outer_joins)
build_join_query(manager, buckets, Arel::Nodes::OuterJoin, aliases)
end
def build_joins(manager, joins, aliases)
+ buckets = Hash.new { |h, k| h[k] = [] }
+
unless left_outer_joins_values.empty?
left_joins = valid_association_list(left_outer_joins_values.flatten)
- joins.unshift construct_join_dependency(left_joins, Arel::Nodes::OuterJoin)
+ buckets[:stashed_join] << construct_join_dependency(left_joins, Arel::Nodes::OuterJoin)
end
- buckets = joins.group_by do |join|
+ joins.map! do |join|
+ if join.is_a?(String)
+ table.create_string_join(Arel.sql(join.strip)) unless join.blank?
+ else
+ join
+ end
+ end.compact_blank!
+
+ while joins.first.is_a?(Arel::Nodes::Join)
+ join_node = joins.shift
+ if join_node.is_a?(Arel::Nodes::StringJoin) && !buckets[:stashed_join].empty?
+ buckets[:join_node] << join_node
+ else
+ buckets[:leading_join] << join_node
+ end
+ end
+
+ joins.each do |join|
case join
- when String
- :string_join
when Hash, Symbol, Array
- :association_join
+ buckets[:association_join] << join
when ActiveRecord::Associations::JoinDependency
- :stashed_join
+ buckets[:stashed_join] << join
when Arel::Nodes::Join
- :join_node
+ buckets[:join_node] << join
else
raise "unknown class: %s" % join.class.name
end
@@ -1124,25 +1142,21 @@ module ActiveRecord
end
def build_join_query(manager, buckets, join_type, aliases)
- buckets.default = []
-
association_joins = buckets[:association_join]
stashed_joins = buckets[:stashed_join]
+ leading_joins = buckets[:leading_join].tap(&:uniq!)
join_nodes = buckets[:join_node].tap(&:uniq!)
- string_joins = buckets[:string_join].compact_blank!.map!(&:strip).tap(&:uniq!)
-
- string_joins.map! { |join| table.create_string_join(Arel.sql(join)) }
join_sources = manager.join_sources
- join_sources.concat(join_nodes) unless join_nodes.empty?
+ join_sources.concat(leading_joins) unless leading_joins.empty?
unless association_joins.empty? && stashed_joins.empty?
- alias_tracker = alias_tracker(join_nodes + string_joins, aliases)
+ alias_tracker = alias_tracker(leading_joins + join_nodes, aliases)
join_dependency = construct_join_dependency(association_joins, join_type)
join_sources.concat(join_dependency.join_constraints(stashed_joins, alias_tracker))
end
- join_sources.concat(string_joins) unless string_joins.empty?
+ join_sources.concat(join_nodes) unless join_nodes.empty?
end
def build_select(arel)
diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb
index 5d1ce19829..300e67b0aa 100644
--- a/activerecord/lib/active_record/tasks/database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/database_tasks.rb
@@ -4,7 +4,6 @@ require "active_record/database_configurations"
module ActiveRecord
module Tasks # :nodoc:
- class DatabaseAlreadyExists < StandardError; end # :nodoc:
class DatabaseNotSupported < StandardError; end # :nodoc:
# ActiveRecord::Tasks::DatabaseTasks is a utility class, which encapsulates
diff --git a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
index e3efeb75b5..b9a8ccb22c 100644
--- a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb
@@ -15,12 +15,6 @@ module ActiveRecord
establish_connection configuration_without_database
connection.create_database configuration["database"], creation_options
establish_connection configuration
- rescue ActiveRecord::StatementInvalid => error
- if connection.error_number(error.cause) == ER_DB_CREATE_EXISTS
- raise DatabaseAlreadyExists
- else
- raise
- end
end
def drop
diff --git a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
index 626ffdfdf9..fc37db216d 100644
--- a/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/postgresql_database_tasks.rb
@@ -21,12 +21,6 @@ module ActiveRecord
connection.create_database configuration["database"],
configuration.merge("encoding" => encoding)
establish_connection configuration
- rescue ActiveRecord::StatementInvalid => error
- if error.cause.is_a?(PG::DuplicateDatabase)
- raise DatabaseAlreadyExists
- else
- raise
- end
end
def drop
diff --git a/activerecord/lib/arel/visitors/mysql.rb b/activerecord/lib/arel/visitors/mysql.rb
index dd77cfdf66..6cb866715f 100644
--- a/activerecord/lib/arel/visitors/mysql.rb
+++ b/activerecord/lib/arel/visitors/mysql.rb
@@ -48,6 +48,14 @@ module Arel # :nodoc: all
visit_Arel_Nodes_IsNotDistinctFrom o, collector
end
+ def visit_Arel_Nodes_Regexp(o, collector)
+ infix_value o, collector, " REGEXP "
+ end
+
+ def visit_Arel_Nodes_NotRegexp(o, collector)
+ infix_value o, collector, " NOT REGEXP "
+ end
+
# In the simple case, MySQL allows us to place JOINs directly into the UPDATE
# query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
# these, we must use a subquery.
diff --git a/activerecord/lib/arel/visitors/oracle.rb b/activerecord/lib/arel/visitors/oracle.rb
index aab66301ef..c54aec71a6 100644
--- a/activerecord/lib/arel/visitors/oracle.rb
+++ b/activerecord/lib/arel/visitors/oracle.rb
@@ -9,7 +9,7 @@ module Arel # :nodoc: all
# if need to select first records without ORDER BY and GROUP BY and without DISTINCT
# then can use simple ROWNUM in WHERE clause
- if o.limit && o.orders.empty? && o.cores.first.groups.empty? && !o.offset && o.cores.first.set_quantifier.class.to_s !~ /Distinct/
+ if o.limit && o.orders.empty? && o.cores.first.groups.empty? && !o.offset && !o.cores.first.set_quantifier.class.to_s.match?(/Distinct/)
o.cores.last.wheres.push Nodes::LessThanOrEqual.new(
Nodes::SqlLiteral.new("ROWNUM"), o.limit.expr
)