跳至内容 跳至搜索

Active Record 抽象适配器

Active Record 支持多种数据库系统。 AbstractAdapter 及其相关类构成了抽象层,使之成为可能。 AbstractAdapter 表示与数据库的连接,并为特定于数据库的功能提供抽象接口,例如建立连接、转义值、为:offset:limit 选项构建正确的 SQL 片段等。

所有具体数据库适配器都遵循此类中规定的接口。 ActiveRecord::Base.lease_connection 返回一个 AbstractAdapter 对象,您可以使用它。

适配器中的大多数方法在迁移期间很有用。最值得注意的是,由 SchemaStatements 提供的实例方法非常有用。

命名空间
方法
A
C
D
E
F
I
L
P
R
S
T
U
V
包含的模块

常量

ADAPTER_NAME = "Abstract"
 
COMMENT_REGEX = %r{(?:--.*\n)|/\*(?:[^*]|\*[^/])*\*/}
 
EXTENDED_TYPE_MAPS = Concurrent::Map.new
 
SIMPLE_INT = /\A\d+\z/
 
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
 

属性

[R] in_use?
[R] lock
[R] logger
[R] owner
[R] pool
[R] visitor

类公共方法

database_exists?(config)

该适配器的数据库是否存在?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 360
def self.database_exists?(config)
  new(config).database_exists?
end

dbconsole(config, options = {})

打开一个数据库控制台会话。

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 121
def self.dbconsole(config, options = {})
  raise NotImplementedError
end

find_cmd_and_exec(commands, *args)

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 92
def self.find_cmd_and_exec(commands, *args) # :doc:
  commands = Array(commands)

  dirs_on_path = ENV["PATH"].to_s.split(File::PATH_SEPARATOR)
  unless (ext = RbConfig::CONFIG["EXEEXT"]).empty?
    commands = commands.map { |cmd| "#{cmd}#{ext}" }
  end

  full_path_command = nil
  found = commands.detect do |cmd|
    dirs_on_path.detect do |path|
      full_path_command = File.join(path, cmd)
      begin
        stat = File.stat(full_path_command)
      rescue SystemCallError
      else
        stat.file? && stat.executable?
      end
    end
  end

  if found
    exec full_path_command, *args
  else
    abort("Couldn't find database client: #{commands.join(', ')}. Check your $PATH and try again.")
  end
end

type_cast_config_to_boolean(config)

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 65
def self.type_cast_config_to_boolean(config)
  if config == "false"
    false
  else
    config
  end
end

type_cast_config_to_integer(config)

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 55
def self.type_cast_config_to_integer(config)
  if config.is_a?(Integer)
    config
  elsif SIMPLE_INT.match?(config)
    config.to_i
  else
    config
  end
end

validate_default_timezone(config)

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 73
def self.validate_default_timezone(config)
  case config
  when nil
  when "utc", "local"
    config.to_sym
  else
    raise ArgumentError, "default_timezone must be either 'utc' or 'local'"
  end
end

实例公共方法

active?()

检查与数据库的连接是否仍然处于活动状态。这包括检查数据库是否真正能够响应,即连接是否陈旧。

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 658
def active?
end

adapter_name()

返回适配器的人类可读名称。使用混合大小写 - 如果需要,始终可以使用小写。

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 355
def adapter_name
  self.class::ADAPTER_NAME
end

check_all_foreign_keys_valid!()

覆盖以检查数据库中的所有外键约束。如果外键约束不满足,适配器应引发ActiveRecord::StatementInvalid

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 643
def check_all_foreign_keys_valid!
end

clear_cache!(new_connection: false)

清除数据库适配器可能正在执行的任何缓存。

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 739
def clear_cache!(new_connection: false)
  if @statements
    @lock.synchronize do
      if new_connection
        @statements.reset
      else
        @statements.clear
      end
    end
  end
end

close()

将连接检查回连接池

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 829
def close
  pool.checkin self
end

connect!()

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 777
def connect!
  verify!
  self
end

connected?()

检查与数据库的连接是否已建立。这并不包括检查数据库是否真正能够响应,即连接是否陈旧。

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 651
def connected?
  !@raw_connection.nil?
end

connection_retries()

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 216
def connection_retries
  (@config[:connection_retries] || 1).to_i
end

database_exists?()

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 364
def database_exists?
  connect!
  true
rescue ActiveRecord::NoDatabaseError
  false
end

default_timezone()

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 228
def default_timezone
  @default_timezone || ActiveRecord.default_timezone
end

disable_extension(name, **)

这将由支持扩展的适配器实现

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 570
def disable_extension(name, **)
end

disable_referential_integrity()

覆盖以在执行&block 时关闭引用完整性。

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 636
def disable_referential_integrity
  yield
end

discard!()

立即忘记这个连接曾经存在。与 disconnect! 不同,这不会与服务器通信。

在调用此方法后,所有其他方法的行为将变得不确定。在分叉进程放弃属于其父进程的连接之前,内部会调用它。

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 714
def discard!
  # This should be overridden by concrete adapters.
end

disconnect!()

如果已连接到数据库,则断开连接。否则,此方法不做任何操作。

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 700
def disconnect!
  @lock.synchronize do
    clear_cache!(new_connection: true)
    reset_transaction
    @raw_connection_dirty = false
  end
end

enable_extension(name, **)

这将由支持扩展的适配器实现

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 574
def enable_extension(name, **)
end

expire()

此方法只能在持有连接池互斥锁时调用。

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 312
def expire
  if in_use?
    if @owner != ActiveSupport::IsolatedExecutionState.context
      raise ActiveRecordError, "Cannot expire connection, " \
        "it is owned by a different thread: #{@owner}. " \
        "Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
    end

    @idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
    @owner = nil
  else
    raise ActiveRecordError, "Cannot expire connection, it is not currently leased."
  end
end

extensions()

扩展列表,由支持它们的适配器填充。

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 624
def extensions
  []
end

index_algorithms()

索引算法列表,由支持它们的适配器填充。

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 629
def index_algorithms
  {}
end

lease()

此方法只能在持有连接池互斥锁时调用。

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 276
def lease
  if in_use?
    msg = +"Cannot lease connection, "
    if @owner == ActiveSupport::IsolatedExecutionState.context
      msg << "it is already leased by the current thread."
    else
      msg << "it is already in use by a different thread: #{@owner}. " \
             "Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
    end
    raise ActiveRecordError, msg
  end

  @owner = ActiveSupport::IsolatedExecutionState.context
end

pool=(value)

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 47
def pool=(value)
  return if value.eql?(@pool)
  @schema_cache = nil
  @pool = value
end

prefetch_primary_key?(table_name = nil)

是否应该在插入语句之前从相应的序列中选择主键值?如果为真,则在每次插入之前调用 next_sequence_value 来设置记录的主键。

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 404
def prefetch_primary_key?(table_name = nil)
  false
end

prepared_statements()

prepared_statements?()

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 243
def prepared_statements?
  @prepared_statements && !prepared_statements_disabled_cache.include?(object_id)
end

preventing_writes?()

确定当前是否正在阻止写入。

如果连接是副本,则返回 true,或者返回 current_preventing_writes 的值。

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 236
def preventing_writes?
  return true if replica?
  return false if connection_class.nil?

  connection_class.current_preventing_writes
end

raw_connection()

提供对该适配器基础数据库驱动程序的访问。例如,此方法在 Mysql2Adapter 的情况下返回 Mysql2::Client 对象,在 PostgreSQLAdapter 的情况下返回 PG::Connection 对象。

这在您需要调用专有方法(例如 PostgreSQL 的 lo_* 方法)时非常有用。

Active Record 无法跟踪使用此客户端是否正在修改数据库。如果是这种情况,通常您需要使用 ActiveRecord::Base.clear_query_cache 使查询缓存失效。

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 797
def raw_connection
  with_raw_connection do |conn|
    disable_lazy_transactions!
    @raw_connection_dirty = true
    conn
  end
end

reconnect!(restore_transactions: false)

如果已连接到数据库,则断开连接,并与数据库建立新的连接。实现者应该定义私有的 reconnect 方法。

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 664
def reconnect!(restore_transactions: false)
  retries_available = connection_retries
  deadline = retry_deadline && Process.clock_gettime(Process::CLOCK_MONOTONIC) + retry_deadline

  @lock.synchronize do
    reconnect

    enable_lazy_transactions!
    @raw_connection_dirty = false
    @verified = true

    reset_transaction(restore: restore_transactions) do
      clear_cache!(new_connection: true)
      configure_connection
    end
  rescue => original_exception
    translated_exception = translate_exception_class(original_exception, nil, nil)
    retry_deadline_exceeded = deadline && deadline < Process.clock_gettime(Process::CLOCK_MONOTONIC)

    if !retry_deadline_exceeded && retries_available > 0
      retries_available -= 1

      if retryable_connection_error?(translated_exception)
        backoff(connection_retries - retries_available)
        retry
      end
    end

    @verified = false

    raise translated_exception
  end
end

replica?()

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 212
def replica?
  @config[:replica] || false
end

requires_reloading?()

如果在开发模式下需要在请求之间重新加载连接,则返回 true。

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 752
def requires_reloading?
  false
end

reset!()

重置此连接的状态,指示 DBMS 清除事务和其他与连接相关的服务器端状态。通常是一个数据库相关的操作。

如果数据库驱动程序或协议不支持此类功能,实现者可以将其别名为 reconnect!。否则,实现者应该在重置连接后立即调用 super(并且在仍然持有 @lock 时)。

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 726
def reset!
  clear_cache!(new_connection: true)
  reset_transaction
  configure_connection
end

retry_deadline()

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 220
def retry_deadline
  if @config[:retry_deadline]
    @config[:retry_deadline].to_f
  else
    nil
  end
end

role()

当前连接的角色(例如 :writing)。在非多角色应用程序中,将返回 :writing

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 297
def role
  @pool.role
end

savepoint_errors_invalidate_transactions?()

保存点上的 TransactionRollbackErrors 是否会影响父事务?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 388
def savepoint_errors_invalidate_transactions?
  false
end

schema_cache()

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 307
def schema_cache
  @pool.schema_cache || (@schema_cache ||= BoundSchemaReflection.for_lone_connection(@pool.schema_reflection, self))
end

schema_version()

返回当前在数据库中可用的模式的版本标识符。这通常等于已执行的最高编号迁移的编号,或者如果不存在模式信息/数据库为空,则为 0。

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 864
def schema_version
  pool.migration_context.current_version
end

shard()

当前连接的分片(例如 :default)。在非分片应用程序中,将返回 :default

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 303
def shard
  @pool.shard
end

supports_advisory_locks?()

此适配器是否支持应用程序强制的咨询锁定?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 397
def supports_advisory_locks?
  false
end

supports_bulk_alter?()

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 377
def supports_bulk_alter?
  false
end

supports_check_constraints?()

此适配器是否支持创建检查约束?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 469
def supports_check_constraints?
  false
end

supports_comments?()

此适配器是否支持数据库对象(表、列、索引)上的元数据注释?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 504
def supports_comments?
  false
end

supports_comments_in_create?()

表、列和索引的注释是否可以在 create/alter table 语句中指定?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 509
def supports_comments_in_create?
  false
end

supports_common_table_expressions?()

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 528
def supports_common_table_expressions?
  false
end

supports_concurrent_connections?()

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 552
def supports_concurrent_connections?
  true
end

supports_datetime_with_precision?()

此适配器是否支持带精度的 datetime?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 494
def supports_datetime_with_precision?
  false
end

supports_ddl_transactions?()

此适配器是否支持事务中的 DDL 回滚?也就是说,CREATE TABLE 或 ALTER TABLE 会被事务回滚吗?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 373
def supports_ddl_transactions?
  false
end

supports_deferrable_constraints?()

此适配器是否支持创建可延迟约束?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 464
def supports_deferrable_constraints?
  false
end

supports_exclusion_constraints?()

此适配器是否支持创建排除约束?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 474
def supports_exclusion_constraints?
  false
end

supports_explain?()

此适配器是否支持 explain?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 433
def supports_explain?
  false
end

supports_expression_index?()

此适配器是否支持表达式索引?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 428
def supports_expression_index?
  false
end

supports_extensions?()

此适配器是否支持数据库扩展?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 443
def supports_extensions?
  false
end

supports_foreign_keys?()

此适配器是否支持创建外键约束?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 454
def supports_foreign_keys?
  false
end

supports_foreign_tables?()

此适配器是否支持外部表?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 519
def supports_foreign_tables?
  false
end

supports_index_include?()

此适配器是否支持包含非键列?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 423
def supports_index_include?
  false
end

supports_index_sort_order?()

此适配器是否支持索引排序顺序?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 413
def supports_index_sort_order?
  false
end

supports_indexes_in_create?()

此适配器是否支持在创建表的同时创建索引?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 449
def supports_indexes_in_create?
  false
end

supports_insert_conflict_target?()

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 548
def supports_insert_conflict_target?
  false
end

supports_insert_on_duplicate_skip?()

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 540
def supports_insert_on_duplicate_skip?
  false
end

supports_insert_on_duplicate_update?()

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 544
def supports_insert_on_duplicate_update?
  false
end

supports_insert_returning?()

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 536
def supports_insert_returning?
  false
end

supports_json?()

此适配器是否支持 JSON 数据类型?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 499
def supports_json?
  false
end

supports_lazy_transactions?()

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 532
def supports_lazy_transactions?
  false
end

supports_materialized_views?()

此适配器是否支持物化视图?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 489
def supports_materialized_views?
  false
end

supports_nulls_not_distinct?()

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 556
def supports_nulls_not_distinct?
  false
end

supports_optimizer_hints?()

此适配器是否支持优化器提示?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 524
def supports_optimizer_hints?
  false
end

supports_partial_index?()

此适配器是否支持部分索引?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 418
def supports_partial_index?
  false
end

supports_partitioned_indexes?()

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 408
def supports_partitioned_indexes?
  false
end

supports_restart_db_transaction?()

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 392
def supports_restart_db_transaction?
  false
end

supports_savepoints?()

此适配器是否支持保存点?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 382
def supports_savepoints?
  false
end

supports_transaction_isolation?()

此适配器是否支持为事务设置隔离级别?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 438
def supports_transaction_isolation?
  false
end

supports_unique_constraints?()

该适配器是否支持创建唯一约束?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 479
def supports_unique_constraints?
  false
end

supports_validate_constraints?()

该适配器是否支持创建无效约束?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 459
def supports_validate_constraints?
  false
end

supports_views?()

该适配器是否支持视图?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 484
def supports_views?
  false
end

supports_virtual_columns?()

该适配器是否支持虚拟列?

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 514
def supports_virtual_columns?
  false
end

throw_away!()

从连接池中移除连接并断开连接。

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 733
def throw_away!
  pool.remove self
  disconnect!
end

unprepared_statement()

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 346
def unprepared_statement
  cache = prepared_statements_disabled_cache.add?(object_id) if @prepared_statements
  yield
ensure
  cache&.delete(object_id)
end

verify!()

检查与数据库的连接是否仍然有效(即不失效)。这是通过调用 active? 在幕后完成的。如果连接不再有效,则此方法将重新连接到数据库。

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 759
def verify!
  unless active?
    @lock.synchronize do
      if @unconfigured_connection
        @raw_connection = @unconfigured_connection
        @unconfigured_connection = nil
        configure_connection
        @verified = true
        return
      end

      reconnect!(restore_transactions: true)
    end
  end

  @verified = true
end

实例私有方法

log(sql, name = "SQL", binds = [], type_casted_binds = [], async: false, &block)

# File activerecord/lib/active_record/connection_adapters/abstract_adapter.rb, line 1128
def log(sql, name = "SQL", binds = [], type_casted_binds = [], async: false, &block) # :doc:
  @instrumenter.instrument(
    "sql.active_record",
    sql:               sql,
    name:              name,
    binds:             binds,
    type_casted_binds: type_casted_binds,
    async:             async,
    connection:        self,
    transaction:       current_transaction.user_transaction.presence,
    row_count:         0,
    &block
  )
rescue ActiveRecord::StatementInvalid => ex
  raise ex.set_query(sql, binds)
end