提交 6efb3945 编写于 作者: S Sean Griffin

Use bind parameters for ranges in where clauses

This is a similar case to wanting ot use bind params for limit and
offset. Right now passing a range grows the amount of prepared
statements in an unbounded fashion. We could avoid using prepared
statements in that case, similar to what we do with arrays, but there's
a known number of variants for ranges.

This ends up duplicating some of the logic from Arel for how to handle
potentially infinite ranges, and that behavior may be removed from Arel
in the future.

Fixes #23074
上级 71d6826d
......@@ -18,6 +18,7 @@ def initialize(table)
register_handler(Class, ClassHandler.new(self))
register_handler(Base, BaseHandler.new(self))
register_handler(Range, RangeHandler.new(self))
register_handler(RangeHandler::RangeWithBinds, RangeHandler.new(self))
register_handler(Relation, RelationHandler.new)
register_handler(Array, ArrayHandler.new(self))
register_handler(AssociationQueryValue, AssociationQueryHandler.new(self))
......@@ -105,10 +106,23 @@ def create_binds_for_hash(attributes)
binds += bvs
when Relation
binds += value.bound_attributes
when Range
first = value.begin
last = value.end
unless first.respond_to?(:infinite?) && first.infinite?
binds << build_bind_param(column_name, first)
first = Arel::Nodes::BindParam.new
end
unless last.respond_to?(:infinite?) && last.infinite?
binds << build_bind_param(column_name, last)
last = Arel::Nodes::BindParam.new
end
result[column_name] = RangeHandler::RangeWithBinds.new(first, last, value.exclude_end?)
else
if can_be_bound?(column_name, value)
result[column_name] = Arel::Nodes::BindParam.new
binds << Relation::QueryAttribute.new(column_name.to_s, value, table.type(column_name))
binds << build_bind_param(column_name, value)
end
end
end
......@@ -145,5 +159,9 @@ def can_be_bound?(column_name, value)
handler_for(value).is_a?(BasicObjectHandler) &&
!table.associated_with?(column_name)
end
def build_bind_param(column_name, value)
Relation::QueryAttribute.new(column_name.to_s, value, table.type(column_name))
end
end
end
module ActiveRecord
class PredicateBuilder
class RangeHandler # :nodoc:
RangeWithBinds = Struct.new(:begin, :end, :exclude_end?)
def initialize(predicate_builder)
@predicate_builder = predicate_builder
end
def call(attribute, value)
attribute.between(value)
if value.begin.respond_to?(:infinite?) && value.begin.infinite?
if value.end.respond_to?(:infinite?) && value.end.infinite?
attribute.not_in([])
elsif value.exclude_end?
attribute.lt(value.end)
else
attribute.lteq(value.end)
end
elsif value.end.respond_to?(:infinite?) && value.end.infinite?
attribute.gteq(value.begin)
elsif value.exclude_end?
attribute.gteq(value.begin).and(attribute.lt(value.end))
else
attribute.between(value)
end
end
protected
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册