diff options
author | Rafael Mendonça França <rafaelmfranca@gmail.com> | 2013-10-21 19:13:04 -0200 |
---|---|---|
committer | Rafael Mendonça França <rafaelmfranca@gmail.com> | 2013-10-21 19:13:04 -0200 |
commit | b98c10c164ba6d17a84097f57ff529a95fec3935 (patch) | |
tree | b77a7d036f6c0b010d6eefe7baa3c85e5c480982 /activerecord | |
parent | a595fc1c56e5e9f4620bf5f248c87e9cb1e31b2f (diff) | |
parent | a2ed5d2381079716cf6224bc7b0538d318b264a2 (diff) | |
download | rails-b98c10c164ba6d17a84097f57ff529a95fec3935.tar.gz rails-b98c10c164ba6d17a84097f57ff529a95fec3935.tar.bz2 rails-b98c10c164ba6d17a84097f57ff529a95fec3935.zip |
Merge pull request #12588 from jetthoughts/12586_subquery_with_unprepared_sql
Inline bind values for sub-queries generated for Relation in where
Conflicts:
activerecord/CHANGELOG.md
Diffstat (limited to 'activerecord')
-rw-r--r-- | activerecord/CHANGELOG.md | 13 | ||||
-rw-r--r-- | activerecord/lib/active_record/relation/query_methods.rb | 7 | ||||
-rw-r--r-- | activerecord/test/cases/relations_test.rb | 25 | ||||
-rw-r--r-- | activerecord/test/cases/sanitize_test.rb | 17 |
4 files changed, 56 insertions, 6 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 97a42ca95b..adcec10fe8 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,12 @@ +* Sub-query generated for `Relation` passed as array condition did not take in account + bind values and have invalid syntax. + + Generate sub-query with inline bind values. + + Fixes: #12586 + + *Paul Nikitochkin* + * Fix a bug where rake db:structure:load crashed when the path contained spaces. @@ -8,9 +17,9 @@ Allows you to call `#unscope` on a relation with negative equality operators, i.e. `Arel::Nodes::NotIn` and `Arel::Nodes::NotEqual` that have been generated through the use of `where.not`. - + *Eric Hankins* - + * Raise an exception when model without primary key calls `.find_with_ids`. *Shimpei Makimoto* diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index a53f034345..d01f0384b4 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -894,6 +894,13 @@ module ActiveRecord def build_where(opts, other = []) case opts when String, Array + #TODO: Remove duplication with: /activerecord/lib/active_record/sanitization.rb:113 + values = Hash === other.first ? other.first.values : other + + values.grep(ActiveRecord::Relation) do |rel| + self.bind_values += rel.bind_values + end + [@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))] when Hash opts = PredicateBuilder.resolve_column_aliases(klass, opts) diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 860bd424b7..55501ea35b 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -639,6 +639,31 @@ class RelationTest < ActiveRecord::TestCase relation = Author.where('id in (?)', Author.where(id: david).select(:id)) assert_equal [david], relation.to_a } + + assert_queries(1) do + relation = Author.where('id in (:author_ids)', author_ids: Author.where(id: david).select(:id)) + assert_equal [david], relation.to_a + end + end + + def test_find_all_using_where_with_relation_with_bound_values + david = authors(:david) + davids_posts = david.posts.to_a + + assert_queries(1) do + relation = Post.where(id: david.posts.select(:id)) + assert_equal davids_posts, relation.to_a + end + + assert_queries(1) do + relation = Post.where('id in (?)', david.posts.select(:id)) + assert_equal davids_posts, relation.to_a, 'should process Relation as bind variables' + end + + assert_queries(1) do + relation = Post.where('id in (:post_ids)', post_ids: david.posts.select(:id)) + assert_equal davids_posts, relation.to_a, 'should process Relation as named bind variables' + end end def test_find_all_using_where_with_relation_and_alternate_primary_key diff --git a/activerecord/test/cases/sanitize_test.rb b/activerecord/test/cases/sanitize_test.rb index 4c0762deca..766b2ff2ef 100644 --- a/activerecord/test/cases/sanitize_test.rb +++ b/activerecord/test/cases/sanitize_test.rb @@ -1,5 +1,7 @@ require "cases/helper" require 'models/binary' +require 'models/author' +require 'models/post' class SanitizeTest < ActiveRecord::TestCase def setup @@ -9,7 +11,7 @@ class SanitizeTest < ActiveRecord::TestCase quoted_bambi = ActiveRecord::Base.connection.quote("Bambi") quoted_column_name = ActiveRecord::Base.connection.quote_column_name("name") quoted_table_name = ActiveRecord::Base.connection.quote_table_name("adorable_animals") - expected_value = "#{quoted_table_name}.#{quoted_column_name} = #{quoted_bambi}" + expected_value = "#{quoted_table_name}.#{quoted_column_name} = #{quoted_bambi}" assert_equal expected_value, Binary.send(:sanitize_sql_hash, {adorable_animals: {name: 'Bambi'}}) end @@ -33,8 +35,15 @@ class SanitizeTest < ActiveRecord::TestCase end def test_sanitize_sql_array_handles_relations - assert_match(/\(\bselect\b.*?\bwhere\b.*?\)/i, - Binary.send(:sanitize_sql_array, ["id in (?)", Binary.where(id: 1)]), - "should sanitize `Relation` as subquery") + david = Author.create!(name: 'David') + david_posts = david.posts.select(:id) + + sub_query_pattern = /\(\bselect\b.*?\bwhere\b.*?\)/i + + select_author_sql = Post.send(:sanitize_sql_array, ['id in (?)', david_posts]) + assert_match(sub_query_pattern, select_author_sql, 'should sanitize `Relation` as subquery for bind variables') + + select_author_sql = Post.send(:sanitize_sql_array, ['id in (:post_ids)', post_ids: david_posts]) + assert_match(sub_query_pattern, select_author_sql, 'should sanitize `Relation` as subquery for named bind variables') end end |