Skip to content

Commit

Permalink
Merge pull request #29724 from eugeneius/sync_primary_key
Browse files Browse the repository at this point in the history
Sync transaction state when accessing primary key
  • Loading branch information
matthewd committed Aug 2, 2017
2 parents f9a43f2 + b19c4ef commit ac6cd68
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 5 deletions.
3 changes: 3 additions & 0 deletions activerecord/lib/active_record/attribute_methods/read.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ def define_method_attribute(name)
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
Expand All @@ -57,6 +59,7 @@ def read_attribute(attr_name, &block)
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

Expand Down
3 changes: 3 additions & 0 deletions activerecord/lib/active_record/attribute_methods/write.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ module ClassMethods
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}=
Expand All @@ -38,6 +40,7 @@ def write_attribute(attr_name, value)
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

Expand Down
4 changes: 2 additions & 2 deletions activerecord/lib/active_record/transactions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -432,8 +432,8 @@ def restore_transaction_record_state(force = false)
@new_record = restore_state[:new_record]
@destroyed = restore_state[:destroyed]
pk = self.class.primary_key
if pk && read_attribute(pk) != restore_state[:id]
write_attribute(pk, restore_state[:id])
if pk && _read_attribute(pk) != restore_state[:id]
_write_attribute(pk, restore_state[:id])
end
freeze if restore_state[:frozen?]
end
Expand Down
52 changes: 49 additions & 3 deletions activerecord/test/cases/transactions_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,7 @@ def test_restore_custom_primary_key_after_rollback
raise ActiveRecord::Rollback
end

assert_nil movie.id
assert_nil movie.movieid
end

def test_assign_id_after_rollback
Expand All @@ -709,8 +709,54 @@ def test_assign_custom_primary_key_after_rollback
raise ActiveRecord::Rollback
end

movie.id = nil
assert_nil movie.id
movie.movieid = nil
assert_nil movie.movieid
end

def test_read_attribute_after_rollback
topic = Topic.new

Topic.transaction do
topic.save!
raise ActiveRecord::Rollback
end

assert_nil topic.read_attribute(:id)
end

def test_read_attribute_with_custom_primary_key_after_rollback
movie = Movie.new(name: "foo")

Movie.transaction do
movie.save!
raise ActiveRecord::Rollback
end

assert_nil movie.read_attribute(:movieid)
end

def test_write_attribute_after_rollback
topic = Topic.create!

Topic.transaction do
topic.save!
raise ActiveRecord::Rollback
end

topic.write_attribute(:id, nil)
assert_nil topic.id
end

def test_write_attribute_with_custom_primary_key_after_rollback
movie = Movie.create!(name: "foo")

Movie.transaction do
movie.save!
raise ActiveRecord::Rollback
end

movie.write_attribute(:movieid, nil)
assert_nil movie.movieid
end

def test_rollback_of_frozen_records
Expand Down

0 comments on commit ac6cd68

Please sign in to comment.