diff options
author | Eugene Kenny <elkenny@gmail.com> | 2017-03-13 02:01:18 +0000 |
---|---|---|
committer | Eugene Kenny <elkenny@gmail.com> | 2017-07-09 01:26:20 +0100 |
commit | b19c4eff47d912ee3a038a6e0653eea7df5b67b8 (patch) | |
tree | 7412e9d3c3e019d6f6e93ff1e328836412ecca65 /activerecord/lib/active_record/attribute_methods | |
parent | 72e1c4229ab0af6a76c1635adff76fc02fa7fe71 (diff) | |
download | rails-b19c4eff47d912ee3a038a6e0653eea7df5b67b8.tar.gz rails-b19c4eff47d912ee3a038a6e0653eea7df5b67b8.tar.bz2 rails-b19c4eff47d912ee3a038a6e0653eea7df5b67b8.zip |
Sync transaction state when accessing primary key
If a record is modified inside a transaction, it must check the outcome
of that transaction before accessing any state which would no longer be
valid if it was rolled back.
For example, consider a new record that was saved inside a transaction
which was later rolled back: it should be restored to its previous state
so that saving it again inserts a new row into the database instead of
trying to update a row that no longer exists.
The `id` and `id=` methods defined on the PrimaryKey module implement
this correctly, but when a model uses a custom primary key, the reader
and writer methods for that attribute must check the transaction state
too. The `read_attribute` and `write_attribute` methods also need to
check the transaction state when accessing the primary key.
Diffstat (limited to 'activerecord/lib/active_record/attribute_methods')
-rw-r--r-- | activerecord/lib/active_record/attribute_methods/read.rb | 3 | ||||
-rw-r--r-- | activerecord/lib/active_record/attribute_methods/write.rb | 3 |
2 files changed, 6 insertions, 0 deletions
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb index fdc4bf6621..f7f3b7ec96 100644 --- a/activerecord/lib/active_record/attribute_methods/read.rb +++ b/activerecord/lib/active_record/attribute_methods/read.rb @@ -29,9 +29,11 @@ module ActiveRecord temp_method = "__temp__#{safe_name}" ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name + sync_with_transaction_state = "sync_with_transaction_state" if name == primary_key generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1 def #{temp_method} + #{sync_with_transaction_state} name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name} _read_attribute(name) { |n| missing_attribute(n, caller) } end @@ -55,6 +57,7 @@ module ActiveRecord end name = self.class.primary_key if name == "id".freeze && self.class.primary_key + sync_with_transaction_state if name == self.class.primary_key _read_attribute(name, &block) end diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb index 54b673c72e..9c43567a11 100644 --- a/activerecord/lib/active_record/attribute_methods/write.rb +++ b/activerecord/lib/active_record/attribute_methods/write.rb @@ -13,10 +13,12 @@ module ActiveRecord def define_method_attribute=(name) safe_name = name.unpack("h*".freeze).first ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name + sync_with_transaction_state = "sync_with_transaction_state" if name == primary_key generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1 def __temp__#{safe_name}=(value) name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name} + #{sync_with_transaction_state} _write_attribute(name, value) end alias_method #{(name + '=').inspect}, :__temp__#{safe_name}= @@ -36,6 +38,7 @@ module ActiveRecord end name = self.class.primary_key if name == "id".freeze && self.class.primary_key + sync_with_transaction_state if name == self.class.primary_key _write_attribute(name, value) end |