Skip to content

Commit 216b37b

Browse files
committed
Only generate an UPDATE/FROM if the first join is an INNER JOIN
Fix: rails#54518 Followup: rails#53950 SQLite3 and PostgreSQL dialects aren't flexible enough to allow more than a simple join. ```sql UPDATE "main_table" SET ... FROM "joined_table" WHERE joined_table.foreign_key = main_table.primary_key ``` So if the first join is anything other than a regular INNER JOIN we have no choice but to degrade to the subquery scheme.
1 parent 9aadd2f commit 216b37b

File tree

3 files changed

+17
-2
lines changed

3 files changed

+17
-2
lines changed

activerecord/lib/arel/visitors/postgresql.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,11 @@ def visit_Arel_Nodes_UpdateStatement(o, collector)
5454
# query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
5555
# these, we must use a subquery.
5656
def prepare_update_statement(o)
57-
if has_join_sources?(o) && !has_limit_or_offset_or_orders?(o) && !has_group_by_and_having?(o)
57+
if has_join_sources?(o) && !has_limit_or_offset_or_orders?(o) && !has_group_by_and_having?(o) &&
58+
# The PostgreSQL dialect isn't flexible enough to allow anything other than a inner join
59+
# for the first join:
60+
# UPDATE table SET .. FROM joined_table WHERE ...
61+
(o.relation.right.first.is_a?(Arel::Nodes::InnerJoin))
5862
o
5963
else
6064
super

activerecord/lib/arel/visitors/sqlite.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,11 @@ def visit_Arel_Nodes_UpdateStatement(o, collector)
5454
def prepare_update_statement(o)
5555
# Sqlite need to be built with the SQLITE_ENABLE_UPDATE_DELETE_LIMIT compile-time option
5656
# to support LIMIT/OFFSET/ORDER in UPDATE and DELETE statements.
57-
if has_join_sources?(o) && !has_limit_or_offset_or_orders?(o) && !has_group_by_and_having?(o)
57+
if has_join_sources?(o) && !has_limit_or_offset_or_orders?(o) && !has_group_by_and_having?(o) &&
58+
# The SQLite3 dialect isn't flexible enough to allow anything other than a inner join
59+
# for the first join:
60+
# UPDATE table SET .. FROM joined_table WHERE ...
61+
(o.relation.right.first.is_a?(Arel::Nodes::InnerJoin))
5862
o
5963
else
6064
super

activerecord/test/cases/relation/update_all_test.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,13 @@ def test_update_all_with_left_joins
125125
assert_equal pets.count, pets.update_all(name: "Bob")
126126
end
127127

128+
def test_update_all_with_left_outer_joins
129+
pets = Pet.left_outer_joins(:toys)
130+
131+
assert_equal true, pets.exists?
132+
assert_equal pets.count, pets.update_all(name: "Boby")
133+
end
134+
128135
def test_update_all_with_includes
129136
pets = Pet.includes(:toys).where(toys: { name: "Bone" })
130137

0 commit comments

Comments
 (0)