mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Fallback to unprepared statement only when bind params limit is exceeded
This is a follow up and/or an alternative of #33844. Unlike #33844, this would attempt to construct unprepared statement only when bind params limit (mysql2 65535, pg 65535, sqlite3 249999) is exceeded. I only defined 65535 as the limit, not defined 249999 for sqlite3, since it is an edge case, I'm not excited to add less worth extra code.
This commit is contained in:
parent
1830383833
commit
b571c4f3f2
5 changed files with 19 additions and 7 deletions
|
@ -71,6 +71,11 @@ module ActiveRecord
|
|||
256
|
||||
end
|
||||
deprecate :joins_per_query
|
||||
|
||||
private
|
||||
def bind_params_length
|
||||
65535
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -46,11 +46,16 @@ module ActiveRecord
|
|||
def select_all(arel, name = nil, binds = [], preparable: nil)
|
||||
arel = arel_from_relation(arel)
|
||||
sql, binds = to_sql_and_binds(arel, binds)
|
||||
|
||||
if !prepared_statements || (arel.is_a?(String) && preparable.nil?)
|
||||
preparable = false
|
||||
elsif binds.length > bind_params_length
|
||||
sql, binds = unprepared_statement { to_sql_and_binds(arel) }
|
||||
preparable = false
|
||||
else
|
||||
preparable = visitor.preparable
|
||||
end
|
||||
|
||||
if prepared_statements && preparable
|
||||
select_prepared(sql, name, binds)
|
||||
else
|
||||
|
|
|
@ -57,14 +57,10 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def build_bind_attribute(column_name, value)
|
||||
attr = build_query_attribute(column_name, value)
|
||||
attr = Relation::QueryAttribute.new(column_name.to_s, value, table.type(column_name))
|
||||
Arel::Nodes::BindParam.new(attr)
|
||||
end
|
||||
|
||||
def build_query_attribute(column_name, value)
|
||||
Relation::QueryAttribute.new(column_name.to_s, value, table.type(column_name))
|
||||
end
|
||||
|
||||
protected
|
||||
def expand_from_hash(attributes)
|
||||
return ["1=0"] if attributes.empty?
|
||||
|
|
|
@ -22,8 +22,8 @@ module ActiveRecord
|
|||
when 1 then predicate_builder.build(attribute, values.first)
|
||||
else
|
||||
values.map! do |v|
|
||||
attr = predicate_builder.build_query_attribute(attribute.name, v)
|
||||
attr.value_for_database if attr.boundable?
|
||||
bind = predicate_builder.build_bind_attribute(attribute.name, v)
|
||||
bind if bind.value.boundable?
|
||||
end.compact!
|
||||
values.empty? ? NullPredicate : attribute.in(values)
|
||||
end
|
||||
|
|
|
@ -34,6 +34,12 @@ if ActiveRecord::Base.connection.prepared_statements
|
|||
ActiveSupport::Notifications.unsubscribe(@subscription)
|
||||
end
|
||||
|
||||
def test_too_many_binds
|
||||
bind_params_length = @connection.send(:bind_params_length)
|
||||
topics = Topic.where(id: (1 .. bind_params_length + 1).to_a)
|
||||
assert_equal Topic.count, topics.count
|
||||
end
|
||||
|
||||
def test_bind_from_join_in_subquery
|
||||
subquery = Author.joins(:thinking_posts).where(name: "David")
|
||||
scope = Author.from(subquery, "authors").where(id: 1)
|
||||
|
|
Loading…
Reference in a new issue