Spec-Zone.ru › Ruby on Rails 8.0

модуль 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) Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 405
def add_transaction_record(record, ensure_finalize = true)
  current_transaction.add_record(record, ensure_finalize)
end

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

begin_db_transaction() Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 410
def begin_db_transaction()    end

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

begin_isolated_db_transaction(isolation) Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 432
def begin_isolated_db_transaction(isolation)
  raise ActiveRecord::TransactionIsolationError, "adapter does not support setting transaction isolation"
end

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

commit_db_transaction() Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 446
def commit_db_transaction()   end

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

create(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [], returning: nil)
Alias for: insert
default_sequence_name(table, column) Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 468
def default_sequence_name(table, column)
  nil
end
delete(arel, name = nil, binds = []) Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 212
def delete(arel, name = nil, binds = [])
  sql, binds = to_sql_and_binds(arel, binds)
  exec_delete(sql, name, binds)
end

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

empty_insert_statement_value(primary_key = nil) Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 498
def empty_insert_statement_value(primary_key = nil)
  "DEFAULT VALUES"
end
exec_delete(sql, name = nil, binds = []) Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 165
def exec_delete(sql, name = nil, binds = [])
  affected_rows(internal_execute(sql, name, binds))
end

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

exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil, returning: nil) Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 157
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

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

exec_query(sql, name = "SQL", binds = [], prepare: false) Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 147
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 = []) Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 172
def exec_update(sql, name = nil, binds = [])
  affected_rows(internal_execute(sql, name, binds))
end

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

execute(sql, name = nil, allow_retry: false) Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 136
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() Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 536
def high_precision_current_timestamp
  HIGH_PRECISION_CURRENT_TIMESTAMP
end

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

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

insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [], returning: nil) Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 195
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 новой записи

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

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

Также является псевдонимом для: create
insert_fixture(fixture, table_name) Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 482
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 = []) Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 486
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 442
def reset_isolation_level
end

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

reset_sequence!(table, column, sequence = nil) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 473
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 458
def restart_db_transaction
  exec_restart_db_transaction
end
rollback_db_transaction() Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 450
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 464
def rollback_to_savepoint(name = nil)
  exec_rollback_to_savepoint(name)
end
sanitize_limit(limit) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 508
def sanitize_limit(limit)
  if limit.is_a?(Integer) || limit.is_a?(Arel::Nodes::SqlLiteral)
    limit
  else
    Integer(limit)
  end
end

Очищает переданный параметр LIMIT для предотвращения SQL-инъекций.

limit может быть чем угодно, что может быть преобразовано в строку с помощью to_s. Оно должно выглядеть как целое число или Arel SQL-литерал.

Возвращает Integer и Arel::Nodes::SqlLiteral ограничения как есть.

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 69
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 84
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 101
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 89
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 95
def select_values(arel, name = nil, binds = [])
  select_rows(arel, name, binds).map(&:first)
end

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

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 352
def transaction(requires_new: nil, isolation: nil, joinable: true, &block)
  if !requires_new && current_transaction.joinable?
    if 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

Изоляция транзакции

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

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 420
def transaction_isolation_levels
  {
    read_uncommitted: "READ UNCOMMITTED",
    read_committed:   "READ COMMITTED",
    repeatable_read:  "REPEATABLE READ",
    serializable:     "SERIALIZABLE"
  }
end
transaction_open?() Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 379
def transaction_open?
  current_transaction.open?
end
truncate(table_name, name = nil) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 218
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 206
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 118
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