aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--activerecord/CHANGELOG2
-rw-r--r--activerecord/lib/active_record/locking/optimistic.rb17
-rw-r--r--activerecord/test/fixtures/db_definitions/schema.rb8
-rw-r--r--activerecord/test/locking_test.rb16
4 files changed, 43 insertions, 0 deletions
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index 460d7f5f3f..719ae56748 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -1,5 +1,7 @@
*SVN*
+* Optimistic locking: gracefully handle nil versions, treat as zero. #5908 [Tom Ward]
+
* validates_confirmation_of only kicks in when the attribute, rather than its confirmation, is present. #785 [z@wzph.com]
* to_xml: the :methods option works on arrays of records. #5845 [Josh Starcher]
diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb
index 7bae573924..823f4e19fc 100644
--- a/activerecord/lib/active_record/locking/optimistic.rb
+++ b/activerecord/lib/active_record/locking/optimistic.rb
@@ -30,6 +30,8 @@ module ActiveRecord
base.lock_optimistically = true
base.alias_method_chain :update, :lock
+ base.alias_method_chain :attributes_from_column_definition, :lock
+
class << base
alias_method :locking_column=, :set_locking_column
end
@@ -39,6 +41,21 @@ module ActiveRecord
lock_optimistically && respond_to?(self.class.locking_column)
end
+ def attributes_from_column_definition_with_lock
+ result = attributes_from_column_definition_without_lock
+
+ # If the locking column has no default value set,
+ # start the lock version at zero. Note we can't use
+ # locking_enabled? at this point as @attributes may
+ # not have been initialized yet
+
+ if lock_optimistically && result.include?(self.class.locking_column)
+ result[self.class.locking_column] ||= 0
+ end
+
+ return result
+ end
+
def update_with_lock #:nodoc:
return update_without_lock unless locking_enabled?
diff --git a/activerecord/test/fixtures/db_definitions/schema.rb b/activerecord/test/fixtures/db_definitions/schema.rb
index e06c14b6ae..6f4aca971e 100644
--- a/activerecord/test/fixtures/db_definitions/schema.rb
+++ b/activerecord/test/fixtures/db_definitions/schema.rb
@@ -49,4 +49,12 @@ ActiveRecord::Schema.define do
t.column :sink_id, :integer, :null => false
end
add_index :edges, [:source_id, :sink_id], :unique => true, :name => 'unique_edge_index'
+
+ create_table :lock_without_defaults, :force => true do |t|
+ t.column :lock_version, :integer
+ end
+
+ create_table :lock_with_custom_column_without_defaults, :force => true do |t|
+ t.column :custom_lock_version, :integer
+ end
end
diff --git a/activerecord/test/locking_test.rb b/activerecord/test/locking_test.rb
index 00df4bb8e4..80f0dc8496 100644
--- a/activerecord/test/locking_test.rb
+++ b/activerecord/test/locking_test.rb
@@ -2,6 +2,12 @@ require 'abstract_unit'
require 'fixtures/person'
require 'fixtures/legacy_thing'
+class LockWithoutDefault < ActiveRecord::Base; end
+
+class LockWithCustomColumnWithoutDefault < ActiveRecord::Base
+ set_locking_column :custom_lock_version
+end
+
class OptimisticLockingTest < Test::Unit::TestCase
fixtures :people, :legacy_things
@@ -56,6 +62,16 @@ class OptimisticLockingTest < Test::Unit::TestCase
assert_equal 1, p1.lock_version
assert_equal p1.lock_version, Person.new(p1.attributes).lock_version
end
+
+ def test_lock_without_default_sets_version_to_zero
+ t1 = LockWithoutDefault.new
+ assert_equal 0, t1.lock_version
+ end
+
+ def test_lock_with_custom_column_without_default_sets_version_to_zero
+ t1 = LockWithCustomColumnWithoutDefault.new
+ assert_equal 0, t1.custom_lock_version
+ end
end