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

Decouple the building Arel ASTs for uniqueness validator

Currently uniqueness validator is coupled with building Arel ASTs.
This commit extracts `WhereClauseFactory#build_for_case_sensitive` for
decouple the building Arel ASTs.
This commit is contained in:
Ryuta Kamizono 2016-09-03 15:38:31 +09:00
parent e8ba0c0f21
commit 41bc3edcaa
4 changed files with 48 additions and 38 deletions

View file

@ -442,14 +442,14 @@ module ActiveRecord
end
def case_sensitive_comparison(table, attribute, column, value)
table[attribute].eq(Arel::Nodes::BindParam.new)
table[attribute].eq(value)
end
def case_insensitive_comparison(table, attribute, column, value)
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

@ -615,7 +615,7 @@ module ActiveRecord
def case_sensitive_comparison(table, attribute, column, value)
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) # :doc: