Expand the number of types which can use prepared statements

This will allow all types which require no additional handling to use
prepared statements. Specifically, this will allow for `true`, `false`,
`Date`, `Time`, and any custom PG type to use prepared statements. This
also revealed another source of nil columns in bind params, and an
inconsistency in their use.

The specific inconsistency comes from a nested query coming from a
through association, where one of the inversed associations is not
bi-directional.

The stop-gap is to simply construct the column at the site it is being
used. This should simply go away on its own once we use `Attribute` to
represent them instead, since we already have all of the information we
need.
This commit is contained in:
Sean Griffin 2015-01-24 21:07:55 -07:00
parent ae8cd56c7b
commit 3327cd3f61
3 changed files with 16 additions and 6 deletions

View File

@ -99,15 +99,17 @@ module ActiveRecord
attributes.each do |column_name, value| attributes.each do |column_name, value|
case value case value
when String, Integer, ActiveRecord::StatementCache::Substitute
result[column_name] = Arel::Nodes::BindParam.new
binds.push([table.column(column_name), value])
when Hash when Hash
attrs, bvs = associated_predicate_builder(column_name).create_binds_for_hash(value) attrs, bvs = associated_predicate_builder(column_name).create_binds_for_hash(value)
result[column_name] = attrs result[column_name] = attrs
binds += bvs binds += bvs
when Relation when Relation
binds += value.arel.bind_values + value.bind_values binds += value.arel.bind_values + value.bind_values
else
if can_be_bound?(column_name, value)
result[column_name] = Arel::Nodes::BindParam.new
binds.push([table.column(column_name), value])
end
end end
end end
@ -137,5 +139,11 @@ module ActiveRecord
def handler_for(object) def handler_for(object)
@handlers.detect { |klass, _| klass === object }.last @handlers.detect { |klass, _| klass === object }.last
end end
def can_be_bound?(column_name, value)
!value.nil? &&
handler_for(value).is_a?(BasicObjectHandler) &&
!table.associated_with?(column_name)
end
end end
end end

View File

@ -944,12 +944,11 @@ module ActiveRecord
[@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))] [@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
when Hash when Hash
opts = predicate_builder.resolve_column_aliases(opts) opts = predicate_builder.resolve_column_aliases(opts)
opts = @klass.send(:expand_hash_conditions_for_aggregates, opts)
tmp_opts, bind_values = predicate_builder.create_binds(opts) attributes, bind_values = predicate_builder.create_binds(opts)
self.bind_values += bind_values self.bind_values += bind_values
attributes = @klass.send(:expand_hash_conditions_for_aggregates, tmp_opts)
predicate_builder.build_from_hash(attributes) predicate_builder.build_from_hash(attributes)
else else
[opts] [opts]

View File

@ -25,6 +25,9 @@ module ActiveRecord
def column(column_name) def column(column_name)
if klass if klass
klass.columns_hash[column_name.to_s] klass.columns_hash[column_name.to_s]
else
# FIXME: We really shouldn't need to do this.
ConnectionAdapters::Column.new(column_name.to_s, nil, Type::Value.new)
end end
end end