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:
commit
f2f2d64429
3 changed files with 49 additions and 13 deletions
|
@ -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.
|
* Raise `ActiveRecord::ValueTooLong` when column limits are exceeded.
|
||||||
Supported by MySQL and PostgreSQL adapters.
|
Supported by MySQL and PostgreSQL adapters.
|
||||||
|
|
||||||
|
|
|
@ -67,9 +67,6 @@ module ActiveRecord
|
||||||
cast_type = klass.type_for_attribute(attribute_name)
|
cast_type = klass.type_for_attribute(attribute_name)
|
||||||
value = cast_type.serialize(value)
|
value = cast_type.serialize(value)
|
||||||
value = klass.connection.type_cast(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?
|
comparison = if !options[:case_sensitive] && !value.nil?
|
||||||
# will use SQL LOWER function before comparison, unless it detects a case insensitive collation
|
# will use SQL LOWER function before comparison, unless it detects a case insensitive collation
|
||||||
|
|
|
@ -349,19 +349,41 @@ class UniquenessValidationTest < ActiveRecord::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_validate_uniqueness_with_limit
|
def test_validate_uniqueness_with_limit
|
||||||
# Event.title is limited to 5 characters
|
if current_adapter?(:SQLite3Adapter)
|
||||||
e1 = Event.create(:title => "abcde")
|
# Event.title has limit 5, but SQLite doesn't truncate.
|
||||||
assert e1.valid?, "Could not create an event with a unique, 5 character title"
|
e1 = Event.create(title: "abcdefgh")
|
||||||
e2 = Event.create(:title => "abcdefgh")
|
assert e1.valid?, "Could not create an event with a unique 8 characters title"
|
||||||
assert !e2.valid?, "Created an event whose title, with limit taken into account, is not unique"
|
|
||||||
|
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
|
end
|
||||||
|
|
||||||
def test_validate_uniqueness_with_limit_and_utf8
|
def test_validate_uniqueness_with_limit_and_utf8
|
||||||
# Event.title is limited to 5 characters
|
if current_adapter?(:SQLite3Adapter)
|
||||||
e1 = Event.create(:title => "一二三四五")
|
# Event.title has limit 5, but does SQLite doesn't truncate.
|
||||||
assert e1.valid?, "Could not create an event with a unique, 5 character title"
|
e1 = Event.create(title: "一二三四五六七八")
|
||||||
e2 = Event.create(:title => "一二三四五六七八")
|
assert e1.valid?, "Could not create an event with a unique 8 characters title"
|
||||||
assert !e2.valid?, "Created an event whose title, with limit taken into account, is not unique"
|
|
||||||
|
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
|
end
|
||||||
|
|
||||||
def test_validate_straight_inheritance_uniqueness
|
def test_validate_straight_inheritance_uniqueness
|
||||||
|
|
Loading…
Reference in a new issue