Spec-Zone.ru › Ruby on Rails 5.2

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

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

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

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

begin_isolated_db_transaction(isolation) Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 302
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 307
def commit_db_transaction()   end

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

create(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
Псевдоним для: insert
default_sequence_name(table, column) Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 321
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 166
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() Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 393
def empty_insert_statement_value
  "DEFAULT VALUES"
end
exec_delete(sql, name = nil, binds = []) Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 128
def exec_delete(sql, name = nil, binds = [])
  exec_query(sql, name, binds)
end

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

exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil) Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 120
def exec_insert(sql, name = nil, binds = [], pk = nil, sequence_name = nil)
  sql, binds = sql_for_insert(sql, pk, nil, sequence_name, binds)
  exec_query(sql, name, binds)
end

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

exec_query(sql, name = "SQL", binds = [], prepare: false) Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 113
def exec_query(sql, name = "SQL", binds = [], prepare: false)
  raise NotImplementedError
end

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

exec_update(sql, name = nil, binds = []) Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 140
def exec_update(sql, name = nil, binds = [])
  exec_query(sql, name, binds)
end

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

execute(sql, name = nil) Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 106
def execute(sql, name = nil)
  raise NotImplementedError
end

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

insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = []) Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 152
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) Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 335
def insert_fixture(fixture, table_name)
  fixture = fixture.stringify_keys

  columns = schema_cache.columns_hash(table_name)
  binds = fixture.map do |name, value|
    if column = columns[name]
      type = lookup_cast_type_from_column(column)
      Relation::QueryAttribute.new(name, value, type)
    else
      raise Fixture::FixtureError, %(table "#{table_name}" has no column named #{name.inspect}.)
    end
  end

  table = Arel::Table.new(table_name)

  values = binds.map do |bind|
    value = with_yaml_fallback(bind.value_for_database)
    [table[bind.name], value]
  end

  manager = Arel::InsertManager.new
  manager.into(table)
  manager.insert(values)
  execute manager.to_sql, "Fixture Insert"
end

Вставляет заданный фикстуру в таблицу. Переопределяется в адаптерах, которым требуется что-то большее, чем просто вставка (например, Oracle). Большинство адаптеров должны реализовывать `insert_fixtures`, который использует пакетную SQL-вставку. Мы сохраняем этот метод, чтобы обеспечить резервное копирование для таких баз данных, как sqlite, которые не поддерживают пакетные вставки.

insert_fixtures(fixtures, table_name) Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 363
      def insert_fixtures(fixtures, table_name)
        ActiveSupport::Deprecation.warn(<<-MSG.squish)
          `insert_fixtures` is deprecated and will be removed in the next version of Rails.
          Consider using `insert_fixtures_set` for performance improvement.
        MSG
        return if fixtures.empty?

        execute(build_fixture_sql(fixtures, table_name), "Fixtures Insert")
      end

Вставляет набор фикстур в таблицу. Переопределяется в адаптерах, которым требуется что-то большее, чем просто вставка (например, Oracle).

insert_fixtures_set(fixture_set, tables_to_delete = []) { || ... } Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 373
def insert_fixtures_set(fixture_set, tables_to_delete = [])
  fixture_inserts = fixture_set.map do |table_name, fixtures|
    next if fixtures.empty?

    build_fixture_sql(fixtures, table_name)
  end.compact

  table_deletes = tables_to_delete.map { |table| "DELETE FROM #{quote_table_name table}".dup }
  total_sql = Array.wrap(combine_multi_statements(table_deletes + fixture_inserts))

  disable_referential_integrity do
    transaction(requires_new: true) do
      total_sql.each do |sql|
        execute sql, "Fixtures Load"
        yield if block_given?
      end
    end
  end
end
reset_sequence!(table, column, sequence = nil) Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 326
def reset_sequence!(table, column, sequence = nil)
  # Do nothing by default. Implement for PostgreSQL, Oracle, ...
end

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

rollback_db_transaction() Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 311
def rollback_db_transaction
  exec_rollback_db_transaction
end

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

rollback_to_savepoint(name = nil) Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 317
def rollback_to_savepoint(name = nil)
  exec_rollback_to_savepoint(name)
end
sanitize_limit(limit) Show source
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 403
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. Он должен выглядеть как целое число или SQL-литерал Arel.

Возвращает Integer и Arel::Nodes::SqlLiteral limits как есть.

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

  if !prepared_statements || (arel.is_a?(String) && preparable.nil?)
    preparable = false
  elsif binds.length > bind_params_length
    sql, binds = unprepared_statement { to_sql_and_binds(arel) }
    preparable = false
  else
    preparable = visitor.preparable
  end

  if prepared_statements && preparable
    select_prepared(sql, name, binds)
  else
    select(sql, name, binds)
  end
end

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

select_one(arel, name = nil, binds = []) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 68
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 85
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 73
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 79
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 252
def transaction(requires_new: nil, isolation: nil, joinable: true)
  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) { yield }
  end
rescue ActiveRecord::Rollback
  # rollbacks are silently swallowed
end

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

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

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

Для решения этой проблемы, transaction эмулирует эффект вложенных транзакций, используя сохраняемые точки: dev.mysql.com/doc/refman/5.7/en/savepoint.html Сохраняемые точки поддерживаются MySQL и PostgreSQL. SQLite3 версии >= '3.6.8' поддерживает сохраняемые точки.

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

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

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

Ограничения

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/5.7/en/set-transaction.html

Исключение ActiveRecord::TransactionIsolationError будет поднято, если:

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

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

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

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

transaction_isolation_levels() Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 290
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 269
def transaction_open?
  current_transaction.open?
end
transaction_state() Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 283
def transaction_state
  current_transaction.state
end
truncate(table_name, name = nil) Показать исходный код
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 133
def truncate(table_name, name = nil)
  raise NotImplementedError
end

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

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

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

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

Spec-Zone.ru

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