diff --git a/activerecord/lib/active_record/connection_adapters.rb b/activerecord/lib/active_record/connection_adapters.rb
index a78980ad722..01c3484ca73 100644
--- a/activerecord/lib/active_record/connection_adapters.rb
+++ b/activerecord/lib/active_record/connection_adapters.rb
@@ -10,6 +10,7 @@ module ActiveRecord
autoload :Column
autoload :ConnectionSpecification
+ autoload :Resolver
autoload_at "active_record/connection_adapters/abstract/schema_definitions" do
autoload :IndexDefinition
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index 5a8accc18e3..26a3cfd2885 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -385,14 +385,14 @@ module ActiveRecord
@spec = spec
- @checkout_timeout = (spec.underlying_configuration_hash[:checkout_timeout] && spec.underlying_configuration_hash[:checkout_timeout].to_f) || 5
- if @idle_timeout = spec.underlying_configuration_hash.fetch(:idle_timeout, 300)
+ @checkout_timeout = (spec.db_config.configuration_hash[:checkout_timeout] && spec.db_config.configuration_hash[:checkout_timeout].to_f) || 5
+ if @idle_timeout = spec.db_config.configuration_hash.fetch(:idle_timeout, 300)
@idle_timeout = @idle_timeout.to_f
@idle_timeout = nil if @idle_timeout <= 0
end
# default max pool size to 5
- @size = (spec.underlying_configuration_hash[:pool] && spec.underlying_configuration_hash[:pool].to_i) || 5
+ @size = (spec.db_config.configuration_hash[:pool] && spec.db_config.configuration_hash[:pool].to_i) || 5
# This variable tracks the cache of threads mapped to reserved connections, with the
# sole purpose of speeding up the +connection+ method. It is not the authoritative
@@ -422,7 +422,7 @@ module ActiveRecord
# +reaping_frequency+ is configurable mostly for historical reasons, but it could
# also be useful if someone wants a very low +idle_timeout+.
- reaping_frequency = spec.underlying_configuration_hash.fetch(:reaping_frequency, 60)
+ reaping_frequency = spec.db_config.configuration_hash.fetch(:reaping_frequency, 60)
@reaper = Reaper.new(self, reaping_frequency && reaping_frequency.to_f)
@reaper.run
end
@@ -505,7 +505,7 @@ module ActiveRecord
# Raises:
# - ActiveRecord::ExclusiveConnectionTimeoutError if unable to gain ownership of all
# connections in the pool within a timeout interval (default duration is
- # spec.underlying_configuration_hash[:checkout_timeout] * 2 seconds).
+ # spec.db_config.configuration_hash[:checkout_timeout] * 2 seconds).
def disconnect(raise_on_acquisition_timeout = true)
with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
synchronize do
@@ -526,7 +526,7 @@ module ActiveRecord
#
# The pool first tries to gain ownership of all connections. If unable to
# do so within a timeout interval (default duration is
- # spec.underlying_configuration_hash[:checkout_timeout] * 2 seconds), then the pool is forcefully
+ # spec.db_config.configuration_hash[:checkout_timeout] * 2 seconds), then the pool is forcefully
# disconnected without any regard for other connection owning threads.
def disconnect!
disconnect(false)
@@ -557,7 +557,7 @@ module ActiveRecord
# Raises:
# - ActiveRecord::ExclusiveConnectionTimeoutError if unable to gain ownership of all
# connections in the pool within a timeout interval (default duration is
- # spec.underlying_configuration_hash[:checkout_timeout] * 2 seconds).
+ # spec.db_config.configuration_hash[:checkout_timeout] * 2 seconds).
def clear_reloadable_connections(raise_on_acquisition_timeout = true)
with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
synchronize do
@@ -579,7 +579,7 @@ module ActiveRecord
#
# The pool first tries to gain ownership of all connections. If unable to
# do so within a timeout interval (default duration is
- # spec.underlying_configuration_hash[:checkout_timeout] * 2 seconds), then the pool forcefully
+ # spec.db_config.configuration_hash[:checkout_timeout] * 2 seconds), then the pool forcefully
# clears the cache and reloads connections without any regard for other
# connection owning threads.
def clear_reloadable_connections!
@@ -899,7 +899,7 @@ module ActiveRecord
alias_method :release, :remove_connection_from_thread_cache
def new_connection
- Base.send(spec.adapter_method, spec.underlying_configuration_hash).tap do |conn|
+ Base.send(spec.db_config.adapter_method, spec.db_config.configuration_hash).tap do |conn|
conn.check_version
end
end
@@ -1063,7 +1063,7 @@ module ActiveRecord
alias :connection_pools :connection_pool_list
def establish_connection(config)
- resolver = ConnectionSpecification::Resolver.new(Base.configurations)
+ resolver = Resolver.new(Base.configurations)
spec = resolver.spec(config)
remove_connection(spec.name)
@@ -1074,7 +1074,7 @@ module ActiveRecord
}
if spec
payload[:spec_name] = spec.name
- payload[:config] = spec.underlying_configuration_hash
+ payload[:config] = spec.db_config.configuration_hash
end
message_bus.instrument("!connection.active_record", payload) do
@@ -1149,7 +1149,7 @@ module ActiveRecord
if pool = owner_to_pool.delete(spec_name)
pool.automatic_reconnect = false
pool.disconnect!
- pool.spec.underlying_configuration_hash
+ pool.spec.db_config.configuration_hash
end
end
@@ -1164,7 +1164,7 @@ module ActiveRecord
# A connection was established in an ancestor process that must have
# subsequently forked. We can't reuse the connection, but we can copy
# the specification and establish a new connection with it.
- establish_connection(ancestor_pool.spec.to_hash).tap do |pool|
+ establish_connection(ancestor_pool.spec.db_config.configuration_hash).tap do |pool|
pool.schema_cache = ancestor_pool.schema_cache if ancestor_pool.schema_cache
end
else
diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
index 77adc163f42..42deb1ccda9 100644
--- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb
+++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb
@@ -4,259 +4,11 @@ require "uri"
module ActiveRecord
module ConnectionAdapters
- class ConnectionSpecification #:nodoc:
- attr_reader :name, :adapter_method, :db_config
+ class ConnectionSpecification # :nodoc:
+ attr_reader :name, :db_config
- def initialize(name, db_config, adapter_method)
- @name, @db_config, @adapter_method = name, db_config, adapter_method
- end
-
- def underlying_configuration_hash
- @db_config.configuration_hash
- end
-
- def initialize_dup(original)
- @db_config = original.db_config.dup
- end
-
- def to_hash
- underlying_configuration_hash.dup.merge(name: @name)
- end
-
- # Expands a connection string into a hash.
- class ConnectionUrlResolver # :nodoc:
- # == Example
- #
- # url = "postgresql://foo:bar@localhost:9000/foo_test?pool=5&timeout=3000"
- # ConnectionUrlResolver.new(url).to_hash
- # # => {
- # adapter: "postgresql",
- # host: "localhost",
- # port: 9000,
- # database: "foo_test",
- # username: "foo",
- # password: "bar",
- # pool: "5",
- # timeout: "3000"
- # }
- def initialize(url)
- raise "Database URL cannot be empty" if url.blank?
- @uri = uri_parser.parse(url)
- @adapter = @uri.scheme && @uri.scheme.tr("-", "_")
- @adapter = "postgresql" if @adapter == "postgres"
-
- if @uri.opaque
- @uri.opaque, @query = @uri.opaque.split("?", 2)
- else
- @query = @uri.query
- end
- end
-
- # Converts the given URL to a full connection hash.
- def to_hash
- config = raw_config.compact_blank
- config.map { |key, value| config[key] = uri_parser.unescape(value) if value.is_a? String }
- config
- end
-
- private
- attr_reader :uri
-
- def uri_parser
- @uri_parser ||= URI::Parser.new
- end
-
- # Converts the query parameters of the URI into a hash.
- #
- # "localhost?pool=5&reaping_frequency=2"
- # # => { pool: "5", reaping_frequency: "2" }
- #
- # returns empty hash if no query present.
- #
- # "localhost"
- # # => {}
- def query_hash
- Hash[(@query || "").split("&").map { |pair| pair.split("=") }].symbolize_keys
- end
-
- def raw_config
- if uri.opaque
- query_hash.merge(
- adapter: @adapter,
- database: uri.opaque
- )
- else
- query_hash.merge(
- adapter: @adapter,
- username: uri.user,
- password: uri.password,
- port: uri.port,
- database: database_from_path,
- host: uri.hostname
- )
- end
- end
-
- # Returns name of the database.
- def database_from_path
- if @adapter == "sqlite3"
- # 'sqlite3:/foo' is absolute, because that makes sense. The
- # corresponding relative version, 'sqlite3:foo', is handled
- # elsewhere, as an "opaque".
-
- uri.path
- else
- # Only SQLite uses a filename as the "database" name; for
- # anything else, a leading slash would be silly.
-
- uri.path.sub(%r{^/}, "")
- end
- end
- end
-
- ##
- # Builds a ConnectionSpecification from user input.
- class Resolver # :nodoc:
- attr_reader :configurations
-
- # Accepts a list of db config objects.
- def initialize(configurations)
- @configurations = configurations
- end
-
- # Returns an instance of ConnectionSpecification for a given adapter.
- # Accepts a hash one layer deep that contains all connection information.
- #
- # == Example
- #
- # config = { "production" => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" } }
- # spec = Resolver.new(config).spec(:production)
- # spec.adapter_method
- # # => "sqlite3_connection"
- # spec.underlying_configuration_hash
- # # => { host: "localhost", database: "foo", adapter: "sqlite3" }
- #
- def spec(config)
- pool_name = config if config.is_a?(Symbol)
-
- db_config = resolve(config, pool_name)
- spec = db_config.configuration_hash
-
- 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 LoadError => e
- # 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 LoadError, "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 LoadError, "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"
-
- unless ActiveRecord::Base.respond_to?(adapter_method)
- raise AdapterNotFound, "database configuration specifies nonexistent #{spec[:adapter]} adapter"
- end
-
- ConnectionSpecification.new(spec.delete(:name) || "primary", db_config, adapter_method)
- end
-
- # Returns fully resolved connection, accepts hash, string or symbol.
- # Always returns a DatabaseConfiguration::DatabaseConfig
- #
- # == Examples
- #
- # Symbol representing current environment.
- #
- # Resolver.new("production" => {}).resolve(:production)
- # # => DatabaseConfigurations::HashConfig.new(env_name: "production", config: {})
- #
- # One layer deep hash of connection values.
- #
- # Resolver.new({}).resolve("adapter" => "sqlite3")
- # # => DatabaseConfigurations::HashConfig.new(config: {"adapter" => "sqlite3"})
- #
- # Connection URL.
- #
- # Resolver.new({}).resolve("postgresql://localhost/foo")
- # # => DatabaseConfigurations::UrlConfig.new(config: {"adapter" => "postgresql", "host" => "localhost", "database" => "foo"})
- #
- def resolve(config_or_env, pool_name = nil)
- env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call.to_s
-
- case config_or_env
- when Symbol
- resolve_symbol_connection(config_or_env, pool_name)
- when String
- DatabaseConfigurations::UrlConfig.new(env, "primary", config_or_env)
- when Hash
- DatabaseConfigurations::HashConfig.new(env, "primary", config_or_env)
- when DatabaseConfigurations::DatabaseConfig
- config_or_env
- else
- raise TypeError, "Invalid type for configuration. Expected Symbol, String, or Hash. Got #{config_or_env.inspect}"
- end
- end
-
- private
- # Takes the environment such as +:production+ or +:development+ and a
- # pool name the corresponds to the name given by the connection pool
- # to the connection. That pool name is merged into the hash with the
- # name key.
- #
- # This requires that the @configurations was initialized with a key that
- # matches.
- #
- # configurations = #
- # ]>
- #
- # Resolver.new(configurations).resolve_symbol_connection(:production, "primary")
- # # => DatabaseConfigurations::HashConfig(config: database: "my_db", env_name: "production", spec_name: "primary")
- def resolve_symbol_connection(env_name, pool_name)
- db_config = configurations.find_db_config(env_name)
-
- if db_config
- config = db_config.configuration_hash.merge(name: pool_name.to_s)
- DatabaseConfigurations::HashConfig.new(db_config.env_name, db_config.spec_name, config)
- else
- raise AdapterNotSpecified, <<~MSG
- The `#{env_name}` database is not configured for the `#{ActiveRecord::ConnectionHandling::DEFAULT_ENV.call}` environment.
-
- Available databases configurations are:
-
- #{build_configuration_sentence}
- MSG
- end
- end
-
- def build_configuration_sentence # :nodoc:
- configs = configurations.configs_for(include_replicas: true)
-
- configs.group_by(&:env_name).map do |env, config|
- namespaces = config.map(&:spec_name)
- if namespaces.size > 1
- "#{env}: #{namespaces.join(", ")}"
- else
- env
- end
- end.join("\n")
- end
+ def initialize(name, db_config)
+ @name, @db_config = name, db_config
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/resolver.rb b/activerecord/lib/active_record/connection_adapters/resolver.rb
new file mode 100644
index 00000000000..49061bd579b
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/resolver.rb
@@ -0,0 +1,145 @@
+# frozen_string_literal: true
+
+module ActiveRecord
+ module ConnectionAdapters
+ # Builds a ConnectionSpecification from user input.
+ class Resolver # :nodoc:
+ attr_reader :configurations
+
+ # Accepts a list of db config objects.
+ def initialize(configurations)
+ @configurations = configurations
+ end
+
+ # Returns an instance of ConnectionSpecification for a given adapter.
+ # Accepts a hash one layer deep that contains all connection information.
+ #
+ # == Example
+ #
+ # config = { "production" => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" } }
+ # spec = Resolver.new(config).spec(:production)
+ # spec.db_config.configuration_hash
+ # # => { host: "localhost", database: "foo", adapter: "sqlite3" }
+ #
+ def spec(config)
+ pool_name = config if config.is_a?(Symbol)
+
+ db_config = resolve(config, pool_name)
+ spec = db_config.configuration_hash
+
+ 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 LoadError => e
+ # 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 LoadError, "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 LoadError, "Error loading the '#{spec[:adapter]}' Active Record adapter. Missing a gem it depends on? #{e.message}", e.backtrace
+ end
+ end
+
+ unless ActiveRecord::Base.respond_to?(db_config.adapter_method)
+ raise AdapterNotFound, "database configuration specifies nonexistent #{spec[:adapter]} adapter"
+ end
+
+ ConnectionSpecification.new(spec.delete(:name) || "primary", db_config)
+ end
+
+ # Returns fully resolved connection, accepts hash, string or symbol.
+ # Always returns a DatabaseConfiguration::DatabaseConfig
+ #
+ # == Examples
+ #
+ # Symbol representing current environment.
+ #
+ # Resolver.new("production" => {}).resolve(:production)
+ # # => DatabaseConfigurations::HashConfig.new(env_name: "production", config: {})
+ #
+ # One layer deep hash of connection values.
+ #
+ # Resolver.new({}).resolve("adapter" => "sqlite3")
+ # # => DatabaseConfigurations::HashConfig.new(config: {"adapter" => "sqlite3"})
+ #
+ # Connection URL.
+ #
+ # Resolver.new({}).resolve("postgresql://localhost/foo")
+ # # => DatabaseConfigurations::UrlConfig.new(config: {"adapter" => "postgresql", "host" => "localhost", "database" => "foo"})
+ #
+ def resolve(config_or_env, pool_name = nil)
+ env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call.to_s
+
+ case config_or_env
+ when Symbol
+ resolve_symbol_connection(config_or_env, pool_name)
+ when String
+ DatabaseConfigurations::UrlConfig.new(env, "primary", config_or_env)
+ when Hash
+ DatabaseConfigurations::HashConfig.new(env, "primary", config_or_env)
+ when DatabaseConfigurations::DatabaseConfig
+ config_or_env
+ else
+ raise TypeError, "Invalid type for configuration. Expected Symbol, String, or Hash. Got #{config_or_env.inspect}"
+ end
+ end
+
+ private
+ # Takes the environment such as +:production+ or +:development+ and a
+ # pool name the corresponds to the name given by the connection pool
+ # to the connection. That pool name is merged into the hash with the
+ # name key.
+ #
+ # This requires that the @configurations was initialized with a key that
+ # matches.
+ #
+ # configurations = #
+ # ]>
+ #
+ # Resolver.new(configurations).resolve_symbol_connection(:production, "primary")
+ # # => DatabaseConfigurations::HashConfig(config: database: "my_db", env_name: "production", spec_name: "primary")
+ def resolve_symbol_connection(env_name, pool_name)
+ db_config = configurations.find_db_config(env_name)
+
+ if db_config
+ config = db_config.configuration_hash.merge(name: pool_name.to_s)
+ DatabaseConfigurations::HashConfig.new(db_config.env_name, db_config.spec_name, config)
+ else
+ raise AdapterNotSpecified, <<~MSG
+ The `#{env_name}` database is not configured for the `#{ActiveRecord::ConnectionHandling::DEFAULT_ENV.call}` environment.
+
+ Available databases configurations are:
+
+ #{build_configuration_sentence}
+ MSG
+ end
+ end
+
+ def build_configuration_sentence # :nodoc:
+ configs = configurations.configs_for(include_replicas: true)
+
+ configs.group_by(&:env_name).map do |env, config|
+ namespaces = config.map(&:spec_name)
+ if namespaces.size > 1
+ "#{env}: #{namespaces.join(", ")}"
+ else
+ env
+ end
+ end.join("\n")
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb
index 68732d01449..a2ebaa6ffd8 100644
--- a/activerecord/lib/active_record/connection_handling.rb
+++ b/activerecord/lib/active_record/connection_handling.rb
@@ -183,7 +183,7 @@ module ActiveRecord
pool_name = primary_class? ? "primary" : name
self.connection_specification_name = pool_name
- resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(Base.configurations)
+ resolver = ConnectionAdapters::Resolver.new(Base.configurations)
config_hash = resolver.resolve(config_or_env, pool_name).configuration_hash
config_hash[:name] = pool_name
@@ -227,7 +227,7 @@ module ActiveRecord
#
# Please use only for reading.
def connection_config
- connection_pool.spec.underlying_configuration_hash
+ connection_pool.spec.db_config.configuration_hash
end
def connection_pool
diff --git a/activerecord/lib/active_record/database_configurations.rb b/activerecord/lib/active_record/database_configurations.rb
index 73d607534b6..d84d57bb057 100644
--- a/activerecord/lib/active_record/database_configurations.rb
+++ b/activerecord/lib/active_record/database_configurations.rb
@@ -3,6 +3,7 @@
require "active_record/database_configurations/database_config"
require "active_record/database_configurations/hash_config"
require "active_record/database_configurations/url_config"
+require "active_record/database_configurations/connection_url_resolver"
module ActiveRecord
# ActiveRecord::DatabaseConfigurations returns an array of DatabaseConfig
diff --git a/activerecord/lib/active_record/database_configurations/connection_url_resolver.rb b/activerecord/lib/active_record/database_configurations/connection_url_resolver.rb
new file mode 100644
index 00000000000..bf56b03bb2e
--- /dev/null
+++ b/activerecord/lib/active_record/database_configurations/connection_url_resolver.rb
@@ -0,0 +1,96 @@
+# frozen_string_literal: true
+
+module ActiveRecord
+ class DatabaseConfigurations
+ # Expands a connection string into a hash.
+ class ConnectionUrlResolver # :nodoc:
+ # == Example
+ #
+ # url = "postgresql://foo:bar@localhost:9000/foo_test?pool=5&timeout=3000"
+ # ConnectionUrlResolver.new(url).to_hash
+ # # => {
+ # adapter: "postgresql",
+ # host: "localhost",
+ # port: 9000,
+ # database: "foo_test",
+ # username: "foo",
+ # password: "bar",
+ # pool: "5",
+ # timeout: "3000"
+ # }
+ def initialize(url)
+ raise "Database URL cannot be empty" if url.blank?
+ @uri = uri_parser.parse(url)
+ @adapter = @uri.scheme && @uri.scheme.tr("-", "_")
+ @adapter = "postgresql" if @adapter == "postgres"
+
+ if @uri.opaque
+ @uri.opaque, @query = @uri.opaque.split("?", 2)
+ else
+ @query = @uri.query
+ end
+ end
+
+ # Converts the given URL to a full connection hash.
+ def to_hash
+ config = raw_config.compact_blank
+ config.map { |key, value| config[key] = uri_parser.unescape(value) if value.is_a? String }
+ config
+ end
+
+ private
+ attr_reader :uri
+
+ def uri_parser
+ @uri_parser ||= URI::Parser.new
+ end
+
+ # Converts the query parameters of the URI into a hash.
+ #
+ # "localhost?pool=5&reaping_frequency=2"
+ # # => { pool: "5", reaping_frequency: "2" }
+ #
+ # returns empty hash if no query present.
+ #
+ # "localhost"
+ # # => {}
+ def query_hash
+ Hash[(@query || "").split("&").map { |pair| pair.split("=") }].symbolize_keys
+ end
+
+ def raw_config
+ if uri.opaque
+ query_hash.merge(
+ adapter: @adapter,
+ database: uri.opaque
+ )
+ else
+ query_hash.merge(
+ adapter: @adapter,
+ username: uri.user,
+ password: uri.password,
+ port: uri.port,
+ database: database_from_path,
+ host: uri.hostname
+ )
+ end
+ end
+
+ # Returns name of the database.
+ def database_from_path
+ if @adapter == "sqlite3"
+ # 'sqlite3:/foo' is absolute, because that makes sense. The
+ # corresponding relative version, 'sqlite3:foo', is handled
+ # elsewhere, as an "opaque".
+
+ uri.path
+ else
+ # Only SQLite uses a filename as the "database" name; for
+ # anything else, a leading slash would be silly.
+
+ uri.path.sub(%r{^/}, "")
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/database_configurations/database_config.rb b/activerecord/lib/active_record/database_configurations/database_config.rb
index cb19e9bc088..797da7fda77 100644
--- a/activerecord/lib/active_record/database_configurations/database_config.rb
+++ b/activerecord/lib/active_record/database_configurations/database_config.rb
@@ -18,8 +18,12 @@ module ActiveRecord
configuration_hash.stringify_keys
end
- def initialize_dup(original)
- @config = original.configuration_hash.dup
+ def adapter_method
+ "#{adapter}_connection"
+ end
+
+ def adapter
+ configuration_hash[:adapter]
end
def replica?
diff --git a/activerecord/lib/active_record/database_configurations/hash_config.rb b/activerecord/lib/active_record/database_configurations/hash_config.rb
index 9ba849717f8..30a0e0cf867 100644
--- a/activerecord/lib/active_record/database_configurations/hash_config.rb
+++ b/activerecord/lib/active_record/database_configurations/hash_config.rb
@@ -53,7 +53,7 @@ module ActiveRecord
private
def resolve_url_key
if configuration_hash[:url] && !configuration_hash[:url].match?(/^jdbc:/)
- connection_hash = ActiveRecord::ConnectionAdapters::ConnectionSpecification::ConnectionUrlResolver.new(configuration_hash[:url]).to_hash
+ connection_hash = ConnectionUrlResolver.new(configuration_hash[:url]).to_hash
configuration_hash.merge!(connection_hash)
end
end
diff --git a/activerecord/lib/active_record/database_configurations/url_config.rb b/activerecord/lib/active_record/database_configurations/url_config.rb
index 27d8b1b1ad8..8f45abca708 100644
--- a/activerecord/lib/active_record/database_configurations/url_config.rb
+++ b/activerecord/lib/active_record/database_configurations/url_config.rb
@@ -64,7 +64,7 @@ module ActiveRecord
if url.nil? || /^jdbc:/.match?(url)
{ url: url }
else
- ActiveRecord::ConnectionAdapters::ConnectionSpecification::ConnectionUrlResolver.new(url).to_hash
+ ConnectionUrlResolver.new(url).to_hash
end
end
diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb
index 1dca822741a..cc8b57b7ceb 100644
--- a/activerecord/lib/active_record/tasks/database_tasks.rb
+++ b/activerecord/lib/active_record/tasks/database_tasks.rb
@@ -116,7 +116,7 @@ module ActiveRecord
if options.has_key?(:config)
@current_config = options[:config]
else
- @current_config ||= ActiveRecord::Base.configurations.configs_for(env_name: options[:env], spec_name: options[:spec]).underlying_configuration_hash
+ @current_config ||= ActiveRecord::Base.configurations.configs_for(env_name: options[:env], spec_name: options[:spec]).db_config.configuration_hash
end
end
@@ -136,7 +136,7 @@ module ActiveRecord
old_pool = ActiveRecord::Base.connection_handler.retrieve_connection_pool(ActiveRecord::Base.connection_specification_name)
each_local_configuration { |configuration| create configuration }
if old_pool
- ActiveRecord::Base.connection_handler.establish_connection(old_pool.spec.to_hash)
+ ActiveRecord::Base.connection_handler.establish_connection(old_pool.spec.db_config.configuration_hash)
end
end
diff --git a/activerecord/test/cases/adapters/mysql2/schema_test.rb b/activerecord/test/cases/adapters/mysql2/schema_test.rb
index b492560d078..53a2835ae1b 100644
--- a/activerecord/test/cases/adapters/mysql2/schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/schema_test.rb
@@ -11,7 +11,7 @@ module ActiveRecord
def setup
@connection = ActiveRecord::Base.connection
- db = Post.connection_pool.spec.underlying_configuration_hash[:database]
+ db = Post.connection_pool.spec.db_config.configuration_hash[:database]
table = Post.table_name
@db_name = db
diff --git a/activerecord/test/cases/connection_adapters/adapter_leasing_test.rb b/activerecord/test/cases/connection_adapters/adapter_leasing_test.rb
index 08a2729c419..6f112692471 100644
--- a/activerecord/test/cases/connection_adapters/adapter_leasing_test.rb
+++ b/activerecord/test/cases/connection_adapters/adapter_leasing_test.rb
@@ -40,7 +40,7 @@ module ActiveRecord
def test_close
db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new("test", "primary", {})
- pool = Pool.new(ConnectionSpecification.new("primary", db_config, nil))
+ pool = Pool.new(ConnectionSpecification.new("primary", db_config))
pool.insert_connection_for_test! @adapter
@adapter.pool = pool
diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
index 186415bede5..3c8f757bf90 100644
--- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb
+++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
@@ -27,13 +27,14 @@ module ActiveRecord
ENV["RACK_ENV"] = original_rack_env
end
- def test_establish_connection_uses_spec_name
+ def test_establish_connection_uses_config_hash_with_spec_name
old_config = ActiveRecord::Base.configurations
config = { "readonly" => { "adapter" => "sqlite3", "pool" => "5" } }
ActiveRecord::Base.configurations = config
- resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(ActiveRecord::Base.configurations)
- spec = resolver.spec(:readonly)
- @handler.establish_connection(spec.to_hash)
+ resolver = ConnectionAdapters::Resolver.new(ActiveRecord::Base.configurations)
+ config_hash = resolver.resolve(config["readonly"], "readonly").configuration_hash
+ config_hash[:name] = "readonly"
+ @handler.establish_connection(config_hash)
assert_not_nil @handler.retrieve_connection_pool("readonly")
ensure
@@ -62,13 +63,13 @@ module ActiveRecord
@handler.establish_connection(:readonly)
assert_not_nil pool = @handler.retrieve_connection_pool("readonly")
- assert_equal "db/readonly.sqlite3", pool.spec.underlying_configuration_hash[:database]
+ assert_equal "db/readonly.sqlite3", pool.spec.db_config.configuration_hash[:database]
assert_not_nil pool = @handler.retrieve_connection_pool("primary")
- assert_equal "db/primary.sqlite3", pool.spec.underlying_configuration_hash[:database]
+ assert_equal "db/primary.sqlite3", pool.spec.db_config.configuration_hash[:database]
assert_not_nil pool = @handler.retrieve_connection_pool("common")
- assert_equal "db/common.sqlite3", pool.spec.underlying_configuration_hash[:database]
+ assert_equal "db/common.sqlite3", pool.spec.db_config.configuration_hash[:database]
ensure
ActiveRecord::Base.configurations = @prev_configs
ENV["RAILS_ENV"] = previous_env
@@ -92,7 +93,7 @@ module ActiveRecord
ActiveRecord::Base.establish_connection
- assert_match "db/primary.sqlite3", ActiveRecord::Base.connection.pool.spec.underlying_configuration_hash[:database]
+ assert_match "db/primary.sqlite3", ActiveRecord::Base.connection.pool.spec.db_config.configuration_hash[:database]
ensure
ActiveRecord::Base.configurations = @prev_configs
ENV["RAILS_ENV"] = previous_env
@@ -115,7 +116,7 @@ module ActiveRecord
ActiveRecord::Base.establish_connection
- assert_match "db/primary.sqlite3", ActiveRecord::Base.connection.pool.spec.underlying_configuration_hash[:database]
+ assert_match "db/primary.sqlite3", ActiveRecord::Base.connection.pool.spec.db_config.configuration_hash[:database]
ensure
ActiveRecord::Base.configurations = @prev_configs
ENV["RAILS_ENV"] = previous_env
@@ -131,7 +132,7 @@ module ActiveRecord
@handler.establish_connection(:development)
assert_not_nil pool = @handler.retrieve_connection_pool("development")
- assert_equal "db/primary.sqlite3", pool.spec.underlying_configuration_hash[:database]
+ assert_equal "db/primary.sqlite3", pool.spec.db_config.configuration_hash[:database]
ensure
ActiveRecord::Base.configurations = @prev_configs
end
@@ -146,7 +147,7 @@ module ActiveRecord
@handler.establish_connection(:development_readonly)
assert_not_nil pool = @handler.retrieve_connection_pool("development_readonly")
- assert_equal "db/readonly.sqlite3", pool.spec.underlying_configuration_hash[:database]
+ assert_equal "db/readonly.sqlite3", pool.spec.db_config.configuration_hash[:database]
ensure
ActiveRecord::Base.configurations = @prev_configs
end
@@ -210,7 +211,7 @@ module ActiveRecord
assert_same klass2.connection, ActiveRecord::Base.connection
- pool = klass2.establish_connection(ActiveRecord::Base.connection_pool.spec.underlying_configuration_hash)
+ pool = klass2.establish_connection(ActiveRecord::Base.connection_pool.spec.db_config.configuration_hash)
assert_same klass2.connection, pool.connection
assert_not_same klass2.connection, ActiveRecord::Base.connection
diff --git a/activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb b/activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb
index b93bb28fff2..e9acf344503 100644
--- a/activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb
+++ b/activerecord/test/cases/connection_adapters/connection_handlers_multi_db_test.rb
@@ -82,10 +82,10 @@ module ActiveRecord
ActiveRecord::Base.connects_to(database: { writing: :primary, reading: :readonly })
assert_not_nil pool = ActiveRecord::Base.connection_handlers[:writing].retrieve_connection_pool("primary")
- assert_equal "db/primary.sqlite3", pool.spec.underlying_configuration_hash[:database]
+ assert_equal "db/primary.sqlite3", pool.spec.db_config.configuration_hash[:database]
assert_not_nil pool = ActiveRecord::Base.connection_handlers[:reading].retrieve_connection_pool("primary")
- assert_equal "db/readonly.sqlite3", pool.spec.underlying_configuration_hash[:database]
+ assert_equal "db/readonly.sqlite3", pool.spec.db_config.configuration_hash[:database]
ensure
ActiveRecord::Base.configurations = @prev_configs
ActiveRecord::Base.establish_connection(:arunit)
@@ -140,10 +140,10 @@ module ActiveRecord
ActiveRecord::Base.connects_to(database: { default: :primary, readonly: :readonly })
assert_not_nil pool = ActiveRecord::Base.connection_handlers[:default].retrieve_connection_pool("primary")
- assert_equal "db/primary.sqlite3", pool.spec.underlying_configuration_hash[:database]
+ assert_equal "db/primary.sqlite3", pool.spec.db_config.configuration_hash[:database]
assert_not_nil pool = ActiveRecord::Base.connection_handlers[:readonly].retrieve_connection_pool("primary")
- assert_equal "db/readonly.sqlite3", pool.spec.underlying_configuration_hash[:database]
+ assert_equal "db/readonly.sqlite3", pool.spec.db_config.configuration_hash[:database]
ensure
ActiveRecord::Base.configurations = @prev_configs
ActiveRecord::Base.establish_connection(:arunit)
@@ -162,7 +162,7 @@ module ActiveRecord
assert_equal handler, ActiveRecord::Base.connection_handlers[:writing]
assert_not_nil pool = handler.retrieve_connection_pool("primary")
- assert_equal({ adapter: "postgresql", database: "bar", host: "localhost" }, pool.spec.underlying_configuration_hash)
+ assert_equal({ adapter: "postgresql", database: "bar", host: "localhost" }, pool.spec.db_config.configuration_hash)
end
ensure
ActiveRecord::Base.establish_connection(:arunit)
@@ -182,7 +182,7 @@ module ActiveRecord
assert_equal handler, ActiveRecord::Base.connection_handlers[:writing]
assert_not_nil pool = handler.retrieve_connection_pool("primary")
- assert_equal(config, pool.spec.underlying_configuration_hash)
+ assert_equal(config, pool.spec.db_config.configuration_hash)
end
ensure
ActiveRecord::Base.establish_connection(:arunit)
@@ -222,7 +222,7 @@ module ActiveRecord
assert_equal handler, ActiveRecord::Base.connection_handlers[:writing]
assert_not_nil pool = handler.retrieve_connection_pool("primary")
- assert_equal(config["default_env"]["animals"], pool.spec.underlying_configuration_hash)
+ assert_equal(config["default_env"]["animals"], pool.spec.db_config.configuration_hash)
end
ensure
ActiveRecord::Base.configurations = @prev_configs
@@ -249,7 +249,7 @@ module ActiveRecord
assert_equal handler, ActiveRecord::Base.connection_handlers[:writing]
assert_not_nil pool = handler.retrieve_connection_pool("primary")
- assert_equal(config["default_env"]["primary"], pool.spec.underlying_configuration_hash)
+ assert_equal(config["default_env"]["primary"], pool.spec.db_config.configuration_hash)
end
ensure
ActiveRecord::Base.configurations = @prev_configs
@@ -284,7 +284,7 @@ module ActiveRecord
ActiveRecord::Base.connects_to database: { writing: :development, reading: :development_readonly }
assert_not_nil pool = ActiveRecord::Base.connection_handlers[:reading].retrieve_connection_pool("primary")
- assert_equal "db/readonly.sqlite3", pool.spec.underlying_configuration_hash[:database]
+ assert_equal "db/readonly.sqlite3", pool.spec.db_config.configuration_hash[:database]
ensure
ActiveRecord::Base.configurations = @prev_configs
ActiveRecord::Base.establish_connection(:arunit)
diff --git a/activerecord/test/cases/connection_adapters/connection_specification_test.rb b/activerecord/test/cases/connection_adapters/connection_specification_test.rb
deleted file mode 100644
index 44b58c1dc24..00000000000
--- a/activerecord/test/cases/connection_adapters/connection_specification_test.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-require "cases/helper"
-
-module ActiveRecord
- module ConnectionAdapters
- class ConnectionSpecificationTest < ActiveRecord::TestCase
- def test_dup_deep_copy_config
- db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new("development", "primary", { a: :b })
- spec = ConnectionSpecification.new("primary", db_config, "bar")
- assert_not_equal(spec.underlying_configuration_hash.object_id, spec.dup.underlying_configuration_hash.object_id)
- end
- end
- end
-end
diff --git a/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb b/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb
index 7db06c5022a..9586fe6c8f0 100644
--- a/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb
+++ b/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb
@@ -24,7 +24,7 @@ module ActiveRecord
def resolve_spec(spec, config)
configs = ActiveRecord::DatabaseConfigurations.new(config)
- resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(configs)
+ resolver = ConnectionAdapters::Resolver.new(configs)
resolver.resolve(spec, spec).configuration_hash
end
diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb
index 64930db1bf1..b8749e629a8 100644
--- a/activerecord/test/cases/connection_pool_test.rb
+++ b/activerecord/test/cases/connection_pool_test.rb
@@ -199,7 +199,7 @@ module ActiveRecord
def test_idle_timeout_configuration
@pool.disconnect!
spec = ActiveRecord::Base.connection_pool.spec
- spec.underlying_configuration_hash.merge!(idle_timeout: "0.02")
+ spec.db_config.configuration_hash.merge!(idle_timeout: "0.02")
@pool = ConnectionPool.new(spec)
idle_conn = @pool.checkout
@pool.checkin(idle_conn)
@@ -224,7 +224,7 @@ module ActiveRecord
def test_disable_flush
@pool.disconnect!
spec = ActiveRecord::Base.connection_pool.spec
- spec.underlying_configuration_hash.merge!(idle_timeout: -5)
+ spec.db_config.configuration_hash.merge!(idle_timeout: -5)
@pool = ConnectionPool.new(spec)
idle_conn = @pool.checkout
@pool.checkin(idle_conn)
@@ -718,8 +718,12 @@ module ActiveRecord
private
def with_single_connection_pool
- one_conn_spec = ActiveRecord::Base.connection_pool.spec.dup
- one_conn_spec.underlying_configuration_hash[:pool] = 1 # this is safe to do, because .dupped ConnectionSpecification also auto-dups its config
+ old_config = ActiveRecord::Base.connection_pool.spec.db_config.configuration_hash
+ db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new("arunit", "primary", old_config.dup)
+ one_conn_spec = ConnectionSpecification.new("primary", db_config)
+
+ one_conn_spec.db_config.configuration_hash[:pool] = 1 # this is safe to do, because .dupped ConnectionSpecification also auto-dups its config
+
yield(pool = ConnectionPool.new(one_conn_spec))
ensure
pool.disconnect! if pool
diff --git a/activerecord/test/cases/connection_specification/resolver_test.rb b/activerecord/test/cases/connection_specification/resolver_test.rb
index 8609f1b584e..542f65bc974 100644
--- a/activerecord/test/cases/connection_specification/resolver_test.rb
+++ b/activerecord/test/cases/connection_specification/resolver_test.rb
@@ -8,13 +8,13 @@ module ActiveRecord
class ResolverTest < ActiveRecord::TestCase
def resolve(spec, config = {})
configs = ActiveRecord::DatabaseConfigurations.new(config)
- resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(configs)
+ resolver = ConnectionAdapters::Resolver.new(configs)
resolver.resolve(spec, spec).configuration_hash
end
def spec(spec, config = {})
configs = ActiveRecord::DatabaseConfigurations.new(config)
- resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(configs)
+ resolver = ConnectionAdapters::Resolver.new(configs)
resolver.spec(spec)
end
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index e5ad8fe441a..e1759ee3271 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -37,7 +37,7 @@ end
def in_memory_db?
current_adapter?(:SQLite3Adapter) &&
- ActiveRecord::Base.connection_pool.spec.underlying_configuration_hash[:database] == ":memory:"
+ ActiveRecord::Base.connection_pool.spec.db_config.configuration_hash[:database] == ":memory:"
end
def subsecond_precision_supported?
diff --git a/activerecord/test/cases/reaper_test.rb b/activerecord/test/cases/reaper_test.rb
index 833dea2e127..d718a4d26a7 100644
--- a/activerecord/test/cases/reaper_test.rb
+++ b/activerecord/test/cases/reaper_test.rb
@@ -58,7 +58,8 @@ module ActiveRecord
end
def test_pool_has_reaper
- spec = ActiveRecord::Base.connection_pool.spec.dup
+ config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", spec_name: "primary")
+ spec = ConnectionSpecification.new("primary", config)
pool = ConnectionPool.new spec
assert pool.reaper
@@ -67,17 +68,19 @@ module ActiveRecord
end
def test_reaping_frequency_configuration
- spec = ActiveRecord::Base.connection_pool.spec.dup
- spec.underlying_configuration_hash[:reaping_frequency] = "10.01"
+ spec = duplicated_spec
+ spec.db_config.configuration_hash[:reaping_frequency] = "10.01"
+
pool = ConnectionPool.new spec
+
assert_equal 10.01, pool.reaper.frequency
ensure
pool.discard!
end
def test_connection_pool_starts_reaper
- spec = ActiveRecord::Base.connection_pool.spec.dup
- spec.underlying_configuration_hash[:reaping_frequency] = "0.0001"
+ spec = duplicated_spec
+ spec.db_config.configuration_hash[:reaping_frequency] = "0.0001"
pool = ConnectionPool.new spec
@@ -94,8 +97,8 @@ module ActiveRecord
end
def test_reaper_works_after_pool_discard
- spec = ActiveRecord::Base.connection_pool.spec.dup
- spec.underlying_configuration_hash[:reaping_frequency] = "0.0001"
+ spec = duplicated_spec
+ spec.db_config.configuration_hash[:reaping_frequency] = "0.0001"
2.times do
pool = ConnectionPool.new spec
@@ -116,7 +119,7 @@ module ActiveRecord
# This doesn't test the reaper directly, but we want to test the action
# it would take on a discarded pool
def test_reap_flush_on_discarded_pool
- spec = ActiveRecord::Base.connection_pool.spec.dup
+ spec = duplicated_spec
pool = ConnectionPool.new spec
pool.discard!
@@ -125,8 +128,8 @@ module ActiveRecord
end
def test_connection_pool_starts_reaper_in_fork
- spec = ActiveRecord::Base.connection_pool.spec.dup
- spec.underlying_configuration_hash[:reaping_frequency] = "0.0001"
+ spec = duplicated_spec
+ spec.db_config.configuration_hash[:reaping_frequency] = "0.0001"
pool = ConnectionPool.new spec
pool.checkout
@@ -169,26 +172,33 @@ module ActiveRecord
pool.discard!
end
- def new_conn_in_thread(pool)
- event = Concurrent::Event.new
- conn = nil
-
- child = Thread.new do
- conn = pool.checkout
- event.set
- Thread.stop
+ private
+ def duplicated_spec
+ old_config = ActiveRecord::Base.connection_pool.spec.db_config.configuration_hash
+ db_config = ActiveRecord::DatabaseConfigurations::HashConfig.new("arunit", "primary", old_config.dup)
+ ConnectionSpecification.new("primary", db_config)
end
- event.wait
- [conn, child]
- end
+ def new_conn_in_thread(pool)
+ event = Concurrent::Event.new
+ conn = nil
- def wait_for_conn_idle(conn, timeout = 5)
- start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
- while conn.in_use? && Process.clock_gettime(Process::CLOCK_MONOTONIC) - start < timeout
- Thread.pass
+ child = Thread.new do
+ conn = pool.checkout
+ event.set
+ Thread.stop
+ end
+
+ event.wait
+ [conn, child]
+ end
+
+ def wait_for_conn_idle(conn, timeout = 5)
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
+ while conn.in_use? && Process.clock_gettime(Process::CLOCK_MONOTONIC) - start < timeout
+ Thread.pass
+ end
end
- end
end
end
end
diff --git a/railties/test/application/rake/multi_dbs_test.rb b/railties/test/application/rake/multi_dbs_test.rb
index 4997333c3c3..307a4d7cd6f 100644
--- a/railties/test/application/rake/multi_dbs_test.rb
+++ b/railties/test/application/rake/multi_dbs_test.rb
@@ -359,7 +359,7 @@ module ApplicationTests
db_migrate_and_schema_dump_and_load "schema"
app_file "db/seeds.rb", <<-RUBY
- print Book.connection.pool.spec.underlying_configuration_hash[:database]
+ print Book.connection.pool.spec.db_config.configuration_hash[:database]
RUBY
output = rails("db:seed")