1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Merge pull request #29950 from MaxLap/avoid_or_clause_duplicates

Avoid duplicate clauses when using #or
This commit is contained in:
Sean Griffin 2017-07-31 09:21:43 -04:00 committed by GitHub
commit 0f245882c9
3 changed files with 66 additions and 6 deletions

View file

@ -1,3 +1,7 @@
* When using #or, extract the common conditions and put them before the OR condition.
*Maxime Handfield Lapointe*
* `Relation#or` now accepts two relations who have different values for
`references` only, as `references` can be implicitly called by `where`.

View file

@ -15,6 +15,12 @@ module ActiveRecord
)
end
def -(other)
WhereClause.new(
predicates - other.predicates,
)
end
def merge(other)
WhereClause.new(
predicates_unreferenced_by(other) + other.predicates,
@ -26,14 +32,17 @@ module ActiveRecord
end
def or(other)
if empty?
self
elsif other.empty?
other
left = self - other
common = self - left
right = other - common
if left.empty? || right.empty?
common
else
WhereClause.new(
[ast.or(other.ast)],
or_clause = WhereClause.new(
[left.ast.or(right.ast)],
)
common + or_clause
end
end

View file

@ -181,6 +181,53 @@ class ActiveRecord::Relation
assert_equal WhereClause.empty, WhereClause.empty.or(where_clause)
end
test "or places common conditions before the OR" do
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"))],
)
common = WhereClause.new(
[table["id"].eq(bind_param(1))],
)
or_clause = WhereClause.new([table["name"].eq(bind_param("Sean"))])
.or(WhereClause.new([table["hair_color"].eq(bind_param("black"))]))
assert_equal common + or_clause, a.or(b)
end
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"))]))
a = common_or + WhereClause.new([table["id"].eq(bind_param(1))])
b = common_or + WhereClause.new([table["foo"].eq(bind_param("bar"))])
new_or = WhereClause.new([table["id"].eq(bind_param(1))])
.or(WhereClause.new([table["foo"].eq(bind_param("bar"))]))
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 only_common, only_common.or(common_with_extra)
assert_equal only_common, common_with_extra.or(only_common)
end
private
def table