модуль 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 252 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 261 def begin_db_transaction() end
Начинает транзакцию (и отключает автоматическую фиксацию).
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 275 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 280 def commit_db_transaction() end
Фиксирует транзакцию (и включает автоматическую фиксацию).
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 139 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 98 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 91 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 84 def exec_query(sql, name = 'SQL', binds = [], prepare: false) raise NotImplementedError end
Выполняет оператор sql в контексте этого соединения, используя binds в качестве подстановок связывания. name регистрируется вместе с выполненным оператором sql.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 110 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 77 def execute(sql, name = nil) raise NotImplementedError end
Выполняет SQL-запрос в контексте этого соединения и возвращает необработанный результат от адаптера соединения. Примечание: в зависимости от вашего коннектора к базе данных, результат, возвращаемый этим методом, может управляться вручную в памяти. Рассмотрите возможность использования обертки #exec_query вместо этого.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 122 def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = []) sql, binds, pk, sequence_name = 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
Выполняет запрос INSERT и возвращает ID новой записи
id_value будет возвращено, если значение не равно nil, в противном случае база данных попытается вычислить последний вставленный ID и вернуть это значение.
Если следующий ID был вычислен заранее (как в Oracle), его следует передать как id_value.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 284 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 = [], preparable: nil)
arel, binds = binds_from_relation arel, binds
sql = to_sql(arel, binds)
if !prepared_statements || (arel.is_a?(String) && preparable.nil?)
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.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 47 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 68 def select_rows(sql, name = nil, binds = []) exec_query(sql, name, binds).rows end
Возвращает массив массивов, содержащих значения полей. Порядок такой же, как и возвращаемый columns.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 52
def select_value(arel, name = nil, binds = [])
arel, binds = binds_from_relation arel, binds
if result = select_rows(to_sql(arel, binds), name, binds).first
result.first
end
end Возвращает одно значение из записи
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 61 def select_values(arel, name = nil, binds = []) arel, binds = binds_from_relation arel, binds select_rows(to_sql(arel, binds), name, binds).map(&:first) end
Возвращает массив значений первого столбца в select:
select_values("SELECT id FROM companies LIMIT 3") => [1,2,3] # File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 147 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 225
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
Вы должны обратиться к документации вашей базы данных, чтобы понять семантику этих разных уровней:
Исключение ActiveRecord::TransactionIsolationError будет сгенерировано, если:
-
Адаптер не поддерживает настройку уровня изоляции
-
Вы присоединяетесь к существующей открытой транзакции
-
Вы создаёте вложенную (с сохраняемой точкой) транзакцию
Адаптеры mysql2 и postgresql поддерживают настройку уровня изоляции транзакций.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 263
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 242 def transaction_open? current_transaction.open? end
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 256 def transaction_state current_transaction.state end
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 103 def truncate(table_name, name = nil) raise NotImplementedError end
Выполняет оператор truncate.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 132 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.