aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/transactions.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/transactions.rb')
-rw-r--r--activerecord/lib/active_record/transactions.rb49
1 files changed, 26 insertions, 23 deletions
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index a7709b9489..60d4c256c4 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -11,6 +11,7 @@ module ActiveRecord
included do
define_callbacks :commit, :rollback, :terminator => "result == false", :scope => [:kind, :name]
end
+
# = Active Record Transactions
#
# Transactions are protective blocks where SQL statements are only permanent
@@ -130,7 +131,7 @@ module ActiveRecord
#
# +transaction+ calls can be nested. By default, this makes all database
# statements in the nested transaction block become part of the parent
- # transaction. For example:
+ # transaction. For example, the following behavior may be surprising:
#
# User.transaction do
# User.create(:username => 'Kotori')
@@ -140,12 +141,15 @@ module ActiveRecord
# end
# end
#
- # User.find(:all) # => empty
+ # creates both "Kotori" and "Nemu". Reason is the <tt>ActiveRecord::Rollback</tt>
+ # exception in the nested block does not issue a ROLLBACK. Since these exceptions
+ # are captured in transaction blocks, the parent block does not see it and the
+ # real transaction is committed.
#
- # It is also possible to requires a sub-transaction by passing
- # <tt>:requires_new => true</tt>. If anything goes wrong, the
- # database rolls back to the beginning of the sub-transaction
- # without rolling back the parent transaction. For example:
+ # In order to get a ROLLBACK for the nested transaction you may ask for a real
+ # sub-transaction by passing <tt>:requires_new => true</tt>. If anything goes wrong,
+ # the database rolls back to the beginning of the sub-transaction without rolling
+ # back the parent transaction. If we add it to the previous example:
#
# User.transaction do
# User.create(:username => 'Kotori')
@@ -155,12 +159,12 @@ module ActiveRecord
# end
# end
#
- # User.find(:all) # => Returns only Kotori
+ # only "Kotori" is created. (This works on MySQL and PostgreSQL, but not on SQLite3.)
#
# Most databases don't support true nested transactions. At the time of
# writing, the only database that we're aware of that supports true nested
# transactions, is MS-SQL. Because of this, Active Record emulates nested
- # transactions by using savepoints. See
+ # transactions by using savepoints on MySQL and PostgreSQL. See
# http://dev.mysql.com/doc/refman/5.0/en/savepoints.html
# for more information about savepoints.
#
@@ -224,8 +228,8 @@ module ActiveRecord
end
# See ActiveRecord::Transactions::ClassMethods for detailed documentation.
- def transaction(&block)
- self.class.transaction(&block)
+ def transaction(options = {}, &block)
+ self.class.transaction(options, &block)
end
def destroy #:nodoc:
@@ -247,6 +251,7 @@ module ActiveRecord
remember_transaction_record_state
yield
rescue Exception
+ IdentityMap.remove(self) if IdentityMap.enabled?
restore_transaction_record_state
raise
ensure
@@ -255,7 +260,7 @@ module ActiveRecord
# Call the after_commit callbacks
def committed! #:nodoc:
- _run_commit_callbacks
+ run_callbacks :commit
ensure
clear_transaction_record_state
end
@@ -263,7 +268,7 @@ module ActiveRecord
# Call the after rollback callbacks. The restore_state argument indicates if the record
# state should be rolled back to the beginning or just to the last savepoint.
def rolledback!(force_restore_state = false) #:nodoc:
- _run_rollback_callbacks
+ run_callbacks :rollback
ensure
restore_transaction_record_state(force_restore_state)
end
@@ -297,8 +302,8 @@ module ActiveRecord
# Save the new record state and id of a record so it can be restored later if a transaction fails.
def remember_transaction_record_state #:nodoc
@_start_transaction_state ||= {}
+ @_start_transaction_state[:id] = id if has_attribute?(self.class.primary_key)
unless @_start_transaction_state.include?(:new_record)
- @_start_transaction_state[:id] = id if has_attribute?(self.class.primary_key)
@_start_transaction_state[:new_record] = @new_record
end
unless @_start_transaction_state.include?(:destroyed)
@@ -321,16 +326,14 @@ module ActiveRecord
@_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
if @_start_transaction_state[:level] < 1
restore_state = remove_instance_variable(:@_start_transaction_state)
- if restore_state
- @attributes = @attributes.dup if @attributes.frozen?
- @new_record = restore_state[:new_record]
- @destroyed = restore_state[:destroyed]
- if restore_state[:id]
- self.id = restore_state[:id]
- else
- @attributes.delete(self.class.primary_key)
- @attributes_cache.delete(self.class.primary_key)
- end
+ @attributes = @attributes.dup if @attributes.frozen?
+ @new_record = restore_state[:new_record]
+ @destroyed = restore_state[:destroyed]
+ if restore_state.has_key?(:id)
+ self.id = restore_state[:id]
+ else
+ @attributes.delete(self.class.primary_key)
+ @attributes_cache.delete(self.class.primary_key)
end
end
end