mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Reimplement Jeremy's PostgreSQL automatic transaction state introspection code.
- Fixed compatibility with the old 'postgres' driver which doesn't support transaction state introspection. - Added unit tests for it.
This commit is contained in:
parent
e916aa7ea1
commit
fb2325e358
3 changed files with 62 additions and 2 deletions
|
@ -53,6 +53,20 @@ module ActiveRecord
|
||||||
def delete(sql, name = nil)
|
def delete(sql, name = nil)
|
||||||
delete_sql(sql, name)
|
delete_sql(sql, name)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Checks whether there is currently no transaction active. This is done
|
||||||
|
# by querying the database driver, and does not use the transaction
|
||||||
|
# house-keeping information recorded by #increment_open_transactions and
|
||||||
|
# friends.
|
||||||
|
#
|
||||||
|
# Returns true if there is no transaction active, false if there is a
|
||||||
|
# transaction active, and nil if this information is unknown.
|
||||||
|
#
|
||||||
|
# Not all adapters supports transaction state introspection. Currently,
|
||||||
|
# only the PostgreSQL adapter supports this.
|
||||||
|
def outside_transaction?
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
# Runs the given block in a database transaction, and returns the result
|
# Runs the given block in a database transaction, and returns the result
|
||||||
# of the block.
|
# of the block.
|
||||||
|
@ -119,7 +133,7 @@ module ActiveRecord
|
||||||
yield
|
yield
|
||||||
end
|
end
|
||||||
rescue Exception => database_transaction_rollback
|
rescue Exception => database_transaction_rollback
|
||||||
if transaction_open
|
if transaction_open && !outside_transaction?
|
||||||
transaction_open = false
|
transaction_open = false
|
||||||
decrement_open_transactions
|
decrement_open_transactions
|
||||||
if open_transactions == 0
|
if open_transactions == 0
|
||||||
|
@ -131,7 +145,9 @@ module ActiveRecord
|
||||||
raise unless database_transaction_rollback.is_a? ActiveRecord::Rollback
|
raise unless database_transaction_rollback.is_a? ActiveRecord::Rollback
|
||||||
end
|
end
|
||||||
ensure
|
ensure
|
||||||
if transaction_open
|
if outside_transaction?
|
||||||
|
@open_transactions = 0
|
||||||
|
elsif transaction_open
|
||||||
decrement_open_transactions
|
decrement_open_transactions
|
||||||
begin
|
begin
|
||||||
if open_transactions == 0
|
if open_transactions == 0
|
||||||
|
|
|
@ -532,6 +532,16 @@ module ActiveRecord
|
||||||
def rollback_db_transaction
|
def rollback_db_transaction
|
||||||
execute "ROLLBACK"
|
execute "ROLLBACK"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if PGconn.public_method_defined?(:transaction_status)
|
||||||
|
# ruby-pg defines Ruby constants for transaction status,
|
||||||
|
# ruby-postgres does not.
|
||||||
|
PQTRANS_IDLE = defined?(PGconn::PQTRANS_IDLE) ? PGconn::PQTRANS_IDLE : 0
|
||||||
|
|
||||||
|
def outside_transaction?
|
||||||
|
@connection.transaction_status == PQTRANS_IDLE
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def create_savepoint
|
def create_savepoint
|
||||||
execute("SAVEPOINT #{current_savepoint_name}")
|
execute("SAVEPOINT #{current_savepoint_name}")
|
||||||
|
|
|
@ -310,6 +310,7 @@ class TransactionTest < ActiveRecord::TestCase
|
||||||
def test_rollback_when_commit_raises
|
def test_rollback_when_commit_raises
|
||||||
Topic.connection.expects(:begin_db_transaction)
|
Topic.connection.expects(:begin_db_transaction)
|
||||||
Topic.connection.expects(:commit_db_transaction).raises('OH NOES')
|
Topic.connection.expects(:commit_db_transaction).raises('OH NOES')
|
||||||
|
Topic.connection.expects(:outside_transaction?).returns(false)
|
||||||
Topic.connection.expects(:rollback_db_transaction)
|
Topic.connection.expects(:rollback_db_transaction)
|
||||||
|
|
||||||
assert_raise RuntimeError do
|
assert_raise RuntimeError do
|
||||||
|
@ -319,6 +320,39 @@ class TransactionTest < ActiveRecord::TestCase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if current_adapter?(:PostgreSQLAdapter) && PGconn.public_method_defined?(:transaction_status)
|
||||||
|
def test_outside_transaction_works
|
||||||
|
Topic.logger.info("-------------")
|
||||||
|
assert Topic.connection.outside_transaction?
|
||||||
|
Topic.connection.begin_db_transaction
|
||||||
|
assert !Topic.connection.outside_transaction?
|
||||||
|
Topic.connection.rollback_db_transaction
|
||||||
|
assert Topic.connection.outside_transaction?
|
||||||
|
end
|
||||||
|
|
||||||
|
uses_mocha 'mocking connection.rollback_db_transaction' do
|
||||||
|
def test_rollback_wont_be_executed_if_no_transaction_active
|
||||||
|
assert_raise RuntimeError do
|
||||||
|
Topic.transaction do
|
||||||
|
Topic.connection.rollback_db_transaction
|
||||||
|
Topic.connection.expects(:rollback_db_transaction).never
|
||||||
|
raise "Rails doesn't scale!"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_open_transactions_count_is_reset_to_zero_if_no_transaction_active
|
||||||
|
Topic.transaction do
|
||||||
|
Topic.transaction do
|
||||||
|
Topic.connection.rollback_db_transaction
|
||||||
|
end
|
||||||
|
assert_equal 0, Topic.connection.open_transactions
|
||||||
|
end
|
||||||
|
assert_equal 0, Topic.connection.open_transactions
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_sqlite_add_column_in_transaction_raises_statement_invalid
|
def test_sqlite_add_column_in_transaction_raises_statement_invalid
|
||||||
return true unless current_adapter?(:SQLite3Adapter, :SQLiteAdapter)
|
return true unless current_adapter?(:SQLite3Adapter, :SQLiteAdapter)
|
||||||
|
|
Loading…
Reference in a new issue