From 110e0e1fcceab68716e0c75d87baffb14403b288 Mon Sep 17 00:00:00 2001 From: Maxime Lapointe Date: Tue, 25 Jul 2017 14:00:39 -0400 Subject: Avoid duplicate clauses when using #or Condenses the clauses that are common to both sides of the OR and put them outside, before the OR This fix the current behavior where the number of conditions is exponential based on the number of times #or is used. --- .../test/cases/relation/where_clause_test.rb | 33 ++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'activerecord/test/cases') diff --git a/activerecord/test/cases/relation/where_clause_test.rb b/activerecord/test/cases/relation/where_clause_test.rb index f3a81f3c70..42cf35ac9c 100644 --- a/activerecord/test/cases/relation/where_clause_test.rb +++ b/activerecord/test/cases/relation/where_clause_test.rb @@ -181,6 +181,39 @@ class ActiveRecord::Relation assert_equal WhereClause.empty, WhereClause.empty.or(where_clause) end + test "or places common conditions before the OR" do + wcs = (0..6).map do |i| + WhereClause.new([table["col_#{i}"].eq(bind_param(i))]) + end + + actual = wcs[0] + expected = wcs[0] + + actual = (actual + wcs[1]).or(actual + wcs[2]) + expected = expected + wcs[1].or(wcs[2]) + + actual = (actual + wcs[3] + wcs[4]).or(actual + wcs[5] + wcs[6]) + expected = expected + (wcs[3] + wcs[4]).or(wcs[5] + wcs[6]) + + assert_equal expected, actual + end + + test "or will use common conditions only if one side only has common conditions" do + common = WhereClause.new( + [table["id"].eq(bind_param(1)), "foo = bar"] + ) + + extra = WhereClause.new([table["extra"].eq(bind_param("pluto"))]) + + actual_extra_only_on_left = (common + extra).or(common) + actual_extra_only_on_right = (common).or(common + extra) + + expected = common + + assert_equal expected, actual_extra_only_on_left + assert_equal expected, actual_extra_only_on_right + end + private def table -- cgit v1.2.3 From be81b5066074fee8126144d072c6132b93d1fe39 Mon Sep 17 00:00:00 2001 From: Maxime Lapointe Date: Fri, 28 Jul 2017 17:53:50 -0400 Subject: Edits following the reviews --- .../test/cases/relation/where_clause_test.rb | 56 ++++++++++++++-------- 1 file changed, 35 insertions(+), 21 deletions(-) (limited to 'activerecord/test/cases') diff --git a/activerecord/test/cases/relation/where_clause_test.rb b/activerecord/test/cases/relation/where_clause_test.rb index 42cf35ac9c..e5eb159d36 100644 --- a/activerecord/test/cases/relation/where_clause_test.rb +++ b/activerecord/test/cases/relation/where_clause_test.rb @@ -182,36 +182,50 @@ class ActiveRecord::Relation end test "or places common conditions before the OR" do - wcs = (0..6).map do |i| - WhereClause.new([table["col_#{i}"].eq(bind_param(i))]) - end - - actual = wcs[0] - expected = wcs[0] + a = WhereClause.new( + [table["id"].eq(bind_param(1)), table["name"].eq(bind_param("Sean"))], + ) + b = WhereClause.new( + [table["id"].eq(bind_param(1)), table["hair_color"].eq(bind_param("black"))], + ) - actual = (actual + wcs[1]).or(actual + wcs[2]) - expected = expected + wcs[1].or(wcs[2]) + common = WhereClause.new( + [table["id"].eq(bind_param(1))], + ) - actual = (actual + wcs[3] + wcs[4]).or(actual + wcs[5] + wcs[6]) - expected = expected + (wcs[3] + wcs[4]).or(wcs[5] + wcs[6]) + or_clause = WhereClause.new([table["name"].eq(bind_param("Sean"))]) + .or(WhereClause.new([table["hair_color"].eq(bind_param("black"))])) - assert_equal expected, actual + assert_equal common + or_clause, a.or(b) end - test "or will use common conditions only if one side only has common conditions" do - common = WhereClause.new( - [table["id"].eq(bind_param(1)), "foo = bar"] - ) + test "or can detect identical or as being a common condition" do + common_or = WhereClause.new([table["name"].eq(bind_param("Sean"))]) + .or(WhereClause.new([table["hair_color"].eq(bind_param("black"))])) - extra = WhereClause.new([table["extra"].eq(bind_param("pluto"))]) + a = common_or + WhereClause.new([table["id"].eq(bind_param(1))]) + b = common_or + WhereClause.new([table["foo"].eq(bind_param("bar"))]) - actual_extra_only_on_left = (common + extra).or(common) - actual_extra_only_on_right = (common).or(common + extra) + new_or = WhereClause.new([table["id"].eq(bind_param(1))]) + .or(WhereClause.new([table["foo"].eq(bind_param("bar"))])) - expected = common + assert_equal common_or + new_or, a.or(b) + end + + test "or will use only common conditions if one side only has common conditions" do + only_common = WhereClause.new([ + table["id"].eq(bind_param(1)), + "foo = bar", + ]) + + common_with_extra = WhereClause.new([ + table["id"].eq(bind_param(1)), + "foo = bar", + table["extra"].eq(bind_param("pluto")), + ]) - assert_equal expected, actual_extra_only_on_left - assert_equal expected, actual_extra_only_on_right + assert_equal only_common, only_common.or(common_with_extra) + assert_equal only_common, common_with_extra.or(only_common) end private -- cgit v1.2.3