Skip to content
Browse files

Reimplement Jeremy's PostgreSQL automatic transaction state introspec…

…tion code.

- Fixed compatibility with the old 'postgres' driver which doesn't support
  transaction state introspection.
- Added unit tests for it.
  • Loading branch information...
1 parent e916aa7 commit fb2325e35855d62abd2c76ce03feaa3ca7992e4f @FooBarWidget FooBarWidget committed Oct 9, 2008
View
20 activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -53,6 +53,20 @@ def update(sql, name = nil)
def delete(sql, name = nil)
delete_sql(sql, name)
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
# of the block.
@@ -119,7 +133,7 @@ def transaction(start_db_transaction = false)
yield
end
rescue Exception => database_transaction_rollback
- if transaction_open
+ if transaction_open && !outside_transaction?
transaction_open = false
decrement_open_transactions
if open_transactions == 0
@@ -131,7 +145,9 @@ def transaction(start_db_transaction = false)
raise unless database_transaction_rollback.is_a? ActiveRecord::Rollback
end
ensure
- if transaction_open
+ if outside_transaction?
+ @open_transactions = 0
+ elsif transaction_open
decrement_open_transactions
begin
if open_transactions == 0
View
10 activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -532,6 +532,16 @@ def commit_db_transaction
def rollback_db_transaction
execute "ROLLBACK"
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
execute("SAVEPOINT #{current_savepoint_name}")
View
34 activerecord/test/cases/transactions_test.rb
@@ -310,6 +310,7 @@ def test_many_savepoints
def test_rollback_when_commit_raises
Topic.connection.expects(:begin_db_transaction)
Topic.connection.expects(:commit_db_transaction).raises('OH NOES')
+ Topic.connection.expects(:outside_transaction?).returns(false)
Topic.connection.expects(:rollback_db_transaction)
assert_raise RuntimeError do
@@ -319,6 +320,39 @@ def test_rollback_when_commit_raises
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
return true unless current_adapter?(:SQLite3Adapter, :SQLiteAdapter)

0 comments on commit fb2325e

Please sign in to comment.
Something went wrong with that request. Please try again.