diff options
Diffstat (limited to 'activerecord')
-rw-r--r-- | activerecord/CHANGELOG.md | 2 | ||||
-rw-r--r-- | activerecord/lib/active_record/relation.rb | 1 | ||||
-rw-r--r-- | activerecord/lib/active_record/relation/predicate_builder.rb | 28 | ||||
-rw-r--r-- | activerecord/lib/active_record/relation/query_methods.rb | 51 | ||||
-rw-r--r-- | activerecord/lib/active_record/table_metadata.rb | 6 | ||||
-rw-r--r-- | activerecord/test/cases/relation/where_chain_test.rb | 2 | ||||
-rw-r--r-- | activerecord/test/cases/relation/where_test.rb | 8 |
7 files changed, 44 insertions, 54 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index faa0aecebb..85b49d4c63 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -17,7 +17,7 @@ *Prathamesh Sonpatki* -* `time` columns can now affected by `time_zone_aware_attributes`. If you have +* `time` columns can now get affected by `time_zone_aware_attributes`. If you have set `config.time_zone` to a value other than `'UTC'`, they will be treated as in that time zone by default in Rails 5.1. If this is not the desired behavior, you can set diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index dd78814c6a..9c4db8a05e 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -33,7 +33,6 @@ module ActiveRecord # This method is a hot spot, so for now, use Hash[] to dup the hash. # https://bugs.ruby-lang.org/issues/7166 @values = Hash[@values] - @values[:bind] = @values[:bind].dup if @values.key? :bind reset end diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 567efce8ae..2860a30f99 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -28,6 +28,27 @@ module ActiveRecord expand_from_hash(attributes) end + def create_binds(attributes) + result = attributes.dup + binds = [] + + attributes.each do |column_name, 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 + attrs, bvs = associated_predicate_builder(column_name).create_binds(value) + result[column_name] = attrs + binds += bvs + when Relation + binds += value.arel.bind_values + value.bind_values + end + end + + [result, binds] + end + def expand(column, value) # Find the foreign key when using queries such as: # Post.where(author: author) @@ -80,8 +101,7 @@ module ActiveRecord attributes.flat_map do |key, value| if value.is_a?(Hash) - builder = self.class.new(table.associated_table(key)) - builder.expand_from_hash(value) + associated_predicate_builder(key).expand_from_hash(value) else expand(key, value) end @@ -90,6 +110,10 @@ module ActiveRecord private + def associated_predicate_builder(association_name) + self.class.new(table.associated_table(association_name)) + end + def convert_dot_notation_to_hash(attributes) dot_notation = attributes.keys.select { |s| s.include?(".") } diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index d6e6cb4d05..c34e4bfb9b 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -909,7 +909,7 @@ module ActiveRecord end end - bind_values.reject! { |col,_| col.name == target_value } + self.bind_values = bind_values.reject { |col,_| col.name == target_value } end def custom_join_ast(table, joins) @@ -945,11 +945,10 @@ module ActiveRecord when Hash opts = predicate_builder.resolve_column_aliases(opts) - tmp_opts, bind_values = create_binds(opts) + tmp_opts, bind_values = predicate_builder.create_binds(opts) self.bind_values += bind_values attributes = @klass.send(:expand_hash_conditions_for_aggregates, tmp_opts) - add_relations_to_bind_values(attributes) predicate_builder.build_from_hash(attributes) else @@ -957,40 +956,6 @@ module ActiveRecord end end - def create_binds(opts) - bindable, non_binds = opts.partition do |column, value| - case value - when String, Integer, ActiveRecord::StatementCache::Substitute - @klass.columns_hash.include? column.to_s - else - false - end - end - - association_binds, non_binds = non_binds.partition do |column, value| - value.is_a?(Hash) && association_for_table(column) - end - - new_opts = {} - binds = [] - - bindable.each do |(column,value)| - binds.push [@klass.columns_hash[column.to_s], value] - new_opts[column] = connection.substitute_at(column) - end - - association_binds.each do |(column, value)| - association_relation = association_for_table(column).klass.send(:relation) - association_new_opts, association_bind = association_relation.send(:create_binds, value) - new_opts[column] = association_new_opts - binds += association_bind - end - - non_binds.each { |column,value| new_opts[column] = value } - - [new_opts, binds] - end - def association_for_table(table_name) table_name = table_name.to_s @klass._reflect_on_association(table_name) || @@ -1153,17 +1118,5 @@ module ActiveRecord raise ArgumentError, "The method .#{method_name}() must contain arguments." end end - - def add_relations_to_bind_values(attributes) - if attributes.is_a?(Hash) - attributes.each_value do |value| - if value.is_a?(ActiveRecord::Relation) - self.bind_values += value.arel.bind_values + value.bind_values - else - add_relations_to_bind_values(value) - end - end - end - end end end diff --git a/activerecord/lib/active_record/table_metadata.rb b/activerecord/lib/active_record/table_metadata.rb index 11e33e8dfe..31a40adb67 100644 --- a/activerecord/lib/active_record/table_metadata.rb +++ b/activerecord/lib/active_record/table_metadata.rb @@ -22,6 +22,12 @@ module ActiveRecord arel_table[column_name] end + def column(column_name) + if klass + klass.columns_hash[column_name.to_s] + end + end + def associated_with?(association_name) klass && klass._reflect_on_association(association_name) end diff --git a/activerecord/test/cases/relation/where_chain_test.rb b/activerecord/test/cases/relation/where_chain_test.rb index 619055f1e7..0f9019bb1b 100644 --- a/activerecord/test/cases/relation/where_chain_test.rb +++ b/activerecord/test/cases/relation/where_chain_test.rb @@ -42,7 +42,7 @@ module ActiveRecord end def test_association_not_eq - expected = Comment.arel_table[@name].not_eq('hello') + expected = Comment.arel_table[@name].not_eq(Arel::Nodes::BindParam.new) relation = Post.joins(:comments).where.not(comments: {title: 'hello'}) assert_equal(expected.to_sql, relation.where_values.first.to_sql) end diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb index b0573579da..537937decd 100644 --- a/activerecord/test/cases/relation/where_test.rb +++ b/activerecord/test/cases/relation/where_test.rb @@ -28,6 +28,14 @@ module ActiveRecord } end + def test_where_copies_bind_params_in_the_right_order + author = authors(:david) + posts = author.posts.where.not(id: 1) + joined = Post.where(id: posts, title: posts.first.title) + + assert_equal joined, [posts.first] + end + def test_where_copies_arel_bind_params chef = Chef.create! CakeDesigner.create!(chef: chef) |