Skip to content
Browse files

Merge pull request #12508 from jetthoughts/12415_generate_subqueries_…

…for_relation_from_binding_params

Generate subquery for Relation passed as array condition for where

Conflicts:
	activerecord/CHANGELOG.md

Conflicts:
	activerecord/CHANGELOG.md
  • Loading branch information...
1 parent cfbd4a4 commit ad13e2254fe975aab9f83c5919733152b33c4e63 @rafaelfranca rafaelfranca committed Oct 13, 2013
View
17 activerecord/CHANGELOG.md
@@ -1,5 +1,22 @@
## unreleased ##
+* Generate subquery for `Relation` if it passed as array condition for `where` method
+
+ Example:
+ # Before
+ Blog.where('id in (?)', Blog.where(id: 1))
+ # => SELECT "blogs".* FROM "blogs" WHERE "blogs"."id" = 1
+ # => SELECT "blogs".* FROM "blogs" WHERE (id IN (1))
+
+ # After
+ Blog.where('id in (?)', Blog.where(id: 1).select(:id))
+ # => SELECT "blogs".* FROM "blogs"
+ # WHERE "blogs"."id" IN (SELECT "blogs"."id" FROM "blogs" WHERE "blogs"."id" = 1)
+
+ Fixes: #12415
+
+ *Paul Nikitochkin*
+
* For missed association exception message
which is raised in `ActiveRecord::Associations::Preloader` class
added owner record class name in order to simplify to find problem code.
View
14 activerecord/lib/active_record/sanitization.rb
@@ -126,15 +126,25 @@ def replace_bind_variables(statement, values) #:nodoc:
raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
bound = values.dup
c = connection
- statement.gsub('?') { quote_bound_value(bound.shift, c) }
+ statement.gsub('?') do
+ replace_bind_variable(bound.shift, c)
+ end
+ end
+
+ def replace_bind_variable(value, c = connection) #:nodoc:
+ if ActiveRecord::Relation === value
+ value.to_sql
+ else
+ quote_bound_value(value, c)
+ end
end
def replace_named_bind_variables(statement, bind_vars) #:nodoc:
statement.gsub(/(:?):([a-zA-Z]\w*)/) do
if $1 == ':' # skip postgresql casts
$& # return the whole match
elsif bind_vars.include?(match = $2.to_sym)
- quote_bound_value(bind_vars[match])
+ replace_bind_variable(bind_vars[match])
else
raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
end
View
5 activerecord/test/cases/relations_test.rb
@@ -616,6 +616,11 @@ def test_find_all_using_where_with_relation
relation = Author.where(:id => Author.where(:id => david.id))
assert_equal [david], relation.to_a
}
+
+ assert_queries(1) {
+ relation = Author.where('id in (?)', Author.where(id: david).select(:id))
+ assert_equal [david], relation.to_a
+ }
end
def test_find_all_using_where_with_relation_and_alternate_primary_key
View
6 activerecord/test/cases/sanitize_test.rb
@@ -32,4 +32,10 @@ def test_sanitize_sql_array_handles_bind_variables
assert_equal "name=#{quoted_bambi_and_thumper}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi\nand\nThumper"])
assert_equal "name=#{quoted_bambi_and_thumper}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi\nand\nThumper".mb_chars])
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")
+ end
end

0 comments on commit ad13e22

Please sign in to comment.
Something went wrong with that request. Please try again.