Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Make uniqueness validations correctly handle nil values

Previously, the uniqueness validations did not work correctly for
nil values, since they checked if there were other rows where
(column IS NULL).  However, in SQL, NULL = NULL is not true, and
multiple rows are allowed to have NULL values in a column with a
unique constraint.  So skip the uniqueness check if the value is
nil.

If you have a UNIQUE NOT NULL column, you should have a separate
presence check to check for the nil values.
  • Loading branch information...
commit 23f9c4b3634e5edb1a10eb4f0118c6f17d33dc71 1 parent 97304e6
@jeremyevans authored
View
2  CHANGELOG
@@ -1,5 +1,7 @@
=== HEAD
+* Make uniqueness validations correctly handle nil values (jeremyevans)
+
* Add ConnectionPool#pool_type to get the type of connection pool in use (jeremyevans)
* Explicitly mark primary keys as NOT NULL on SQLite (jeremyevans)
View
1  lib/sequel/plugins/validation_class_methods.rb
@@ -385,6 +385,7 @@ def validates_uniqueness_of(*atts)
error_field = a
a = Array(a)
v = Array(v)
+ next if v.empty? || !v.all?
ds = o.class.filter(a.zip(v))
num_dups = ds.count
allow = if num_dups == 0
View
4 lib/sequel/plugins/validation_helpers.rb
@@ -224,7 +224,9 @@ def validates_unique(*atts)
ds = if where
where.call(model.dataset, self, arr)
else
- model.where(arr.map{|x| [x, send(x)]})
+ vals = arr.map{|x| send(x)}
+ next unless vals.all?
+ model.where(arr.zip(vals))
end
ds = yield(ds) if block_given?
ds = ds.exclude(pk_hash) unless new?
View
20 spec/extensions/validation_class_methods_spec.rb
@@ -578,7 +578,7 @@ def dont_skip; true; end
specify "should validate uniqueness_of with allow_missing => true" do
@c.validates_uniqueness_of :value, :allow_missing => true
@m.should be_valid
- @m.value = nil
+ @m.value = 1
@m.should_not be_valid
end
end
@@ -858,6 +858,12 @@ class ::User < Sequel::Model
@user = User.new(:username => "0records", :password => "anothertest")
@user.should be_valid
@user.errors.full_messages.should == []
+
+ User.db.sqls
+ @user = User.new(:password => "anothertest")
+ @user.should be_valid
+ @user.errors.full_messages.should == []
+ User.db.sqls.should == []
end
it "should validate the uniqueness of multiple columns" do
@@ -907,6 +913,18 @@ class ::User < Sequel::Model
@user = User.new(:username => "0records", :password => "anothertest")
@user.should be_valid
@user.errors.full_messages.should == []
+
+ User.db.sqls
+ @user = User.new(:password => "anothertest")
+ @user.should be_valid
+ @user.errors.full_messages.should == []
+ @user = User.new(:username => "0records")
+ @user.should be_valid
+ @user.errors.full_messages.should == []
+ @user = User.new
+ @user.should be_valid
+ @user.errors.full_messages.should == []
+ User.db.sqls.should == []
end
it "should have a validates block that contains multiple validations" do
View
17 spec/extensions/validation_helpers_spec.rb
@@ -331,6 +331,11 @@ def validate
@user = @c.load(:id=>3, :username => "0records", :password => "anothertest")
@user.should be_valid
+ MODEL_DB.sqls
+ @user = @c.new(:password => "anothertest")
+ @user.should be_valid
+ MODEL_DB.sqls.should == []
+
@user = @c.new(:username => "1record", :password => "anothertest")
@user.should_not be_valid
@user.errors.full_messages.should == ['username is already taken']
@@ -369,6 +374,18 @@ def validate
@user = @c.load(:id=>3, :username => "0records", :password => "anothertest")
@user.should be_valid
+ MODEL_DB.sqls
+ @user = @c.new(:password => "anothertest")
+ @user.should be_valid
+ @user.errors.full_messages.should == []
+ @user = @c.new(:username => "0records")
+ @user.should be_valid
+ @user.errors.full_messages.should == []
+ @user = @c.new
+ @user.should be_valid
+ @user.errors.full_messages.should == []
+ MODEL_DB.sqls.should == []
+
@user = @c.new(:username => "1record", :password => "anothertest")
@user.should_not be_valid
@user.errors.full_messages.should == ['username and password is already taken']
Please sign in to comment.
Something went wrong with that request. Please try again.