diff options
-rw-r--r-- | activerecord/CHANGELOG | 4 | ||||
-rwxr-xr-x | activerecord/lib/active_record/connection_adapters/abstract_adapter.rb | 8 | ||||
-rw-r--r-- | activerecord/lib/active_record/transactions.rb | 24 | ||||
-rw-r--r-- | activerecord/test/transactions_test.rb | 2 |
4 files changed, 20 insertions, 18 deletions
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 7e26b3c10a..a82f8a0013 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,9 @@ *SVN* +* Fixed that nested transactions now work by letting the outer most transaction have the responsibilty of starting and rolling back the transaction. + If any of the inner transactions swallow the exception raised, though, the transaction will not be rolled back. So always let the transaction + bubble up even when you've dealt with local issues. Closes #231 and #340. + * Fixed validates_{confirmation,acceptance}_of to only happen when the virtual attributes are not nil #348 [dpiddy@gmail.com] * Added a require_association hook on const_missing that makes it possible to use any model class without requiring it first. This makes STI look like: diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index b521a0fdf8..d41356ffa4 100755 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -298,16 +298,16 @@ module ActiveRecord end # Wrap a block in a transaction. Returns result of block. - def transaction + def transaction(start_db_transaction = true) begin if block_given? - begin_db_transaction + begin_db_transaction if start_db_transaction result = yield - commit_db_transaction + commit_db_transaction if start_db_transaction result end rescue Exception => database_transaction_rollback - rollback_db_transaction + rollback_db_transaction if start_db_transaction raise end end diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index d440e74346..16c56c58f0 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -77,13 +77,17 @@ module ActiveRecord # Tribute: Object-level transactions are implemented by Transaction::Simple by Austin Ziegler. module ClassMethods def transaction(*objects, &block) - TRANSACTION_MUTEX.lock + TRANSACTION_MUTEX.synchronize do + Thread.current['open_transactions'] ||= 0 + Thread.current['start_db_transaction'] = (Thread.current['open_transactions'] == 0) + Thread.current['open_transactions'] += 1 + end begin objects.each { |o| o.extend(Transaction::Simple) } objects.each { |o| o.start_transaction } - result = connection.transaction(&block) + result = connection.transaction(Thread.current['start_db_transaction'], &block) objects.each { |o| o.commit_transaction } return result @@ -91,7 +95,9 @@ module ActiveRecord objects.each { |o| o.abort_transaction } raise ensure - TRANSACTION_MUTEX.unlock + TRANSACTION_MUTEX.synchronize do + Thread.current['open_transactions'] -= 1 + end end end end @@ -101,19 +107,11 @@ module ActiveRecord end def destroy_with_transactions #:nodoc: - if TRANSACTION_MUTEX.locked? - destroy_without_transactions - else - transaction { destroy_without_transactions } - end + transaction { destroy_without_transactions } end def save_with_transactions(perform_validation = true) #:nodoc: - if TRANSACTION_MUTEX.locked? - save_without_transactions(perform_validation) - else - transaction { save_without_transactions(perform_validation) } - end + transaction { save_without_transactions(perform_validation) } end end end diff --git a/activerecord/test/transactions_test.rb b/activerecord/test/transactions_test.rb index 18b2ea3e65..cf10b7d3ee 100644 --- a/activerecord/test/transactions_test.rb +++ b/activerecord/test/transactions_test.rb @@ -84,7 +84,7 @@ class TransactionTest < Test::Unit::TestCase end end - def xtest_nested_explicit_transactions + def test_nested_explicit_transactions Topic.transaction do Topic.transaction do @first.approved = 1 |