Skip to content

Commit

Permalink
Fix ActiveRecord::Persistence#touch with locking
Browse files Browse the repository at this point in the history
`ActiveRecord::Persistence#touch` does not work well when optimistic
locking enabled and `locking_column`, without default value, is null in
the database.
  • Loading branch information
bogdanvlviv committed Jun 20, 2017
1 parent 890de77 commit 4d1264c
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 3 deletions.
5 changes: 5 additions & 0 deletions activerecord/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
* `ActiveRecord::Persistence#touch` does not work well when optimistic locking enabled and
`locking_column`, without default value, is null in the database.

*bogdanvlviv*

* Fix destroying existing object does not work well when optimistic locking enabled and
`locking column` is null in the database.

Expand Down
2 changes: 1 addition & 1 deletion activerecord/lib/active_record/persistence.rb
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ def touch(*names, time: nil)

if locking_enabled?
locking_column = self.class.locking_column
scope = scope.where(locking_column => _read_attribute(locking_column))
scope = scope.where(locking_column => read_attribute_before_type_cast(locking_column))
changes[locking_column] = increment_lock
end

Expand Down
27 changes: 25 additions & 2 deletions activerecord/test/cases/locking_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,33 @@ def test_lock_without_default_sets_version_to_zero
assert_equal 0, t1.lock_version_before_type_cast
end

def test_touch_existing_lock_without_default_should_work_with_null_in_the_database
ActiveRecord::Base.connection.execute("INSERT INTO lock_without_defaults(title) VALUES('title1')")
t1 = LockWithoutDefault.last

assert_equal 0, t1.lock_version
assert_nil t1.lock_version_before_type_cast

t1.touch

assert_equal 1, t1.lock_version
end

def test_touch_stale_object_with_lock_without_default
t1 = LockWithoutDefault.create!(title: "title1")
stale_object = LockWithoutDefault.find(t1.id)

t1.update!(title: "title2")

assert_raises(ActiveRecord::StaleObjectError) do
stale_object.touch
end
end

def test_lock_without_default_should_work_with_null_in_the_database
ActiveRecord::Base.connection.execute("INSERT INTO lock_without_defaults(title) VALUES('title1')")
t1 = LockWithoutDefault.last
t2 = LockWithoutDefault.last
t2 = LockWithoutDefault.find(t1.id)

assert_equal 0, t1.lock_version
assert_nil t1.lock_version_before_type_cast
Expand Down Expand Up @@ -304,7 +327,7 @@ def test_lock_with_custom_column_without_default_should_work_with_null_in_the_da
ActiveRecord::Base.connection.execute("INSERT INTO lock_without_defaults_cust(title) VALUES('title1')")

t1 = LockWithCustomColumnWithoutDefault.last
t2 = LockWithCustomColumnWithoutDefault.last
t2 = LockWithCustomColumnWithoutDefault.find(t1.id)

assert_equal 0, t1.custom_lock_version
assert_nil t1.custom_lock_version_before_type_cast
Expand Down
2 changes: 2 additions & 0 deletions activerecord/test/schema/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -453,11 +453,13 @@
create_table :lock_without_defaults, force: true do |t|
t.column :title, :string
t.column :lock_version, :integer
t.timestamps null: true
end

create_table :lock_without_defaults_cust, force: true do |t|
t.column :title, :string
t.column :custom_lock_version, :integer
t.timestamps null: true
end

create_table :magazines, force: true do |t|
Expand Down

0 comments on commit 4d1264c

Please sign in to comment.