public
Description: Ruby on Rails
Homepage: http://rubyonrails.org
Clone URL: git://github.com/rails/rails.git
Partial updates don't update lock_version if nothing changed.  [#426 
state:resolved]
Sun Jun 15 13:25:59 -0700 2008
jeremy (committer)
Sun Jun 22 20:33:43 -0700 2008
commit  3610997ba32128921115bedb89c322a7bcbe161a
tree    00ea6993b4c2981bf64a371f26a1d4bcaed7e3be
parent  0fd3e4cd2b2b1b31304a922dc65284d5363f78b6
...
1
2
 
 
3
4
5
...
1
2
3
4
5
6
7
0
@@ -1,5 +1,7 @@
0
 *Edge*
0
 
0
+* Partial updates don't update lock_version if nothing changed.  #426 [Daniel Morrison]
0
+
0
 * Fix column collision with named_scope and :joins.  #46 [Duncan Beevers, Mark Catley]
0
 
0
 * db:migrate:down and :up update schema_migrations.  #369 [Michael Raidel, RaceCondition]
...
68
69
70
 
71
72
73
...
68
69
70
71
72
73
74
0
@@ -68,6 +68,7 @@ module ActiveRecord
0
 
0
         def update_with_lock(attribute_names = @attributes.keys) #:nodoc:
0
           return update_without_lock(attribute_names) unless locking_enabled?
0
+          return 0 if attribute_names.empty?
0
 
0
           lock_col = self.class.locking_column
0
           previous_value = send(lock_col).to_i
...
2
3
4
 
5
6
7
...
125
126
127
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
129
130
...
2
3
4
5
6
7
8
...
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
0
@@ -2,6 +2,7 @@ require 'cases/helper'
0
 require 'models/topic'    # For booleans
0
 require 'models/pirate'   # For timestamps
0
 require 'models/parrot'
0
+require 'models/person'   # For optimistic locking
0
 
0
 class Pirate # Just reopening it, not defining it
0
   attr_accessor :detected_changes_in_after_update # Boolean for if changes are detected
0
@@ -125,6 +126,24 @@ class DirtyTest < ActiveRecord::TestCase
0
     end
0
   end
0
 
0
+  def test_partial_update_with_optimistic_locking
0
+    person = Person.new(:first_name => 'foo')
0
+    old_lock_version = 1
0
+
0
+    with_partial_updates Person, false do
0
+      assert_queries(2) { 2.times { person.save! } }
0
+      Person.update_all({ :first_name => 'baz' }, :id => person.id)
0
+    end
0
+
0
+    with_partial_updates Person, true do
0
+      assert_queries(0) { 2.times { person.save! } }
0
+      assert_equal old_lock_version, person.reload.lock_version
0
+
0
+      assert_queries(1) { person.first_name = 'bar'; person.save! }
0
+      assert_not_equal old_lock_version, person.reload.lock_version
0
+    end
0
+  end
0
+
0
   def test_changed_attributes_should_be_preserved_if_save_failure
0
     pirate = Pirate.new
0
     pirate.parrot_id = 1
...
29
30
31
 
32
33
34
35
 
36
37
38
...
42
43
44
 
45
46
47
48
 
49
 
50
51
52
...
54
55
56
 
57
58
59
60
61
 
62
63
64
65
 
66
67
68
...
81
82
83
 
84
85
86
87
 
88
89
90
...
93
94
95
 
96
97
98
...
146
147
148
 
 
 
 
 
 
 
 
 
149
150
151
...
29
30
31
32
33
34
35
36
37
38
39
40
...
44
45
46
47
48
49
50
51
52
53
54
55
56
57
...
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
...
89
90
91
92
93
94
95
96
97
98
99
100
...
103
104
105
106
107
108
109
...
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
0
@@ -29,10 +29,12 @@ class OptimisticLockingTest < ActiveRecord::TestCase
0
     assert_equal 0, p1.lock_version
0
     assert_equal 0, p2.lock_version
0
 
0
+    p1.first_name = 'stu'
0
     p1.save!
0
     assert_equal 1, p1.lock_version
0
     assert_equal 0, p2.lock_version
0
 
0
+    p2.first_name = 'sue'
0
     assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
0
   end
0
 
0
@@ -42,11 +44,14 @@ class OptimisticLockingTest < ActiveRecord::TestCase
0
     assert_equal 0, p1.lock_version
0
     assert_equal 0, p2.lock_version
0
 
0
+    p1.first_name = 'stu'
0
     p1.save!
0
     assert_equal 1, p1.lock_version
0
     assert_equal 0, p2.lock_version
0
 
0
+    p2.first_name = 'sue'
0
     assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
0
+    p2.first_name = 'sue2'
0
     assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
0
   end
0
 
0
@@ -54,15 +59,18 @@ class OptimisticLockingTest < ActiveRecord::TestCase
0
     p1 = Person.new(:first_name => 'anika')
0
     assert_equal 0, p1.lock_version
0
 
0
+    p1.first_name = 'anika2'
0
     p1.save!
0
     p2 = Person.find(p1.id)
0
     assert_equal 0, p1.lock_version
0
     assert_equal 0, p2.lock_version
0
 
0
+    p1.first_name = 'anika3'
0
     p1.save!
0
     assert_equal 1, p1.lock_version
0
     assert_equal 0, p2.lock_version
0
 
0
+    p2.first_name = 'sue'
0
     assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
0
   end
0
 
0
@@ -81,10 +89,12 @@ class OptimisticLockingTest < ActiveRecord::TestCase
0
     assert_equal 0, t1.version
0
     assert_equal 0, t2.version
0
 
0
+    t1.tps_report_number = 700
0
     t1.save!
0
     assert_equal 1, t1.version
0
     assert_equal 0, t2.version
0
 
0
+    t2.tps_report_number = 800
0
     assert_raises(ActiveRecord::StaleObjectError) { t2.save! }
0
   end
0
 
0
@@ -93,6 +103,7 @@ class OptimisticLockingTest < ActiveRecord::TestCase
0
     assert_equal 0, p1.lock_version
0
     assert_equal p1.lock_version, Person.new(p1.attributes).lock_version
0
 
0
+    p1.first_name = 'bianca2'
0
     p1.save!
0
     assert_equal 1, p1.lock_version
0
     assert_equal p1.lock_version, Person.new(p1.attributes).lock_version
0
@@ -146,6 +157,15 @@ class OptimisticLockingTest < ActiveRecord::TestCase
0
     assert ref.save
0
   end
0
 
0
+  # Useful for partial updates, don't only update the lock_version if there
0
+  # is nothing else being updated.
0
+  def test_update_without_attributes_does_not_only_update_lock_version
0
+    assert_nothing_raised do
0
+      p1 = Person.new(:first_name => 'anika')
0
+      p1.send(:update_with_lock, [])
0
+    end
0
+  end
0
+
0
   private
0
 
0
     def add_counter_column_to(model)

Comments