1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Merge pull request #23523 from kamipo/avoid_truncation_in_uniqueness_validation

Avoid a string value truncation in uniqueness validation
This commit is contained in:
Jeremy Daer 2016-04-18 17:34:40 -07:00
commit f2f2d64429
No known key found for this signature in database
GPG key ID: AB8F6399D5C60664
3 changed files with 49 additions and 13 deletions

View file

@ -1,3 +1,20 @@
* SQLite: Fix uniqueness validation when values exceed the column limit.
SQLite doesn't impose length restrictions on strings, BLOBs, or numeric
values. It treats them as helpful metadata. When we truncate strings
before checking uniqueness, we'd miss values that exceed the column limit.
Other databases enforce length limits. A large value will pass uniqueness
validation since the column limit guarantees no value that long exists.
When we insert the row, it'll raise `ActiveRecord::ValueTooLong` as we
expect.
This fixes edge-case incorrect validation failures for values that exceed
the column limit but are identical to an existing value *when truncated*.
Now these will pass validation and raise an exception.
*Ryuta Kamizono*
* Raise `ActiveRecord::ValueTooLong` when column limits are exceeded.
Supported by MySQL and PostgreSQL adapters.

View file

@ -67,9 +67,6 @@ module ActiveRecord
cast_type = klass.type_for_attribute(attribute_name)
value = cast_type.serialize(value)
value = klass.connection.type_cast(value)
if value.is_a?(String) && column.limit
value = value.to_s[0, column.limit]
end
comparison = if !options[:case_sensitive] && !value.nil?
# will use SQL LOWER function before comparison, unless it detects a case insensitive collation

View file

@ -349,19 +349,41 @@ class UniquenessValidationTest < ActiveRecord::TestCase
end
def test_validate_uniqueness_with_limit
# Event.title is limited to 5 characters
e1 = Event.create(:title => "abcde")
assert e1.valid?, "Could not create an event with a unique, 5 character title"
e2 = Event.create(:title => "abcdefgh")
assert !e2.valid?, "Created an event whose title, with limit taken into account, is not unique"
if current_adapter?(:SQLite3Adapter)
# Event.title has limit 5, but SQLite doesn't truncate.
e1 = Event.create(title: "abcdefgh")
assert e1.valid?, "Could not create an event with a unique 8 characters title"
e2 = Event.create(title: "abcdefgh")
assert_not e2.valid?, "Created an event whose title is not unique"
elsif current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter, :SQLServerAdapter)
assert_raise(ActiveRecord::ValueTooLong) do
Event.create(title: "abcdefgh")
end
else
assert_raise(ActiveRecord::StatementInvalid) do
Event.create(title: "abcdefgh")
end
end
end
def test_validate_uniqueness_with_limit_and_utf8
# Event.title is limited to 5 characters
e1 = Event.create(:title => "一二三四五")
assert e1.valid?, "Could not create an event with a unique, 5 character title"
e2 = Event.create(:title => "一二三四五六七八")
assert !e2.valid?, "Created an event whose title, with limit taken into account, is not unique"
if current_adapter?(:SQLite3Adapter)
# Event.title has limit 5, but does SQLite doesn't truncate.
e1 = Event.create(title: "一二三四五六七八")
assert e1.valid?, "Could not create an event with a unique 8 characters title"
e2 = Event.create(title: "一二三四五六七八")
assert_not e2.valid?, "Created an event whose title is not unique"
elsif current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter, :SQLServerAdapter)
assert_raise(ActiveRecord::ValueTooLong) do
Event.create(title: "一二三四五六七八")
end
else
assert_raise(ActiveRecord::StatementInvalid) do
Event.create(title: "一二三四五六七八")
end
end
end
def test_validate_straight_inheritance_uniqueness