@@ -29,16 +29,77 @@ def visit_Arel_Nodes_Concat(o, collector)
2929 visit o . right , collector
3030 end
3131
32+ # def visit_Arel_Nodes_UpdateStatement(o, collector)
33+ # if has_join_and_composite_primary_key?(o)
34+ # update_statement_using_join(o, collector)
35+ # else
36+ # o.limit = Nodes::Limit.new(9_223_372_036_854_775_807) if o.orders.any? && o.limit.nil?
37+ #
38+ # super
39+ # end
40+ # end
41+
3242 def visit_Arel_Nodes_UpdateStatement ( o , collector )
33- if has_join_and_composite_primary_key? ( o )
34- update_statement_using_join ( o , collector )
43+ collector . retryable = false
44+ o = prepare_update_statement ( o )
45+
46+ collector << "UPDATE "
47+
48+ # UPDATE with JOIN is in the form of:
49+ #
50+ # UPDATE t1
51+ # SET ..
52+ # FROM t2
53+ # WHERE t1.join_id = t2.join_id
54+ #
55+ # Or if more than one join is present:
56+ #
57+ # UPDATE t1
58+ # SET ..
59+ # FROM t2
60+ # JOIN t3 ON t2.join_id = t3.join_id
61+ # WHERE t1.join_id = t2.join_id
62+ if has_join_sources? ( o )
63+ visit o . relation . left , collector
64+ collect_nodes_for o . values , collector , " SET "
65+ collector << " FROM "
66+ first_join , *remaining_joins = o . relation . right
67+ visit first_join . left , collector
68+
69+ if remaining_joins && !remaining_joins . empty?
70+ collector << " "
71+ remaining_joins . each do |join |
72+ visit join , collector
73+ end
74+ end
75+
76+ collect_nodes_for [ first_join . right . expr ] + o . wheres , collector , " WHERE " , " AND "
77+ else
78+ collector = visit o . relation , collector
79+ collect_nodes_for o . values , collector , " SET "
80+ collect_nodes_for o . wheres , collector , " WHERE " , " AND "
81+ end
82+
83+ collect_nodes_for o . orders , collector , " ORDER BY "
84+ maybe_visit o . limit , collector
85+ end
86+
87+ # In the simple case, PostgreSQL allows us to place FROM or JOINs directly into the UPDATE
88+ # query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
89+ # these, we must use a subquery.
90+ def prepare_update_statement ( o )
91+
92+
93+ if has_join_sources? ( o ) && !has_limit_or_offset_or_orders? ( o ) && !has_group_by_and_having? ( o )
94+ o
3595 else
3696 o . limit = Nodes ::Limit . new ( 9_223_372_036_854_775_807 ) if o . orders . any? && o . limit . nil?
3797
3898 super
3999 end
40100 end
41101
102+
42103 def visit_Arel_Nodes_DeleteStatement ( o , collector )
43104 if has_join_and_composite_primary_key? ( o )
44105 delete_statement_using_join ( o , collector )
@@ -61,16 +122,16 @@ def delete_statement_using_join(o, collector)
61122 collect_nodes_for o . wheres , collector , " WHERE " , " AND "
62123 end
63124
64- def update_statement_using_join ( o , collector )
65- collector . retryable = false
66-
67- collector << "UPDATE "
68- visit o . relation . left , collector
69- collect_nodes_for o . values , collector , " SET "
70- collector << " FROM "
71- visit o . relation , collector
72- collect_nodes_for o . wheres , collector , " WHERE " , " AND "
73- end
125+ # def update_statement_using_join(o, collector)
126+ # collector.retryable = false
127+ #
128+ # collector << "UPDATE "
129+ # visit o.relation.left, collector
130+ # collect_nodes_for o.values, collector, " SET "
131+ # collector << " FROM "
132+ # visit o.relation, collector
133+ # collect_nodes_for o.wheres, collector, " WHERE ", " AND "
134+ # end
74135
75136 def visit_Arel_Nodes_Lock ( o , collector )
76137 o . expr = Arel . sql ( "WITH(UPDLOCK)" ) if o . expr . to_s =~ /FOR UPDATE/
0 commit comments