module ActiveRecord::ConnectionHandling

def append_to_connected_to_stack(entry)

def append_to_connected_to_stack(entry)
  if shard_swapping_prohibited? && entry[:shard].present?
    raise ArgumentError, "cannot swap `shard` while shard swapping is prohibited."
  end
  connected_to_stack << entry
end

def clear_active_connections!(role = nil)

def clear_active_connections!(role = nil)
  deprecation_for_delegation(__method__)
  connection_handler.clear_active_connections!(role)
end

def clear_all_connections!(role = nil)

def clear_all_connections!(role = nil)
  deprecation_for_delegation(__method__)
  connection_handler.clear_all_connections!(role)
end

def clear_cache! # :nodoc:

:nodoc:
def clear_cache! # :nodoc:
  connection.schema_cache.clear!
end

def clear_query_caches_for_current_thread

Clears the query cache for all connections associated with the current thread.
def clear_query_caches_for_current_thread
  connection_handler.each_connection_pool do |pool|
    pool.connection.clear_query_cache if pool.active_connection?
  end
end

def clear_reloadable_connections!(role = nil)

def clear_reloadable_connections!(role = nil)
  deprecation_for_delegation(__method__)
  connection_handler.clear_reloadable_connections!(role)
end

def connected?

Returns +true+ if Active Record is connected.
def connected?
  connection_handler.connected?(connection_specification_name, role: current_role, shard: current_shard)
end

def connected_to(role: nil, shard: nil, prevent_writes: false, &blk)

end
Dog.first # finds first Dog record stored on the shard one replica
ActiveRecord::Base.connected_to(role: :reading, shard: :shard_one_replica) do

and then look up the connection by shard key.
When a shard and role is passed, Active Record will first lookup the role,

raised.
shard is passed, an +ActiveRecord::ConnectionNotEstablished+ error will be
When swapping to a shard, the role must be passed as well. If a non-existent

end
Dog.create! # throws exception because we're on a replica
ActiveRecord::Base.connected_to(role: :reading) do

end
Dog.create! # creates dog using dog writing connection
ActiveRecord::Base.connected_to(role: :writing) do

an +ActiveRecord::ConnectionNotEstablished+ error will be raised:
based on the requested role. If a non-established role is requested
If only a role is passed, Active Record will look up the connection

connection will be returned to the original role / shard.
shard for the duration of the block. At the end of the block the
Connects to a role (e.g. writing, reading, or a custom role) and/or
def connected_to(role: nil, shard: nil, prevent_writes: false, &blk)
  if self != Base && !abstract_class
    raise NotImplementedError, "calling `connected_to` is only allowed on ActiveRecord::Base or abstract classes."
  end
  if !connection_class? && !primary_class?
    raise NotImplementedError, "calling `connected_to` is only allowed on the abstract class that established the connection."
  end
  unless role || shard
    raise ArgumentError, "must provide a `shard` and/or `role`."
  end
  with_role_and_shard(role, shard, prevent_writes, &blk)
end

def connected_to?(role:, shard: ActiveRecord::Base.default_shard)

end
ActiveRecord::Base.connected_to?(role: :writing, shard: :shard_one) #=> true
ActiveRecord::Base.connected_to?(role: :reading, shard: :default) #=> false
ActiveRecord::Base.connected_to?(role: :reading, shard: :shard_one) #=> true
ActiveRecord::Base.connected_to(role: :reading, shard: :shard_one) do

end
ActiveRecord::Base.connected_to?(role: :reading) #=> false
ActiveRecord::Base.connected_to?(role: :writing) #=> true
ActiveRecord::Base.connected_to(role: :writing) do

used.
current connected shard. If no shard is passed, the default will be
Returns true if role is the current connected role and/or
def connected_to?(role:, shard: ActiveRecord::Base.default_shard)
  current_role == role.to_sym && current_shard == shard.to_sym
end

def connected_to_many(*classes, role:, shard: nil, prevent_writes: false)

end
Person.first # Read from primary writer
Dinner.first # Read from meals replica
Dog.first # Read from animals replica
ActiveRecord::Base.connected_to_many(AnimalsRecord, MealsRecord, role: :reading) do

Usage:

+connected_to_many+ is an alternative to deeply nested +connected_to+ blocks.

+prevent_writes+ to true.
can be passed to block writes on a connection. +reading+ will automatically set
Connects a role and/or shard to the provided connection names. Optionally +prevent_writes+
def connected_to_many(*classes, role:, shard: nil, prevent_writes: false)
  classes = classes.flatten
  if self != Base || classes.include?(Base)
    raise NotImplementedError, "connected_to_many can only be called on ActiveRecord::Base."
  end
  prevent_writes = true if role == ActiveRecord.reading_role
  append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: classes)
  yield
ensure
  connected_to_stack.pop
end

def connecting_to(role: default_role, shard: default_shard, prevent_writes: false)

does not yield to a block like +connected_to+.
It is not recommended to use this method in a request since it

being used. For example, when booting a console in readonly mode.
This method is useful for ensuring that a specific connection is

Use a specified connection.
def connecting_to(role: default_role, shard: default_shard, prevent_writes: false)
  prevent_writes = true if role == ActiveRecord.reading_role
  append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self])
end

def connection

to any of the specific Active Records.
also be used to "borrow" the connection to do database work unrelated
Returns the connection currently associated with the class. This can
def connection
  retrieve_connection
end

def connection_db_config

Use only for reading.

@name="primary", @config={pool: 5, timeout: 5000, database: "storage/development.sqlite3", adapter: "sqlite3"}>
#ActiveRecord::Base.connection_db_config

Returns the db_config object from the associated connection:
def connection_db_config
  connection_pool.db_config
end

def connection_pool

def connection_pool
  connection_handler.retrieve_connection_pool(connection_specification_name, role: current_role, shard: current_shard) || raise(ConnectionNotEstablished)
end

def connection_specification_name

Returns the connection specification name from the current class or its parent.
def connection_specification_name
  if !defined?(@connection_specification_name) || @connection_specification_name.nil?
    return self == Base ? Base.name : superclass.connection_specification_name
  end
  @connection_specification_name
end

def connects_to(database: {}, shards: {})

Returns an array of database connections.

end
}
shard_two: { writing: :primary_shard_two, reading: :primary_shard_replica_two }
default: { writing: :primary, reading: :primary_replica },
connects_to shards: {

self.abstract_class = true
class AnimalsModel < ApplicationRecord

supports read replicas as well. You can connect a model to a list of shards like this:
+connects_to+ also supports horizontal sharding. The horizontal sharding API

end
connects_to database: { writing: :primary, reading: :primary_replica }

self.abstract_class = true
class AnimalsModel < ApplicationRecord

establish a connection to that config.
This will look up the database config using the +database_key+ and

takes a hash consisting of a +role+ and a +database_key+.
Connects a model to the databases specified. The +database+ keyword
def connects_to(database: {}, shards: {})
  raise NotImplementedError, "`connects_to` can only be called on ActiveRecord::Base or abstract classes" unless self == Base || abstract_class?
  if database.present? && shards.present?
    raise ArgumentError, "`connects_to` can only accept a `database` or `shards` argument, but not both arguments."
  end
  connections = []
  if shards.empty?
    shards[:default] = database
  end
  self.default_shard = shards.keys.first
  shards.each do |shard, database_keys|
    database_keys.each do |role, database_key|
      db_config = resolve_config_for_connection(database_key)
      self.connection_class = true
      connections << connection_handler.establish_connection(db_config, owner_name: self, role: role, shard: shard.to_sym)
    end
  end
  connections
end

def deprecation_for_delegation(method)

def deprecation_for_delegation(method)
  ActiveRecord.deprecator.warn(<<-MSG.squish)
    Calling `ActiveRecord::Base.#{method} is deprecated. Please
    call the method directly on the connection handler; for
    example: `ActiveRecord::Base.connection_handler.#{method}`.
  MSG
end

def establish_connection(config_or_env = nil)

may be returned on an error.
The exceptions AdapterNotSpecified, AdapterNotFound, and +ArgumentError+

ActiveRecord::Base.establish_connection(:production)

configuration hash:
a symbol can also be given as argument, representing a key in the
is set (\Rails automatically loads the contents of config/database.yml into it),
In case {ActiveRecord::Base.configurations}[rdoc-ref:Core.configurations]

)
"postgres://myuser:mypass@localhost/somedatabase"
ActiveRecord::Base.establish_connection(

Or a URL:

)
"database" => "path/to/dbfile"
"adapter" => "sqlite3",
ActiveRecord::Base.establish_connection(

Also accepts keys as strings (for parsing from YAML for example):

)
database: "path/to/dbfile"
adapter: "sqlite3",
ActiveRecord::Base.establish_connection(

Example for SQLite database:

)
database: "somedatabase"
password: "mypass",
username: "myuser",
host: "localhost",
adapter: "mysql2",
ActiveRecord::Base.establish_connection(

example for regular databases (MySQL, PostgreSQL, etc):
the :adapter key must be specified with the name of a database adapter (in lower-case)
Establishes the connection to the database. Accepts a hash as input where
def establish_connection(config_or_env = nil)
  config_or_env ||= DEFAULT_ENV.call.to_sym
  db_config = resolve_config_for_connection(config_or_env)
  connection_handler.establish_connection(db_config, owner_name: self, role: current_role, shard: current_shard)
end

def flush_idle_connections!(role = nil)

def flush_idle_connections!(role = nil)
  deprecation_for_delegation(__method__)
  connection_handler.flush_idle_connections!(role)
end

def mysql2_adapter_class

:nodoc:
def mysql2_adapter_class
  ConnectionAdapters::Mysql2Adapter
end

def mysql2_connection(config)

Establishes a connection to the database that's used by all Active Record objects.
def mysql2_connection(config)
  mysql2_adapter_class.new(config)
end

def postgresql_adapter_class

:nodoc:
def postgresql_adapter_class
  ConnectionAdapters::PostgreSQLAdapter
end

def postgresql_connection(config)

Establishes a connection to the database that's used by all Active Record objects
def postgresql_connection(config)
  postgresql_adapter_class.new(config)
end

def primary_class? # :nodoc:

:nodoc:
def primary_class? # :nodoc:
  self == Base || application_record_class?
end

def prohibit_shard_swapping(enabled = true)

database isolation.
is useful in cases you're using sharding to provide per-request
nested call to connected_to or connected_to_many to swap again. This
In some cases you may want to be able to swap shards but not allow a

Prohibit swapping shards while inside of the passed block.
def prohibit_shard_swapping(enabled = true)
  prev_value = ActiveSupport::IsolatedExecutionState[:active_record_prohibit_shard_swapping]
  ActiveSupport::IsolatedExecutionState[:active_record_prohibit_shard_swapping] = enabled
  yield
ensure
  ActiveSupport::IsolatedExecutionState[:active_record_prohibit_shard_swapping] = prev_value
end

def remove_connection(name = nil)

def remove_connection(name = nil)
  if name
    ActiveRecord.deprecator.warn(<<-MSG.squish)
      The name argument for `#remove_connection` is deprecated without replacement
      and will be removed in Rails 7.2. `#remove_connection` should always be called
      on the connection class directly, which makes the name argument obsolete.
    MSG
  end
  name ||= @connection_specification_name if defined?(@connection_specification_name)
  # if removing a connection that has a pool, we reset the
  # connection_specification_name so it will use the parent
  # pool.
  if connection_handler.retrieve_connection_pool(name, role: current_role, shard: current_shard)
    self.connection_specification_name = nil
  end
  connection_handler.remove_connection_pool(name, role: current_role, shard: current_shard)
end

def resolve_config_for_connection(config_or_env)

def resolve_config_for_connection(config_or_env)
  raise "Anonymous class is not allowed." unless name
  connection_name = primary_class? ? Base.name : name
  self.connection_specification_name = connection_name
  Base.configurations.resolve(config_or_env)
end

def retrieve_connection

def retrieve_connection
  connection_handler.retrieve_connection(connection_specification_name, role: current_role, shard: current_shard)
end

def shard_swapping_prohibited?

Determine whether or not shard swapping is currently prohibited
def shard_swapping_prohibited?
  ActiveSupport::IsolatedExecutionState[:active_record_prohibit_shard_swapping]
end

def sqlite3_adapter_class

:nodoc:
def sqlite3_adapter_class
  ConnectionAdapters::SQLite3Adapter
end

def sqlite3_connection(config)

def sqlite3_connection(config)
  sqlite3_adapter_class.new(config)
end

def trilogy_adapter_class

:nodoc:
def trilogy_adapter_class
  ConnectionAdapters::TrilogyAdapter
end

def trilogy_connection(config)

Establishes a connection to the database that's used by all Active Record objects.
def trilogy_connection(config)
  configuration = config.dup
  # Set FOUND_ROWS capability on the connection so UPDATE queries returns number of rows
  # matched rather than number of rows updated.
  configuration[:found_rows] = true
  options = [
    configuration[:host],
    configuration[:port],
    configuration[:database],
    configuration[:username],
    configuration[:password],
    configuration[:socket],
    0
  ]
  trilogy_adapter_class.new nil, logger, options, configuration
end

def while_preventing_writes(enabled = true, &block)

method.
See +READ_QUERY+ for the queries that are blocked by this

user and is meant to be a safeguard against accidental writes.
This method does not provide the same protection as a readonly

will prevent writes to the database for the duration of the block.
even if you are on a database that can write. +while_preventing_writes+
In some cases you may want to prevent writes to the database

Prevent writing to the database regardless of role.
def while_preventing_writes(enabled = true, &block)
  connected_to(role: current_role, prevent_writes: enabled, &block)
end

def with_role_and_shard(role, shard, prevent_writes)

def with_role_and_shard(role, shard, prevent_writes)
  prevent_writes = true if role == ActiveRecord.reading_role
  append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self])
  return_value = yield
  return_value.load if return_value.is_a? ActiveRecord::Relation
  return_value
ensure
  self.connected_to_stack.pop
end