module 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 255 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 264 def begin_db_transaction() end
Начинает транзакцию (и отключает автоматическую фиксацию).
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 278 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 283 def commit_db_transaction() end
Фиксирует транзакцию (и включает автоматическую фиксацию).
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 144 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 108 def exec_delete(sql, name = nil, binds = []) exec_query(sql, name, binds) end
Выполняет оператор delete sql в контексте этого соединения, используя binds в качестве заменителей привязки. name регистрируется вместе с выполненным оператором sql.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 100 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.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 93 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 120 def exec_update(sql, name = nil, binds = []) exec_query(sql, name, binds) end
Выполняет оператор update sql в контексте этого соединения, используя binds в качестве заменителей привязки. name регистрируется вместе с выполненным оператором sql.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 86 def execute(sql, name = nil) raise NotImplementedError end
Выполняет SQL-запрос в контексте этого соединения и возвращает необработанный результат из адаптера соединения. Примечание: в зависимости от вашего средства подключения к базе данных, результат, возвращаемый этим методом, может управляться вручную в памяти. Рассмотрите возможность использования обертки #exec_query вместо этого.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 132 def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = []) value = exec_insert(to_sql(arel, binds), 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 287 def rollback_db_transaction exec_rollback_db_transaction end
Откатывает транзакцию (и включает автоматическую фиксацию). Необходимо сделать, если блок транзакции вызывает исключение или возвращает false.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 31
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 48 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 65 def select_rows(arel, name = nil, binds = []) select_all(arel, name, binds).rows end
Возвращает массив массивов, содержащих значения полей. Порядок такой же, как и возвращаемый columns.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 53 def select_value(arel, name = nil, binds = []) single_value_from_rows(select_rows(arel, name, binds)) end
Возвращает одно значение из записи
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 59 def select_values(arel, name = nil, binds = []) select_rows(arel, 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 150 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, self).freeze
else
arel.dup.freeze
end
end Преобразует AST arel в SQL
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 228
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 266
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 245 def transaction_open? current_transaction.open? end
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 259 def transaction_state current_transaction.state end
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 113 def truncate(table_name, name = nil) raise NotImplementedError end
Выполняет оператор truncate.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 139 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.