@@ -29,16 +29,77 @@ def visit_Arel_Nodes_Concat(o, collector)
29
29
visit o . right , collector
30
30
end
31
31
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
+
32
42
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
35
95
else
36
96
o . limit = Nodes ::Limit . new ( 9_223_372_036_854_775_807 ) if o . orders . any? && o . limit . nil?
37
97
38
98
super
39
99
end
40
100
end
41
101
102
+
42
103
def visit_Arel_Nodes_DeleteStatement ( o , collector )
43
104
if has_join_and_composite_primary_key? ( o )
44
105
delete_statement_using_join ( o , collector )
@@ -61,16 +122,16 @@ def delete_statement_using_join(o, collector)
61
122
collect_nodes_for o . wheres , collector , " WHERE " , " AND "
62
123
end
63
124
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
74
135
75
136
def visit_Arel_Nodes_Lock ( o , collector )
76
137
o . expr = Arel . sql ( "WITH(UPDLOCK)" ) if o . expr . to_s =~ /FOR UPDATE/
0 commit comments