未验证 提交 9817d74f 编写于 作者: R Ryuta Kamizono 提交者: GitHub

Merge pull request #39022 from kamipo/perf_where_in

PERF: Improve performance of where when using an array of values
......@@ -20,20 +20,17 @@ def call(attribute, value)
case values.length
when 0 then NullPredicate
when 1 then predicate_builder.build(attribute, values.first)
else
values.map! do |v|
predicate_builder.build_bind_attribute(attribute.name, v)
end
values.empty? ? NullPredicate : attribute.in(values)
else attribute.in(values)
end
unless nils.empty?
if nils.empty?
return values_predicate if ranges.empty?
else
values_predicate = values_predicate.or(predicate_builder.build(attribute, nil))
end
array_predicates = ranges.map { |range| predicate_builder.build(attribute, range) }
array_predicates.unshift(values_predicate)
array_predicates.inject(&:or)
array_predicates = ranges.map! { |range| predicate_builder.build(attribute, range) }
array_predicates.inject(values_predicate, &:or)
end
private
......
......@@ -91,7 +91,7 @@ def contradiction?
predicates.any? do |x|
case x
when Arel::Nodes::In
Array === x.right && x.right.empty?
Arel::Nodes::CastedArray === x.right && x.right.value.empty?
when Arel::Nodes::Equality
x.right.respond_to?(:unboundable?) && x.right.unboundable?
end
......
......@@ -34,6 +34,16 @@ def eql?(other)
alias :== :eql?
end
class CastedArray < Casted # :nodoc:
def value_for_database
if attribute.able_to_type_cast?
value.map { |v| attribute.type_cast_for_database(v) }
else
value
end
end
end
class Quoted < Arel::Nodes::Unary # :nodoc:
alias :value_for_database :value
alias :value_before_type_cast :value
......
......@@ -31,7 +31,7 @@ def eq_any(others)
end
def eq_all(others)
grouping_all :eq, quoted_array(others)
grouping_all :eq, others
end
def between(other)
......@@ -238,7 +238,7 @@ def quoted_node(other)
end
def quoted_array(others)
others.map { |v| quoted_node(v) }
Nodes::CastedArray.new(others, self)
end
def infinity?(value)
......
......@@ -98,6 +98,7 @@ def eql?(other)
def type_cast_for_database(attribute_name, value)
type_caster.type_cast_for_database(attribute_name, value)
rescue ::RangeError
end
def able_to_type_cast?
......
......@@ -81,6 +81,10 @@ def visit_Arel_Nodes_Exists(o, collector)
end
end
def visit_Arel_Nodes_CastedArray(o, collector)
collector << o.value_for_database.map! { |v| quote(v) }.join(", ")
end
def visit_Arel_Nodes_Casted(o, collector)
collector << quote(o.value_for_database).to_s
end
......@@ -513,12 +517,8 @@ def visit_Arel_Nodes_In(o, collector)
collector.preparable = false
attr, values = o.left, o.right
if Array === values
unless values.empty?
values.delete_if { |value| unboundable?(value) }
end
return collector << "1=0" if values.empty?
if Arel::Nodes::CastedArray === values
return collector << "1=0" if values.value.empty?
end
visit(attr, collector) << " IN ("
......@@ -529,12 +529,8 @@ def visit_Arel_Nodes_NotIn(o, collector)
collector.preparable = false
attr, values = o.left, o.right
if Array === values
unless values.empty?
values.delete_if { |value| unboundable?(value) }
end
return collector << "1=1" if values.empty?
if Arel::Nodes::CastedArray === values
return collector << "1=1" if values.value.empty?
end
visit(attr, collector) << " NOT IN ("
......
......@@ -618,14 +618,14 @@ class AttributeTest < Arel::Spec
attribute = Attribute.new nil, nil
node = attribute.between(-::Float::INFINITY..::Float::INFINITY)
_(node).must_equal Nodes::NotIn.new(attribute, [])
_(node).must_equal attribute.not_in([])
end
it "can be constructed with a quoted infinite range" do
attribute = Attribute.new nil, nil
node = attribute.between(quoted_range(-::Float::INFINITY, ::Float::INFINITY, false))
_(node).must_equal Nodes::NotIn.new(attribute, [])
_(node).must_equal attribute.not_in([])
end
it "can be constructed with a range ending at Infinity" do
......@@ -707,11 +707,7 @@ class AttributeTest < Arel::Spec
_(node).must_equal Nodes::In.new(
attribute,
[
Nodes::Casted.new(1, attribute),
Nodes::Casted.new(2, attribute),
Nodes::Casted.new(3, attribute),
]
Nodes::CastedArray.new([1, 2, 3], attribute)
)
end
......@@ -831,14 +827,14 @@ class AttributeTest < Arel::Spec
attribute = Attribute.new nil, nil
node = attribute.not_between(-::Float::INFINITY..::Float::INFINITY)
_(node).must_equal Nodes::In.new(attribute, [])
_(node).must_equal attribute.in([])
end
it "can be constructed with a quoted infinite range" do
attribute = Attribute.new nil, nil
node = attribute.not_between(quoted_range(-::Float::INFINITY, ::Float::INFINITY, false))
_(node).must_equal Nodes::In.new(attribute, [])
_(node).must_equal attribute.in([])
end
it "can be constructed with a range ending at Infinity" do
......@@ -934,11 +930,7 @@ class AttributeTest < Arel::Spec
_(node).must_equal Nodes::NotIn.new(
attribute,
[
Nodes::Casted.new(1, attribute),
Nodes::Casted.new(2, attribute),
Nodes::Casted.new(3, attribute),
]
Nodes::CastedArray.new([1, 2, 3], attribute)
)
end
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册