модуль ActiveRecord::ConnectionAdapters::DatabaseStatements
Публичные методы класса
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 4 def initialize super reset_transaction end
Публичные методы экземпляра
Регистрирует запись в текущей транзакции, чтобы можно было вызвать её обратные вызовы after_commit и after_rollback.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 248 def add_transaction_record(record) @transaction.add_record(record) end
Начинает транзакцию (и отключает автоматическую фиксацию).
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 253 def begin_db_transaction() end
Начинает транзакцию с установленным уровнем изоляции. По умолчанию выдает ошибку; адаптеры, поддерживающие установку уровня изоляции, должны реализовать этот метод.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 267 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 272 def commit_db_transaction() end
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 278 def default_sequence_name(table, column) nil end
Выполняет оператор delete и возвращает количество затронутых строк.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 105 def delete(arel, name = nil, binds = []) exec_delete(to_sql(arel, binds), name, binds) end
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 301 def empty_insert_statement_value "DEFAULT VALUES" end
Выполняет оператор delete sql в контексте этого соединения, используя binds в качестве подстановок связываний. name регистрируется вместе с выполненным оператором sql.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 74 def exec_delete(sql, 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 67 def exec_insert(sql, name, binds, pk = nil, sequence_name = nil) exec_query(sql, name, binds) end
Выполняет оператор sql в контексте этого соединения, используя binds в качестве подстановок связываний. name регистрируется вместе с выполненным оператором sql.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 61 def exec_query(sql, name = 'SQL', binds = []) end
Выполняет оператор update sql в контексте этого соединения, используя binds в качестве подстановок связываний. name регистрируется вместе с выполненным оператором sql.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 81 def exec_update(sql, name, binds) exec_query(sql, name, binds) end
Выполняет SQL-запрос в контексте этого соединения.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 54 def execute(sql, name = nil) end
Возвращает последний автоматически сгенерированный ID из затронутой таблицы.
id_value будет возвращено, если значение не равно nil, в противном случае база данных попытается вычислить последний вставленный ID и вернуть это значение.
Если следующий ID был вычислен заранее (как в Oracle), его следует передать как id_value.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 93 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
Вставляет данный заполнитель в таблицу. Переопределяется в адаптерах, которые требуют чего-то большего, чем простая вставка (например, Oracle).
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 289
def insert_fixture(fixture, table_name)
columns = schema_cache.columns_hash(table_name)
key_list = []
value_list = fixture.map do |name, value|
key_list << quote_column_name(name)
quote(value, columns[name])
end
execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", 'Fixture Insert'
end # File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 305
def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
"WHERE #{quoted_primary_key} IN (SELECT #{quoted_primary_key} FROM #{quoted_table_name} #{where_sql})"
end Устанавливает последовательность в максимальное значение столбца таблицы.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 283 def reset_sequence!(table, column, sequence = nil) # Do nothing by default. Implement for PostgreSQL, Oracle, ... end
Откатывает транзакцию (и включает автоматическую фиксацию). Необходимо выполнить, если блок транзакции вызывает исключение или возвращает false.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 276 def rollback_db_transaction() end
Очищает заданный параметр LIMIT, чтобы предотвратить SQL-инъекции.
limit может быть чем угодно, что может быть преобразовано в строку через to_s. Он должен выглядеть как целое число, или список целых чисел, разделенных запятыми, или Arel SQL литерал.
Возвращает Integer и Arel::Nodes::SqlLiteral limits как есть. Возвращает очищенный параметр limit, либо как целое число, либо как строку, содержащую список целых чисел, разделенных запятыми.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 318
def sanitize_limit(limit)
if limit.is_a?(Integer) || limit.is_a?(Arel::Nodes::SqlLiteral)
limit
elsif limit.to_s.include?(',')
Arel.sql limit.to_s.split(',').map{ |i| Integer(i) }.join(',')
else
Integer(limit)
end
end Возвращает экземпляр ActiveRecord::Result.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 22 def select_all(arel, name = nil, binds = []) arel, binds = binds_from_relation arel, binds select(to_sql(arel, binds), name, binds) end
Возвращает хэш записи с именами столбцов в качестве ключей и значениями столбцов в качестве значений.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 29 def select_one(arel, name = nil, binds = []) select_all(arel, name, binds).first end
Возвращает массив массивов, содержащих значения полей. Порядок такой же, как и возвращаемый columns.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 49 def select_rows(sql, name = nil, binds = []) end
Возвращает одно значение из записи
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 34
def select_value(arel, name = nil, binds = [])
if result = select_one(arel, name, binds)
result.values.first
end
end Возвращает массив значений первого столбца в запросе:
select_values("SELECT id FROM companies LIMIT 3") => [1,2,3] # File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 42 def select_values(arel, name = nil) arel, binds = binds_from_relation arel, [] select_rows(to_sql(arel, binds), name, binds).map(&:first) end
Возвращает true, если адаптер подключения поддерживает кэширование подготовленных запросов, иначе возвращает false
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 111 def supports_statement_cache? false end
Преобразует AST arel в SQL
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 10
def to_sql(arel, binds = [])
if arel.respond_to?(:ast)
binds = binds.dup
visitor.accept(arel.ast) do
quote(*binds.shift.reverse)
end
else
arel
end
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 191
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
within_new_transaction(options) { yield }
end
rescue ActiveRecord::Rollback
# rollbacks are silently swallowed
end # File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 255
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 226 def transaction_open? @transaction.open? end
Выполняет оператор обновления и возвращает количество измененных строк.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 100 def update(arel, name = nil, binds = []) exec_update(to_sql(arel, binds), name, binds) end
Защищенные методы экземпляра
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 383
def binds_from_relation(relation, binds)
if relation.is_a?(Relation) && binds.blank?
relation, binds = relation.arel, relation.bind_values
end
[relation, binds]
end Выполняет оператор удаления и возвращает количество измененных строк.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 370 def delete_sql(sql, name = nil) update_sql(sql, name) end
Возвращает последний сгенерированный ID из затронутой таблицы.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 359 def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) execute(sql, name) id_value end
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 378 def last_inserted_id(result) row = result.rows.first row && row.first end
Возвращает экземпляр ActiveRecord::Result.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 354 def select(sql, name = nil, binds = []) end
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 374 def sql_for_insert(sql, pk, id_value, sequence_name, binds) [sql, binds] end
Возвращает подзапрос для заданного ключа, используя информацию о соединении.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 347 def subquery_for(key, select) subselect = select.clone subselect.projections = [key] subselect end
Выполняет оператор обновления и возвращает количество измененных строк.
# File activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb, line 365 def update_sql(sql, name = nil) execute(sql, name) end
© 2004–2016 David Heinemeier Hansson
Licensed under the MIT License.