Skip to content

Commit 501dd65

Browse files
committed
Allow to update duplicates on conflict in PostgreSQL
1 parent 861d5ed commit 501dd65

File tree

3 files changed

+21
-8
lines changed

3 files changed

+21
-8
lines changed

README.md

+11-4
Original file line numberDiff line numberDiff line change
@@ -149,22 +149,29 @@ Book.bulk_insert(*destination_columns, ignore: true) do |worker|
149149
end
150150
```
151151

152-
### Update Duplicates (MySQL)
152+
### Update Duplicates (MySQL, PostgreSQL)
153153

154154
If you don't want to ignore duplicate rows but instead want to update them
155155
then you can use the _update_duplicates_ option. Set this option to true
156-
and when a duplicate row is found the row will be updated with your new
157-
values. Default value for this option is false.
156+
(MySQL) or list unique column names (PostgreSQL) and when a duplicate row
157+
is found the row will be updated with your new values.
158+
Default value for this option is false.
158159

159160
```ruby
160161
destination_columns = [:title, :author]
161162

162-
# Update duplicate rows
163+
# Update duplicate rows (MySQL)
163164
Book.bulk_insert(*destination_columns, update_duplicates: true) do |worker|
164165
worker.add(...)
165166
worker.add(...)
166167
# ...
167168
end
169+
170+
# Update duplicate rows (PostgreSQL)
171+
Book.bulk_insert(*destination_columns, update_duplicates: %w[title]) do |worker|
172+
worker.add(...)
173+
# ...
174+
end
168175
```
169176

170177
### Return Primary Keys (PostgreSQL, PostGIS)

lib/bulk_insert/worker.rb

+7-1
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,14 @@ def primary_key_return_statement
150150
end
151151

152152
def on_conflict_statement
153-
if (adapter_name =~ /\APost(?:greSQL|GIS)/i && ignore )
153+
is_postgres = adapter_name =~ /\APost(?:greSQL|GIS)/i
154+
if is_postgres && ignore
154155
' ON CONFLICT DO NOTHING'
156+
elsif is_postgres && update_duplicates
157+
update_values = @columns.map do |column|
158+
"#{column.name}=EXCLUDED.#{column.name}"
159+
end.join(', ')
160+
' ON CONFLICT(' + update_duplicates.join(', ') + ') DO UPDATE SET ' + update_values
155161
elsif adapter_name =~ /^mysql/i && update_duplicates
156162
update_values = @columns.map do |column|
157163
"`#{column.name}`=VALUES(`#{column.name}`)"

test/bulk_insert/worker_test.rb

+3-3
Original file line numberDiff line numberDiff line change
@@ -332,14 +332,14 @@ class BulkInsertWorkerTest < ActiveSupport::TestCase
332332
'id',
333333
%w(greeting age happy created_at updated_at color),
334334
500, # batch size
335-
true, # ignore
336-
false, # update duplicates
335+
false, # ignore
336+
%w(greeting age happy), # update duplicates
337337
true # return primary keys
338338
)
339339
pgsql_worker.adapter_name = 'PostgreSQL'
340340
pgsql_worker.add ["Yo", 15, false, nil, nil]
341341

342-
assert_equal pgsql_worker.compose_insert_query, "INSERT INTO \"testings\" (\"greeting\",\"age\",\"happy\",\"created_at\",\"updated_at\",\"color\") VALUES ('Yo',15,0,NULL,NULL,'chartreuse') ON CONFLICT DO NOTHING RETURNING id"
342+
assert_equal pgsql_worker.compose_insert_query, "INSERT INTO \"testings\" (\"greeting\",\"age\",\"happy\",\"created_at\",\"updated_at\",\"color\") VALUES ('Yo',15,0,NULL,NULL,'chartreuse') ON CONFLICT(greeting, age, happy) DO UPDATE SET greeting=EXCLUDED.greeting, age=EXCLUDED.age, happy=EXCLUDED.happy, created_at=EXCLUDED.created_at, updated_at=EXCLUDED.updated_at, color=EXCLUDED.color RETURNING id"
343343
end
344344

345345
test "adapter dependent PostGIS methods" do

0 commit comments

Comments
 (0)