@@ -40,59 +40,39 @@ def visit_Arel_Nodes_UpdateStatement(o, collector)
4040
4141 # UPDATE with JOIN is in the form of:
4242 #
43- # UPDATE t1
43+ # UPDATE t1 AS __active_record_update_alias
4444 # SET ..
45- # FROM t2
46- # WHERE t1.join_id = t2.join_id
47- #
48- # Or if more than one join is present:
49- #
50- # UPDATE t1
51- # SET ..
52- # FROM t2
53- # JOIN t3 ON t2.join_id = t3.join_id
54- # WHERE t1.join_id = t2.join_id
45+ # FROM t1 JOIN t2 ON t2.join_id = t1.join_id ..
46+ # WHERE t1.id = __active_record_update_alias.id AND ..
5547 if has_join_sources? ( o )
56- visit o . relation . left , collector
48+ collector = visit o . relation . left , collector
5749 collect_nodes_for o . values , collector , " SET "
5850 collector << " FROM "
59- first_join , *remaining_joins = o . relation . right
60- from_items = remaining_joins . extract! do |join |
61- join . right . expr . right . relation == o . relation . left
62- end
63-
64- from_where = [ first_join . left ] + from_items . map ( &:left )
65- collect_nodes_for from_where , collector , " " , ", "
66-
67- if remaining_joins && !remaining_joins . empty?
68- collector << " "
69- remaining_joins . each do |join |
70- visit join , collector
71- collector << " "
72- end
73- end
74-
75- from_where = [ first_join . right . expr ] + from_items . map { |i | i . right . expr }
76- collect_nodes_for from_where + o . wheres , collector , " WHERE " , " AND "
51+ collector = inject_join o . relation . right , collector , " "
7752 else
7853 collector = visit o . relation , collector
7954 collect_nodes_for o . values , collector , " SET "
80- collect_nodes_for o . wheres , collector , " WHERE " , " AND "
8155 end
8256
57+ collect_nodes_for o . wheres , collector , " WHERE " , " AND "
8358 collect_nodes_for o . orders , collector , " ORDER BY "
8459 maybe_visit o . limit , collector
8560 end
8661
87- # Same as PostgreSQL and SQLite except we need to add limit if using subquery .
62+ # Similar to PostgreSQL and SQLite.
8863 def prepare_update_statement ( o )
89- if has_join_sources? ( o ) && !has_limit_or_offset_or_orders? ( o ) && !has_group_by_and_having? ( o ) &&
90- # The dialect isn't flexible enough to allow anything other than a inner join
91- # for the first join:
92- # UPDATE table SET .. FROM joined_table WHERE ...
93- ( o . relation . right . all? { |join | join . is_a? ( Arel ::Nodes ::InnerJoin ) || join . right . expr . right . relation != o . relation . left } )
94- o
64+ if o . key && has_join_sources? ( o ) && !has_group_by_and_having? ( o ) && !has_limit_or_offset_or_orders? ( o )
65+ # Join clauses cannot reference the target table, so alias the
66+ # updated table, place the entire relation in the FROM clause, and
67+ # add a self-join (which requires the primary key)
68+ stmt = o . clone
69+
70+ stmt . relation , stmt . wheres = o . relation . clone , o . wheres . clone
71+ stmt . relation . right = [ stmt . relation . left , *stmt . relation . right ]
72+ # Don't need to use alias
73+ stmt
9574 else
75+ # If using subquery, we need to add limit
9676 o . limit = Nodes ::Limit . new ( 9_223_372_036_854_775_807 ) if o . orders . any? && o . limit . nil?
9777
9878 super
0 commit comments