aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNat Budin <nbudin@patientslikeme.com>2014-05-12 14:30:05 -0700
committerNat Budin <nbudin@patientslikeme.com>2014-05-14 16:18:42 -0700
commit1d316ac1fd68962782762b02694a1bf9fd4ef44e (patch)
treea96fd0f01687caf5e8b3e7a9b45fb740663512f5
parenteacb4264af7593a41487625d5a7a6f6986c58b47 (diff)
downloadrails-1d316ac1fd68962782762b02694a1bf9fd4ef44e.tar.gz
rails-1d316ac1fd68962782762b02694a1bf9fd4ef44e.tar.bz2
rails-1d316ac1fd68962782762b02694a1bf9fd4ef44e.zip
Make filter_binds filter out symbols that are equal to strings
ActiveRecord::Relation::Merger's filter_binds method does not filter out bind variables when one of the attribute nodes has a string name, but the other has a symbol name, even when those names are actually equal. This can result in there being more bind variables than placeholders in the generated SQL. This is particularly an issue for PostgreSQL, where this is treated as an error. This patch changes the filter_binds method to make it convert both attribute names to strings before comparing.
-rw-r--r--activerecord/CHANGELOG.md9
-rw-r--r--activerecord/lib/active_record/relation/merger.rb2
-rw-r--r--activerecord/test/cases/relation/merging_test.rb5
-rw-r--r--activerecord/test/models/comment.rb8
-rw-r--r--activerecord/test/models/post.rb9
5 files changed, 32 insertions, 1 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index f6cd5efb45..6c927dd353 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,3 +1,12 @@
+* `ActiveRecord::Relation::Merger#filter_binds` now compares equivalent symbols and
+ strings in column names as equal.
+
+ This fixes a rare case in which more bind values are passed than there are
+ placeholders for them in the generated SQL statement, which can make PostgreSQL
+ throw a `StatementInvalid` exception.
+
+ *Nat Budin*
+
* `change_column_default` allows `[]` as argument to `change_column_default`.
Fixes #11586.
diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb
index fcb28a18f6..ac41d0aa80 100644
--- a/activerecord/lib/active_record/relation/merger.rb
+++ b/activerecord/lib/active_record/relation/merger.rb
@@ -156,7 +156,7 @@ module ActiveRecord
def filter_binds(lhs_binds, removed_wheres)
return lhs_binds if removed_wheres.empty?
- set = Set.new removed_wheres.map { |x| x.left.name }
+ set = Set.new removed_wheres.map { |x| x.left.name.to_s }
lhs_binds.dup.delete_if { |col,_| set.include? col.name }
end
diff --git a/activerecord/test/cases/relation/merging_test.rb b/activerecord/test/cases/relation/merging_test.rb
index 48f45d45b1..7c9543e5ae 100644
--- a/activerecord/test/cases/relation/merging_test.rb
+++ b/activerecord/test/cases/relation/merging_test.rb
@@ -108,6 +108,11 @@ class RelationMergingTest < ActiveRecord::TestCase
merged = left.merge(right)
assert_equal post, merged.first
end
+
+ def test_merging_compares_symbols_and_strings_as_equal
+ post = PostThatLoadsCommentsInAnAfterSaveHook.create!(title: "First Post", body: "Blah blah blah.")
+ assert_equal "First comment!", post.comments.where(body: "First comment!").first_or_create.body
+ end
end
class MergingDifferentRelationsTest < ActiveRecord::TestCase
diff --git a/activerecord/test/models/comment.rb b/activerecord/test/models/comment.rb
index f82df417ce..d44befbfd2 100644
--- a/activerecord/test/models/comment.rb
+++ b/activerecord/test/models/comment.rb
@@ -40,3 +40,11 @@ end
class VerySpecialComment < Comment
end
+
+class CommentThatAutomaticallyAltersPostBody < Comment
+ belongs_to :post, class_name: "PostThatLoadsCommentsInAnAfterSaveHook", foreign_key: :post_id
+
+ after_save do |comment|
+ comment.post.update_attributes(body: "Automatically altered")
+ end
+end \ No newline at end of file
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index b1e56c14d1..4ee67a4bb3 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -208,3 +208,12 @@ class SpecialPostWithDefaultScope < ActiveRecord::Base
self.table_name = 'posts'
default_scope { where(:id => [1, 5,6]) }
end
+
+class PostThatLoadsCommentsInAnAfterSaveHook < ActiveRecord::Base
+ self.table_name = 'posts'
+ has_many :comments, class_name: "CommentThatAutomaticallyAltersPostBody", foreign_key: :post_id
+
+ after_save do |post|
+ post.comments.load
+ end
+end