Spec-Zone.ru › Ruby on Rails 7.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) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 345
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 350
def begin_db_transaction()    end

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

begin_isolated_db_transaction(isolation) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 364
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 369
def commit_db_transaction()   end

Подтверждает транзакцию (и включает автоматическое подтверждение).

create(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
Псевдоним для: insert
default_sequence_name(table, column) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 383
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 179
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 415
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 138
def exec_delete(sql, name = nil, binds = [])
  exec_query(sql, name, binds)
end

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

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

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

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

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

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

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

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

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

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

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

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

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

Выполняет запрос INSERT и возвращает ID новой записи

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

Если следующий ID был вычислен заранее (как в Oracle), его следует передать как id_value.

Также известен как: create
insert_fixture(fixture, table_name) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 397
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 401
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

  with_multi_statements do
    disable_referential_integrity do
      transaction(requires_new: true) do
        execute_batch(statements, "Fixtures Load")
      end
    end
  end
end
reset_sequence!(table, column, sequence = nil) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 388
def reset_sequence!(table, column, sequence = nil)
  # Do nothing by default. Implement for PostgreSQL, Oracle, ...
end

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

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

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

rollback_to_savepoint(name = nil) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 379
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 425
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) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 62
def select_all(arel, name = nil, binds = [], preparable: nil, async: false)
  arel = arel_from_relation(arel)
  sql, binds, preparable = to_sql_and_binds(arel, binds, preparable)

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

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

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

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

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

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

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

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

select_values(arel, name = nil, binds = []) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 84
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, joinable: true) { || ... } Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 309
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
  else
    transaction_manager.within_new_transaction(isolation: isolation, joinable: joinable, &block)
  end
rescue ActiveRecord::Rollback
  # rollbacks are silently swallowed
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.connection.transaction do  # BEGIN
  Model.connection.transaction(requires_new: true) do  # CREATE SAVEPOINT active_record_1
    Model.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 и postgresql поддерживают установку уровня изоляции транзакции.

transaction_isolation_levels() Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 352
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 335
def transaction_open?
  current_transaction.open?
end
truncate(table_name, name = nil) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 185
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 173
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 107
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