aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAaron Patterson <aaron.patterson@gmail.com>2013-05-21 11:01:25 -0700
committerAaron Patterson <aaron.patterson@gmail.com>2013-05-21 11:01:25 -0700
commitac70ec64138765ddd2a7c5e0130243e6df98cf2d (patch)
tree3cc3eb3ee81e314fbd60dcabafda0aa40850b382
parentf47b4236e089b07cb683ee9b7ff8b06111a0ec10 (diff)
parentf3ebbeae6eb647767ccd49e25821b1ba33923596 (diff)
downloadrails-ac70ec64138765ddd2a7c5e0130243e6df98cf2d.tar.gz
rails-ac70ec64138765ddd2a7c5e0130243e6df98cf2d.tar.bz2
rails-ac70ec64138765ddd2a7c5e0130243e6df98cf2d.zip
Merge branch 'bindwhere'
* bindwhere: avoid creating a set if no where values are removed remove bind values for where clauses that were removed push partitioning up so bind elimination can get the removed wheres push partion logic down and initialization logic up partition the where values so we can access the removed ones
-rw-r--r--activerecord/lib/active_record/relation/merger.rb40
-rw-r--r--activerecord/test/cases/relations_test.rb22
2 files changed, 42 insertions, 20 deletions
diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb
index 98afeb8cc5..c114ea0c0d 100644
--- a/activerecord/lib/active_record/relation/merger.rb
+++ b/activerecord/lib/active_record/relation/merger.rb
@@ -99,8 +99,15 @@ module ActiveRecord
end
def merge_multi_values
- relation.where_values = merged_wheres
- relation.bind_values = merged_binds
+ lhs_wheres = relation.where_values
+ rhs_wheres = values[:where] || []
+ lhs_binds = relation.bind_values
+ rhs_binds = values[:bind] || []
+
+ removed, kept = partition_overwrites(lhs_wheres, rhs_wheres)
+
+ relation.where_values = kept + rhs_wheres
+ relation.bind_values = filter_binds(lhs_binds, removed) + rhs_binds
if values[:reordering]
# override any order specified in the original relation
@@ -123,34 +130,27 @@ module ActiveRecord
end
end
- def merged_binds
- if values[:bind]
- (relation.bind_values + values[:bind]).uniq(&:first)
- else
- relation.bind_values
- end
- end
-
- def merged_wheres
- rhs_wheres = values[:where] || []
- lhs_wheres = relation.where_values
+ def filter_binds(lhs_binds, removed_wheres)
+ return lhs_binds if removed_wheres.empty?
- if rhs_wheres.empty? || lhs_wheres.empty?
- lhs_wheres + rhs_wheres
- else
- reject_overwrites(lhs_wheres, rhs_wheres) + rhs_wheres
- end
+ set = Set.new removed_wheres.map { |x| x.left.name }
+ lhs_binds.dup.delete_if { |col,_| set.include? col.name }
end
# Remove equalities from the existing relation with a LHS which is
# present in the relation being merged in.
- def reject_overwrites(lhs_wheres, rhs_wheres)
+ # returns [things_to_remove, things_to_keep]
+ def partition_overwrites(lhs_wheres, rhs_wheres)
+ if lhs_wheres.empty? || rhs_wheres.empty?
+ return [[], lhs_wheres]
+ end
+
nodes = rhs_wheres.find_all do |w|
w.respond_to?(:operator) && w.operator == :==
end
seen = Set.new(nodes) { |node| node.left }
- lhs_wheres.reject do |w|
+ lhs_wheres.partition do |w|
w.respond_to?(:operator) && w.operator == :== && seen.include?(w.left)
end
end
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index cf6af4e8f4..b64ff13d29 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -1546,4 +1546,26 @@ class RelationTest < ActiveRecord::TestCase
assert merged.to_sql.include?("wtf")
assert merged.to_sql.include?("bbq")
end
+
+ def test_merging_removes_rhs_bind_parameters
+ left = Post.where(id: Arel::Nodes::BindParam.new('?'))
+ column = Post.columns_hash['id']
+ left.bind_values += [[column, 20]]
+ right = Post.where(id: 10)
+
+ merged = left.merge(right)
+ assert_equal [], merged.bind_values
+ end
+
+ def test_merging_keeps_lhs_bind_parameters
+ column = Post.columns_hash['id']
+ binds = [[column, 20]]
+
+ right = Post.where(id: Arel::Nodes::BindParam.new('?'))
+ right.bind_values += binds
+ left = Post.where(id: 10)
+
+ merged = left.merge(right)
+ assert_equal binds, merged.bind_values
+ end
end