Spec-Zone.ru › Ruby on Rails 8.1

module ActiveRecord::ConnectionAdapters::DatabaseStatements

Открытые методы класса

new () Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 6
def initialize
  super
  reset_transaction
end
Вызывает метод суперкласса

Публичные методы экземпляра

add_transaction_record (record, ensure_finalize = true) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 424
def add_transaction_record(record, ensure_finalize = true)
  current_transaction.add_record(record, ensure_finalize)
end

Регистрирует запись в текущей транзакции, чтобы можно было вызвать её обратные вызовы after_commit и after_rollback.

begin_db_transaction () Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 429
def begin_db_transaction()    end

Начинает транзакцию (и отключает автоматическую фиксацию).

begin_isolated_db_transaction (isolation) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 454
def begin_isolated_db_transaction(isolation)
  raise ActiveRecord::TransactionIsolationError, "adapter does not support setting transaction isolation"
end

Начинает транзакцию с заданным уровнем изоляции. По умолчанию вызывает ошибку; адаптеры, поддерживающие установку уровня изоляции, должны реализовать этот метод.

commit_db_transaction () Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 468
def commit_db_transaction()   end

Фиксирует транзакцию (и включает автоматическую фиксацию).

create (arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [], returning: nil)
Псевдоним для: insert
default_sequence_name (table, column) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 490
def default_sequence_name(table, column)
  nil
end
delete (arel, name = nil, binds = []) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 215
def delete(arel, name = nil, binds = [])
  sql, binds = to_sql_and_binds(arel, binds)
  exec_delete(sql, name, binds)
end

Выполняет оператор удаления и возвращает количество затронутых строк.

empty_insert_statement_value (primary_key = nil) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 520
def empty_insert_statement_value(primary_key = nil)
  "DEFAULT VALUES"
end
exec_delete (sql, name = nil, binds = []) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 168
def exec_delete(sql, name = nil, binds = [])
  affected_rows(internal_execute(sql, name, binds))
end

Выполняет оператор удаления sql в контексте этого подключения, используя binds в качестве подстановочных значений параметров. name регистрируется в журнале вместе с выполненным оператором sql.

exec_insert (sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 160
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil)
  sql, binds = sql_for_insert(sql, pk, binds, returning)
  internal_exec_query(sql, name, binds)
end

Выполняет оператор вставки sql в контексте этого подключения, используя binds в качестве подстановочных значений параметров. name регистрируется в журнале вместе с выполненным оператором sql. Некоторые адаптеры поддерживают аргумент ключевого слова ‘returning`, позволяющий управлять результатом запроса: `nil` — значение по умолчанию, сохраняющее стандартное поведение. Если передан массив имён столбцов, результат будет содержать значения указанных столбцов вставленной строки.

exec_query (sql, name = "SQL", binds = [], prepare: false) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 150
def exec_query(sql, name = "SQL", binds = [], prepare: false)
  internal_exec_query(sql, name, binds, prepare: prepare)
end

Выполняет оператор sql в контексте этого подключения, используя binds в качестве подстановочных значений параметров. name регистрируется в журнале вместе с выполненным оператором sql.

Примечание: предполагается, что запрос имеет побочные эффекты, поэтому кэш запросов будет очищен. Если запрос предназначен только для чтения, используйте вместо этого select_all.

exec_update (sql, name = nil, binds = []) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 175
def exec_update(sql, name = nil, binds = [])
  affected_rows(internal_execute(sql, name, binds))
end

Выполняет оператор обновления sql в контексте этого подключения, используя binds в качестве подстановочных значений параметров. name регистрируется в журнале вместе с выполненным оператором sql.

execute (sql, name = nil, allow_retry: false) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 139
def execute(sql, name = nil, allow_retry: false)
  internal_execute(sql, name, allow_retry: allow_retry)
end

Выполняет оператор SQL в контексте этого подключения и возвращает необработанный результат адаптера подключения.

Если установить allow_retry в значение true, в случае исключения, связанного с подключением, база данных повторно подключится и повторит выполнение оператора SQL. Этот параметр следует включать только для заведомо идемпотентных запросов.

Примечание: предполагается, что запрос имеет побочные эффекты, поэтому кэш запросов будет очищен. Если запрос предназначен только для чтения, используйте вместо этого select_all.

Примечание: в зависимости от используемого коннектора базы данных для результата, возвращаемого этим методом, может потребоваться ручное управление памятью. Вместо этого рассмотрите возможность использования обёртки exec_query.

high_precision_current_timestamp () Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 544
def high_precision_current_timestamp
  HIGH_PRECISION_CURRENT_TIMESTAMP
end

Возвращает SQL-литерал Arel для CURRENT_TIMESTAMP, предназначенный для использования со столбцами даты и времени произвольной точности.

Адаптеры, поддерживающие дату и время с заданной точностью, должны переопределить этот метод, чтобы обеспечить максимально доступную точность.

insert (arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [], returning: nil) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 198
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [], returning: nil)
  sql, binds = to_sql_and_binds(arel, binds)
  value = exec_insert(sql, name, binds, pk, sequence_name, returning: returning)

  return returning_column_values(value) unless returning.nil?

  id_value || last_inserted_id(value)
end

Выполняет запрос INSERT и возвращает идентификатор новой записи.

Возвращается id_value, если только его значение не равно nil; в этом случае база данных попытается вычислить идентификатор последней вставленной записи и вернуть это значение.

Если следующий идентификатор был вычислен заранее (как в Oracle), его следует передать в качестве id_value. Некоторые адаптеры поддерживают аргумент ключевого слова ‘returning`, позволяющий задать возвращаемое значение метода: `nil` — значение по умолчанию, сохраняющее стандартное поведение. Если передан массив имён столбцов, метод вернёт массив значений указанных столбцов вставленной строки.

Также имеет псевдоним: create
insert_fixture (fixture, table_name) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 504
def insert_fixture(fixture, table_name)
  execute(build_fixture_sql(Array.wrap(fixture), table_name), "Fixture Insert")
end

Вставляет указанную фикстуру в таблицу. Переопределяется в адаптерах, которым требуется нечто большее, чем простая вставка (например, Oracle). Большинство адаптеров должны реализовать insert_fixtures_set, использующий массовую вставку SQL. Этот метод сохранён для резервного варианта для баз данных, таких как SQLite, которые не поддерживают массовую вставку.

insert_fixtures_set (fixture_set, tables_to_delete = []) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 508
def insert_fixtures_set(fixture_set, tables_to_delete = [])
  fixture_inserts = build_fixture_statements(fixture_set)
  table_deletes = tables_to_delete.map { |table| "DELETE FROM #{quote_table_name(table)}" }
  statements = table_deletes + fixture_inserts

  transaction(requires_new: true) do
    disable_referential_integrity do
      execute_batch(statements, "Fixtures Load")
    end
  end
end
reset_isolation_level () Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 464
def reset_isolation_level
end

Точка расширения, вызываемая после фиксации или отката изолированной транзакции базы данных. Большинству адаптеров не нужно ничего реализовывать, поскольку уровень изоляции задаётся отдельно для каждой транзакции. Однако некоторые базы данных, например SQLite, задают его на уровне подключения и требуют явного сброса после фиксации или отката.

reset_sequence! (table, column, sequence = nil) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 495
def reset_sequence!(table, column, sequence = nil)
  # Do nothing by default. Implement for PostgreSQL, Oracle, ...
end

Устанавливает последовательность в максимальное значение столбца таблицы.

restart_db_transaction () Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 480
def restart_db_transaction
  exec_restart_db_transaction
end
rollback_db_transaction () Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 472
def rollback_db_transaction
  exec_rollback_db_transaction
rescue ActiveRecord::ConnectionNotEstablished, ActiveRecord::ConnectionFailed
  # Connection's gone; that counts as a rollback
end

Откатывает транзакцию (и включает автоматическую фиксацию). Это необходимо сделать, если блок транзакции вызывает исключение или возвращает false.

rollback_to_savepoint (name = nil) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 486
def rollback_to_savepoint(name = nil)
  exec_rollback_to_savepoint(name)
end
select_all (arel, name = nil, binds = [], preparable: nil, async: false, allow_retry: false) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 72
def select_all(arel, name = nil, binds = [], preparable: nil, async: false, allow_retry: false)
  arel = arel_from_relation(arel)
  sql, binds, preparable, allow_retry = to_sql_and_binds(arel, binds, preparable, allow_retry)

  select(sql, name, binds,
    prepare: prepared_statements && preparable,
    async: async && FutureResult::SelectAll,
    allow_retry: allow_retry
  )
rescue ::RangeError
  ActiveRecord::Result.empty(async: async)
end

Возвращает экземпляр ActiveRecord::Result.

select_one (arel, name = nil, binds = [], async: false) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 87
def select_one(arel, name = nil, binds = [], async: false)
  select_all(arel, name, binds, async: async).then(&:first)
end

Возвращает хеш записи, в котором ключами являются имена столбцов, а значениями — значения столбцов.

select_rows (arel, name = nil, binds = [], async: false) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 104
def select_rows(arel, name = nil, binds = [], async: false)
  select_all(arel, name, binds, async: async).then(&:rows)
end

Возвращает массив массивов со значениями полей. Порядок совпадает с порядком, возвращаемым columns.

select_value (arel, name = nil, binds = [], async: false) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 92
def select_value(arel, name = nil, binds = [], async: false)
  select_rows(arel, name, binds, async: async).then { |rows| single_value_from_rows(rows) }
end

Возвращает одно значение из записи

select_values (arel, name = nil, binds = []) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 98
def select_values(arel, name = nil, binds = [])
  select_rows(arel, name, binds).map(&:first)
end

Возвращает массив значений первого столбца в запросе select:

select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
to_sql (arel_or_sql_string, binds = []) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 12
def to_sql(arel_or_sql_string, binds = [])
  sql, _ = to_sql_and_binds(arel_or_sql_string, binds)
  sql
end

Преобразует AST Arel в SQL

transaction (requires_new: nil, isolation: nil) { |user_transaction| ... } Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 355
def transaction(requires_new: nil, isolation: nil, joinable: true, &block)
  # If we're running inside the single, non-joinable transaction that
  # ActiveRecord::TestFixtures starts around each example (depth == 1),
  # an `isolation:` hint must be validated then ignored so that the
  # adapter isn't asked to change the isolation level mid-transaction.
  if isolation && !requires_new && open_transactions == 1 && !current_transaction.joinable?
    iso = isolation.to_sym

    unless transaction_isolation_levels.include?(iso)
      raise ActiveRecord::TransactionIsolationError,
            "invalid transaction isolation level: #{iso.inspect}"
    end

    current_transaction.isolation = iso
    isolation = nil
  end

  if !requires_new && current_transaction.joinable?
    if isolation && current_transaction.isolation != isolation
      raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
    end
    yield current_transaction.user_transaction
  else
    within_new_transaction(isolation: isolation, joinable: joinable, &block)
  end
rescue ActiveRecord::Rollback
  # rollbacks are silently swallowed
end

Выполняет переданный блок в транзакции базы данных и возвращает результат выполнения блока.

Transaction обратные вызовы

transaction передаёт объект ActiveRecord::Transaction, для которого можно зарегистрировать обратный вызов:

ActiveRecord::Base.transaction do |transaction|
  transaction.before_commit { puts "before commit!" }
  transaction.after_commit { puts "after commit!" }
  transaction.after_rollback { puts "after rollback!" }
end

Поддержка вложенных транзакций

Вызовы transaction могут быть вложенными. По умолчанию все операторы базы данных во вложенном блоке транзакции становятся частью родительской транзакции. Например, следующее поведение может показаться неожиданным:

ActiveRecord::Base.transaction do
  Post.create(title: 'first')
  ActiveRecord::Base.transaction do
    Post.create(title: 'second')
    raise ActiveRecord::Rollback
  end
end

В результате создаются обе записи: «first» и «second». Причина в том, что исключение ActiveRecord::Rollback во вложенном блоке не приводит к выполнению ROLLBACK. Поскольку такие исключения перехватываются в блоках транзакций, родительский блок их не видит, и фактическая транзакция фиксируется.

Большинство баз данных не поддерживают настоящие вложенные транзакции. Насколько нам известно на момент написания документации, единственная база данных с поддержкой настоящих вложенных транзакций — MS-SQL.

Чтобы обойти эту проблему, transaction имитирует эффект вложенных транзакций с помощью точек сохранения: dev.mysql.com/doc/refman/en/savepoint.html.

Этот метод можно безопасно вызывать, если транзакция базы данных уже открыта, то есть если transaction вызывается внутри другого блока transaction. При вложенном вызове transaction будет вести себя следующим образом:

  • Блок будет выполнен без каких-либо дополнительных действий. Все операторы базы данных, выполненные в блоке, фактически добавляются к уже открытой транзакции базы данных.

  • Однако, если задан параметр :requires_new, блок будет обёрнут в точку сохранения базы данных, которая действует как под-транзакция.

Чтобы выполнить ROLLBACK для вложенной транзакции, можно запросить настоящую под-транзакцию, передав requires_new: true. Если что-то пойдёт не так, база данных откатится к началу под-транзакции, не откатывая родительскую транзакцию. Добавим этот параметр к предыдущему примеру:

ActiveRecord::Base.transaction do
  Post.create(title: 'first')
  ActiveRecord::Base.transaction(requires_new: true) do
    Post.create(title: 'second')
    raise ActiveRecord::Rollback
  end
end

будет создана только запись с заголовком «first».

Подробнее см. в разделе ActiveRecord::Transactions.

Ограничения

MySQL не поддерживает транзакции DDL. При выполнении операции DDL все созданные точки сохранения автоматически освобождаются. Например, если вы создали точку сохранения, а затем выполнили оператор CREATE TABLE, созданная точка сохранения будет автоматически освобождена.

Это означает, что в MySQL не следует выполнять операции DDL внутри вызова transaction, если вы знаете, что он может создать точку сохранения. В противном случае transaction вызовет исключения при попытке освободить уже автоматически освобождённые точки сохранения:

Model.lease_connection.transaction do  # BEGIN
  Model.lease_connection.transaction(requires_new: true) do  # CREATE SAVEPOINT active_record_1
    Model.lease_connection.create_table(...)
    # active_record_1 now automatically released
  end  # RELEASE SAVEPOINT active_record_1  <--- BOOM! database error!
end

Transaction изоляция

Если ваша база данных поддерживает настройку уровня изоляции транзакции, его можно задать следующим образом:

Post.transaction(isolation: :serializable) do
  # ...
end

Допустимые уровни изоляции:

  • :read_uncommitted

  • :read_committed

  • :repeatable_read

  • :serializable

Обратитесь к документации вашей базы данных, чтобы понять семантику этих уровней:

  • www.postgresql.org/docs/current/static/transaction-iso.html

  • dev.mysql.com/doc/refman/en/set-transaction.html

Исключение ActiveRecord::TransactionIsolationError будет вызвано, если:

  • Адаптер не поддерживает настройку уровня изоляции

  • Вы присоединяетесь к уже открытой транзакции

  • Вы создаёте вложенную транзакцию (с точкой сохранения)

Адаптеры mysql2, trilogy и postgresql поддерживают настройку уровня изоляции транзакций.

transaction_isolation_levels () Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 447
def transaction_isolation_levels
  TRANSACTION_ISOLATION_LEVELS
end
transaction_open? () Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 398
def transaction_open?
  current_transaction.open?
end
truncate (table_name, name = nil) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 221
def truncate(table_name, name = nil)
  execute(build_truncate_statement(table_name), name)
end

Выполняет оператор truncate.

update (arel, name = nil, binds = []) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 209
def update(arel, name = nil, binds = [])
  sql, binds = to_sql_and_binds(arel, binds)
  exec_update(sql, name, binds)
end

Выполняет оператор update и возвращает количество затронутых строк.

write_query? (sql) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 121
def write_query?(sql)
  raise NotImplementedError
end

Определяет, является ли оператор SQL запросом на запись.

© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API