aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/transactions.rb
diff options
context:
space:
mode:
authorRyuta Kamizono <kamipo@gmail.com>2018-05-19 11:46:03 +0900
committerRyuta Kamizono <kamipo@gmail.com>2019-04-16 12:30:45 +0900
commit63ff495bdf90e0ab20114a49db5cffe3cb9ef2fd (patch)
tree33d5e390a602ef7c77a2c675865b573802fdd35b /activerecord/lib/active_record/transactions.rb
parent20b94af9eb9305d19a343f72f0afb18eb49e2de7 (diff)
downloadrails-63ff495bdf90e0ab20114a49db5cffe3cb9ef2fd.tar.gz
rails-63ff495bdf90e0ab20114a49db5cffe3cb9ef2fd.tar.bz2
rails-63ff495bdf90e0ab20114a49db5cffe3cb9ef2fd.zip
Fix dirty tracking after rollback.
Currently the rollback only restores primary key value, `new_record?`, `destroyed?`, and `frozen?`. Since the `save` clears current dirty attribute states, retrying save after rollback will causes no change saved if partial writes is enabled (by default). This makes `remember_transaction_record_state` remembers original values then restores dirty attribute states after rollback. Fixes #15018. Fixes #30167. Fixes #33868. Fixes #33443. Closes #33444. Closes #34504.
Diffstat (limited to 'activerecord/lib/active_record/transactions.rb')
-rw-r--r--activerecord/lib/active_record/transactions.rb13
1 files changed, 10 insertions, 3 deletions
diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb
index 03a373f0af..ea288456b9 100644
--- a/activerecord/lib/active_record/transactions.rb
+++ b/activerecord/lib/active_record/transactions.rb
@@ -390,6 +390,7 @@ module ActiveRecord
id: id,
new_record: @new_record,
destroyed: @destroyed,
+ attributes: @attributes,
frozen?: frozen?,
)
@_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) + 1
@@ -422,12 +423,18 @@ module ActiveRecord
transaction_level = (@_start_transaction_state[:level] || 0) - 1
if transaction_level < 1 || force_restore_state
restore_state = @_start_transaction_state
- thaw
@new_record = restore_state[:new_record]
@destroyed = restore_state[:destroyed]
+ @attributes = restore_state[:attributes].map do |attr|
+ value = @attributes.fetch_value(attr.name)
+ attr = attr.with_value_from_user(value) if attr.value != value
+ attr
+ end
+ @mutations_from_database = nil
+ @mutations_before_last_save = nil
pk = self.class.primary_key
- if pk && _read_attribute(pk) != restore_state[:id]
- _write_attribute(pk, restore_state[:id])
+ if pk && @attributes.fetch_value(pk) != restore_state[:id]
+ @attributes.write_from_user(pk, restore_state[:id])
end
freeze if restore_state[:frozen?]
end