1
0
Fork 0
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:
Ryuta Kamizono 2018-09-14 06:57:20 +09:00
parent 1830383833
commit b571c4f3f2
5 changed files with 19 additions and 7 deletions

View file

@ -71,6 +71,11 @@ module ActiveRecord
256
end
deprecate :joins_per_query
private
def bind_params_length
65535
end
end
end
end

View file

@ -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

View file

@ -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?

View file

@ -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

View file

@ -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)