Skip to content

Commit f7eaab9

Browse files
tarmoNZKoz
authored andcommitted
validates_uniqueness_of uses database case sensitivity support instead of using ruby
Signed-off-by: Michael Koziarski <michael@koziarski.com>
1 parent f3da46e commit f7eaab9

File tree

3 files changed

+23
-27
lines changed

3 files changed

+23
-27
lines changed

activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,10 @@ def empty_insert_statement(table_name)
149149
"INSERT INTO #{quote_table_name(table_name)} VALUES(DEFAULT)"
150150
end
151151

152+
def case_sensitive_equality_operator
153+
"="
154+
end
155+
152156
protected
153157
# Returns an array of record hashes with the column names as keys and
154158
# column values as values.

activerecord/lib/active_record/connection_adapters/mysql_adapter.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,10 @@ def pk_and_sequence_for(table) #:nodoc:
511511
keys.length == 1 ? [keys.first, nil] : nil
512512
end
513513

514+
def case_sensitive_equality_operator
515+
"= BINARY"
516+
end
517+
514518
private
515519
def connect
516520
@connection.reconnect = true if @connection.respond_to?(:reconnect=)

activerecord/lib/active_record/validations.rb

Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -627,17 +627,23 @@ def validates_uniqueness_of(*attr_names)
627627

628628
is_text_column = finder_class.columns_hash[attr_name.to_s].text?
629629

630-
if !value.nil? && is_text_column
631-
value = value.to_s
630+
if value.nil?
631+
comparison_operator = "IS ?"
632+
else
633+
comparison_operator = "#{connection.case_sensitive_equality_operator} ?"
634+
635+
if is_text_column
636+
value = value.to_s
637+
end
632638
end
633639

640+
sql_attribute = "#{record.class.quoted_table_name}.#{connection.quote_column_name(attr_name)}"
641+
634642
if value.nil? || (configuration[:case_sensitive] || !is_text_column)
635-
condition_sql = "#{record.class.quoted_table_name}.#{attr_name} #{attribute_condition(value)}"
643+
condition_sql = "#{sql_attribute} #{comparison_operator}"
636644
condition_params = [value]
637645
else
638-
# sqlite has case sensitive SELECT query, while MySQL/Postgresql don't.
639-
# Hence, this is needed only for sqlite.
640-
condition_sql = "LOWER(#{record.class.quoted_table_name}.#{attr_name}) #{attribute_condition(value)}"
646+
condition_sql = "LOWER(#{sql_attribute}) #{comparison_operator}"
641647
condition_params = [value.downcase]
642648
end
643649

@@ -654,28 +660,10 @@ def validates_uniqueness_of(*attr_names)
654660
condition_params << record.send(:id)
655661
end
656662

657-
results = finder_class.with_exclusive_scope do
658-
connection.select_all(
659-
construct_finder_sql(
660-
:select => "#{connection.quote_column_name(attr_name)}",
661-
:from => "#{finder_class.quoted_table_name}",
662-
:conditions => [condition_sql, *condition_params]
663-
)
664-
)
665-
end
666-
667-
unless results.length.zero?
668-
found = true
669-
670-
# As MySQL/Postgres don't have case sensitive SELECT queries, we try to find duplicate
671-
# column in ruby when case sensitive option
672-
if configuration[:case_sensitive] && finder_class.columns_hash[attr_name.to_s].text?
673-
found = results.any? { |a| a[attr_name.to_s] == value.to_s }
674-
end
675-
676-
if found
663+
finder_class.with_exclusive_scope do
664+
if finder_class.exists?([condition_sql, *condition_params])
677665
message = record.errors.generate_message(attr_name, :taken, :default => configuration[:message])
678-
record.errors.add(attr_name, message)
666+
record.errors.add(attr_name, message)
679667
end
680668
end
681669
end

0 commit comments

Comments
 (0)