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

Merge pull request #26378 from kamipo/decouple_building_arel_ast_for_uniqueness_validator

Decouple the building Arel ASTs for uniqueness validator
This commit is contained in:
Jeremy Daer 2017-02-06 23:56:09 -07:00 committed by GitHub
commit f6d68408a0
4 changed files with 51 additions and 41 deletions

View file

@ -452,15 +452,15 @@ module ActiveRecord
@connection
end
def case_sensitive_comparison(table, attribute, column, value)
table[attribute].eq(Arel::Nodes::BindParam.new)
def case_sensitive_comparison(table, attribute, column, value) # :nodoc:
table[attribute].eq(value)
end
def case_insensitive_comparison(table, attribute, column, value)
def case_insensitive_comparison(table, attribute, column, value) # :nodoc:
if can_perform_case_insensitive_comparison_for?(column)
table[attribute].lower.eq(table.lower(Arel::Nodes::BindParam.new))
table[attribute].lower.eq(table.lower(value))
else
table[attribute].eq(Arel::Nodes::BindParam.new)
table[attribute].eq(value)
end
end

View file

@ -619,9 +619,9 @@ module ActiveRecord
SQL
end
def case_sensitive_comparison(table, attribute, column, value)
def case_sensitive_comparison(table, attribute, column, value) # :nodoc:
if column.collation && !column.case_sensitive?
table[attribute].eq(Arel::Nodes::Bin.new(Arel::Nodes::BindParam.new))
table[attribute].eq(Arel::Nodes::Bin.new(value))
else
super
end

View file

@ -15,9 +15,12 @@ module ActiveRecord
attributes = klass.send(:expand_hash_conditions_for_aggregates, attributes)
attributes.stringify_keys!
attributes, binds = predicate_builder.create_binds(attributes)
parts = predicate_builder.build_from_hash(attributes)
if perform_case_sensitive?(options = other.last)
parts, binds = build_for_case_sensitive(attributes, options)
else
attributes, binds = predicate_builder.create_binds(attributes)
parts = predicate_builder.build_from_hash(attributes)
end
when Arel::Nodes::Node
parts = [opts]
else
@ -32,6 +35,43 @@ module ActiveRecord
protected
attr_reader :klass, :predicate_builder
private
def perform_case_sensitive?(options)
options && options.key?(:case_sensitive)
end
def build_for_case_sensitive(attributes, options)
parts, binds = [], []
table = klass.arel_table
attributes.each do |attribute, value|
if reflection = klass._reflect_on_association(attribute)
attribute = reflection.foreign_key.to_s
value = value[reflection.klass.primary_key] unless value.nil?
end
if value.nil?
parts << table[attribute].eq(value)
else
column = klass.column_for_attribute(attribute)
binds << predicate_builder.send(:build_bind_param, attribute, value)
value = Arel::Nodes::BindParam.new
predicate = if options[:case_sensitive]
klass.connection.case_sensitive_comparison(table, attribute, column, value)
else
klass.connection.case_insensitive_comparison(table, attribute, column, value)
end
parts << predicate
end
end
[parts, binds]
end
end
end
end

View file

@ -50,37 +50,7 @@ module ActiveRecord
end
def build_relation(klass, attribute, value)
if reflection = klass._reflect_on_association(attribute)
attribute = reflection.foreign_key
value = value.attributes[reflection.klass.primary_key] unless value.nil?
end
if value.nil?
return klass.unscoped.where!(attribute => value)
end
# the attribute may be an aliased attribute
if klass.attribute_alias?(attribute)
attribute = klass.attribute_alias(attribute)
end
attribute_name = attribute.to_s
table = klass.arel_table
column = klass.columns_hash[attribute_name]
cast_type = klass.type_for_attribute(attribute_name)
comparison = if !options[:case_sensitive]
# will use SQL LOWER function before comparison, unless it detects a case insensitive collation
klass.connection.case_insensitive_comparison(table, attribute, column, value)
else
klass.connection.case_sensitive_comparison(table, attribute, column, value)
end
klass.unscoped.tap do |scope|
parts = [comparison]
binds = [Relation::QueryAttribute.new(attribute_name, value, cast_type)]
scope.where_clause += Relation::WhereClause.new(parts, binds)
end
klass.unscoped.where!({ attribute => value }, options)
end
def scope_relation(record, relation)