модуль ActiveRecord::ConnectionAdapters::DatabaseStatements
Публичные методы класса
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 4 def initialize super reset_transaction end
Публичные методы экземпляра
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 233 def add_transaction_record(record) current_transaction.add_record(record) end
Регистрирует запись в текущей транзакции, чтобы можно было вызвать её обратные вызовы after_commit и after_rollback.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 242 def begin_db_transaction() end
Начинает транзакцию (и отключает автоматическое подтверждение).
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 256 def begin_isolated_db_transaction(isolation) raise ActiveRecord::TransactionIsolationError, "adapter does not support setting transaction isolation" end
Начинает транзакцию с заданным уровнем изоляции. По умолчанию вызывает ошибку; адаптеры, поддерживающие настройку уровня изоляции, должны реализовывать этот метод.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 261 def commit_db_transaction() end
Подтверждает транзакцию (и включает автоматическое подтверждение).
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 118 def delete(arel, name = nil, binds = []) exec_delete(to_sql(arel, binds), name, binds) end
Выполняет оператор DELETE и возвращает количество изменённых строк.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 82 def exec_delete(sql, name, binds) exec_query(sql, name, binds) end
Выполняет оператор DELETE sql в контексте этого подключения, используя binds в качестве подстановок связывания. name регистрируется вместе с выполненным оператором sql.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 75 def exec_insert(sql, name, binds, pk = nil, sequence_name = nil) exec_query(sql, name, binds) end
Выполняет оператор INSERT sql в контексте этого подключения, используя binds в качестве подстановок связывания. name регистрируется вместе с выполненным оператором sql.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 69 def exec_query(sql, name = 'SQL', binds = []) end
Выполняет оператор sql в контексте этого подключения, используя binds в качестве подстановок связывания. name регистрируется вместе с выполненным оператором sql.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 94 def exec_update(sql, name, binds) exec_query(sql, name, binds) end
Выполняет оператор UPDATE sql в контексте этого подключения, используя binds в качестве подстановок связывания. name регистрируется вместе с выполненным оператором sql.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 62 def execute(sql, name = nil) end
Выполняет SQL-оператор в контексте этого подключения.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 106 def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = []) sql, binds = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds) value = exec_insert(sql, name, binds, pk, sequence_name) id_value || last_inserted_id(value) end
Возвращает последний сгенерированный идентификатор из затронутой таблицы.
id_value будет возвращён, если значение не равно null, в противном случае база данных попытается вычислить последний вставленный идентификатор и вернуть это значение.
Если следующий идентификатор был вычислен заранее (как в Oracle), он должен быть передан как id_value.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 265 def rollback_db_transaction exec_rollback_db_transaction end
Откатывает транзакцию (и включает автоматическое подтверждение). Это необходимо сделать, если блок транзакции вызывает исключение или возвращает false.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 30 def select_all(arel, name = nil, binds = []) arel, binds = binds_from_relation arel, binds select(to_sql(arel, binds), name, binds) end
Возвращает экземпляр ActiveRecord::Result.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 37 def select_one(arel, name = nil, binds = []) select_all(arel, name, binds).first end
Возвращает хэш записи, где имена столбцов являются ключами, а значения столбцов — значениями.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 57 def select_rows(sql, name = nil, binds = []) end
Возвращает массив массивов, содержащих значения полей. Порядок соответствует порядку, возвращаемому columns.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 42
def select_value(arel, name = nil, binds = [])
if result = select_one(arel, name, binds)
result.values.first
end
end Возвращает единственное значение из записи
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 50 def select_values(arel, name = nil) arel, binds = binds_from_relation arel, [] select_rows(to_sql(arel, binds), name, binds).map(&:first) end
Возвращает массив значений первого столбца в запросе:
select_values("SELECT id FROM companies LIMIT 3") => [1,2,3] # File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 124 def supports_statement_cache? false end
Возвращает true если адаптер подключения поддерживает кеширование подготовленных запросов, иначе возвращает false.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 10
def to_sql(arel, binds = [])
if arel.respond_to?(:ast)
collected = visitor.accept(arel.ast, collector)
collected.compile(binds.dup, self)
else
arel
end
end Преобразует AST arel в SQL
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 204
def transaction(options = {})
options.assert_valid_keys :requires_new, :joinable, :isolation
if !options[:requires_new] && current_transaction.joinable?
if options[:isolation]
raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
end
yield
else
transaction_manager.within_new_transaction(options) { yield }
end
rescue ActiveRecord::Rollback
# rollbacks are silently swallowed
end Выполняет заданный блок в транзакции базы данных и возвращает результат блока.
Поддержка вложенных транзакций
Большинство баз данных не поддерживают истинные вложенные транзакции. На момент написания, единственная база данных, о которой нам известно, поддерживающая истинные вложенные транзакции, это MS-SQL.
Для решения этой проблемы, transaction эмулирует эффект вложенных транзакций, используя контрольные точки: dev.mysql.com/doc/refman/5.0/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
Вы должны обратиться к документации вашей базы данных для понимания семантики этих различных уровней:
Исключение ActiveRecord::TransactionIsolationError будет сгенерировано, если:
-
Адаптер не поддерживает установку уровня изоляции
-
Вы присоединяетесь к уже открытой транзакции
-
Вы создаёте вложенную (контрольную точку) транзакцию
Адаптеры mysql, mysql2 и postgresql поддерживают установку уровня изоляции транзакций. Однако поддержка отключена для версий MySQL ниже 5, поскольку они затронуты ошибкой ошибкой, которая означает, что уровень изоляции сохраняется за пределами транзакции.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 244
def transaction_isolation_levels
{
read_uncommitted: "READ UNCOMMITTED",
read_committed: "READ COMMITTED",
repeatable_read: "REPEATABLE READ",
serializable: "SERIALIZABLE"
}
end # File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 223 def transaction_open? current_transaction.open? end
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 237 def transaction_state current_transaction.state end
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 87 def truncate(table_name, name = nil) raise NotImplementedError end
Выполняет оператор truncate.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 113 def update(arel, name = nil, binds = []) exec_update(to_sql(arel, binds), name, binds) end
Выполняет оператор update и возвращает количество изменённых строк.
© 2004–2018 David Heinemeier Hansson
Licensed under the MIT License.