activerecord-import 2.1.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +39 -0
  3. data/README.markdown +9 -9
  4. data/lib/activerecord-import/active_record/adapters/mysql2_proxy_adapter.rb +8 -0
  5. data/lib/activerecord-import/active_record/adapters/postgresql_proxy_adapter.rb +8 -0
  6. data/lib/activerecord-import/active_record/adapters/sqlite3_proxy_adapter.rb +8 -0
  7. data/lib/activerecord-import/active_record/adapters/trilogy_proxy_adapter.rb +8 -0
  8. data/lib/activerecord-import/adapters/active_record_proxy_adapter.rb +19 -0
  9. data/lib/activerecord-import/adapters/mysql2_proxy_adapter.rb +9 -0
  10. data/lib/activerecord-import/adapters/mysql_adapter.rb +10 -3
  11. data/lib/activerecord-import/adapters/postgresql_adapter.rb +1 -1
  12. data/lib/activerecord-import/adapters/postgresql_proxy_adapter.rb +9 -0
  13. data/lib/activerecord-import/adapters/sqlite3_adapter.rb +1 -1
  14. data/lib/activerecord-import/adapters/sqlite3_proxy_adapter.rb +9 -0
  15. data/lib/activerecord-import/adapters/trilogy_proxy_adapter.rb +9 -0
  16. data/lib/activerecord-import/base.rb +2 -2
  17. data/lib/activerecord-import/import.rb +243 -211
  18. data/lib/activerecord-import/version.rb +1 -1
  19. metadata +13 -200
  20. data/.github/workflows/test.yaml +0 -161
  21. data/.gitignore +0 -37
  22. data/.rubocop.yml +0 -118
  23. data/.rubocop_todo.yml +0 -30
  24. data/Brewfile +0 -5
  25. data/Dockerfile +0 -23
  26. data/Gemfile +0 -63
  27. data/Rakefile +0 -73
  28. data/activerecord-import.gemspec +0 -28
  29. data/benchmarks/README +0 -32
  30. data/benchmarks/benchmark.rb +0 -72
  31. data/benchmarks/lib/base.rb +0 -143
  32. data/benchmarks/lib/cli_parser.rb +0 -111
  33. data/benchmarks/lib/float.rb +0 -17
  34. data/benchmarks/lib/mysql2_benchmark.rb +0 -21
  35. data/benchmarks/lib/output_to_csv.rb +0 -21
  36. data/benchmarks/lib/output_to_html.rb +0 -66
  37. data/benchmarks/models/test_innodb.rb +0 -5
  38. data/benchmarks/models/test_memory.rb +0 -5
  39. data/benchmarks/models/test_myisam.rb +0 -5
  40. data/benchmarks/schema/mysql2_schema.rb +0 -18
  41. data/docker-compose.yml +0 -34
  42. data/gemfiles/5.2.gemfile +0 -4
  43. data/gemfiles/6.0.gemfile +0 -4
  44. data/gemfiles/6.1.gemfile +0 -4
  45. data/gemfiles/7.0.gemfile +0 -4
  46. data/gemfiles/7.1.gemfile +0 -3
  47. data/gemfiles/7.2.gemfile +0 -3
  48. data/gemfiles/8.0.gemfile +0 -3
  49. data/test/adapters/jdbcmysql.rb +0 -3
  50. data/test/adapters/jdbcpostgresql.rb +0 -3
  51. data/test/adapters/jdbcsqlite3.rb +0 -3
  52. data/test/adapters/makara_postgis.rb +0 -3
  53. data/test/adapters/mysql2.rb +0 -3
  54. data/test/adapters/mysql2_makara.rb +0 -3
  55. data/test/adapters/mysql2_proxy.rb +0 -3
  56. data/test/adapters/mysql2spatial.rb +0 -3
  57. data/test/adapters/postgis.rb +0 -3
  58. data/test/adapters/postgresql.rb +0 -3
  59. data/test/adapters/postgresql_makara.rb +0 -3
  60. data/test/adapters/postgresql_proxy.rb +0 -3
  61. data/test/adapters/seamless_database_pool.rb +0 -3
  62. data/test/adapters/spatialite.rb +0 -3
  63. data/test/adapters/sqlite3.rb +0 -3
  64. data/test/adapters/trilogy.rb +0 -11
  65. data/test/database.yml.sample +0 -65
  66. data/test/github/database.yml +0 -78
  67. data/test/import_test.rb +0 -970
  68. data/test/jdbcmysql/import_test.rb +0 -7
  69. data/test/jdbcpostgresql/import_test.rb +0 -6
  70. data/test/jdbcsqlite3/import_test.rb +0 -6
  71. data/test/makara_postgis/import_test.rb +0 -10
  72. data/test/models/account.rb +0 -5
  73. data/test/models/alarm.rb +0 -4
  74. data/test/models/animal.rb +0 -8
  75. data/test/models/author.rb +0 -9
  76. data/test/models/bike_maker.rb +0 -10
  77. data/test/models/book.rb +0 -18
  78. data/test/models/car.rb +0 -5
  79. data/test/models/card.rb +0 -5
  80. data/test/models/chapter.rb +0 -6
  81. data/test/models/composite_book.rb +0 -19
  82. data/test/models/composite_chapter.rb +0 -12
  83. data/test/models/customer.rb +0 -18
  84. data/test/models/deck.rb +0 -8
  85. data/test/models/dictionary.rb +0 -6
  86. data/test/models/discount.rb +0 -5
  87. data/test/models/end_note.rb +0 -6
  88. data/test/models/group.rb +0 -5
  89. data/test/models/order.rb +0 -17
  90. data/test/models/playing_card.rb +0 -4
  91. data/test/models/promotion.rb +0 -5
  92. data/test/models/question.rb +0 -5
  93. data/test/models/rule.rb +0 -5
  94. data/test/models/tag.rb +0 -12
  95. data/test/models/tag_alias.rb +0 -11
  96. data/test/models/topic.rb +0 -31
  97. data/test/models/user.rb +0 -5
  98. data/test/models/user_token.rb +0 -6
  99. data/test/models/vendor.rb +0 -9
  100. data/test/models/widget.rb +0 -33
  101. data/test/mysql2/import_test.rb +0 -7
  102. data/test/mysql2_makara/import_test.rb +0 -8
  103. data/test/mysql2_proxy/import_test.rb +0 -6
  104. data/test/mysqlspatial2/import_test.rb +0 -8
  105. data/test/postgis/import_test.rb +0 -10
  106. data/test/postgresql/import_test.rb +0 -6
  107. data/test/postgresql_proxy/import_test.rb +0 -6
  108. data/test/schema/generic_schema.rb +0 -230
  109. data/test/schema/jdbcpostgresql_schema.rb +0 -3
  110. data/test/schema/mysql2_schema.rb +0 -21
  111. data/test/schema/postgis_schema.rb +0 -3
  112. data/test/schema/postgresql_schema.rb +0 -94
  113. data/test/schema/sqlite3_schema.rb +0 -15
  114. data/test/schema/version.rb +0 -12
  115. data/test/sqlite3/import_test.rb +0 -6
  116. data/test/support/active_support/test_case_extensions.rb +0 -73
  117. data/test/support/assertions.rb +0 -75
  118. data/test/support/factories.rb +0 -66
  119. data/test/support/generate.rb +0 -31
  120. data/test/support/mysql/import_examples.rb +0 -97
  121. data/test/support/postgresql/import_examples.rb +0 -642
  122. data/test/support/shared_examples/on_duplicate_key_ignore.rb +0 -45
  123. data/test/support/shared_examples/on_duplicate_key_update.rb +0 -437
  124. data/test/support/shared_examples/recursive_import.rb +0 -352
  125. data/test/support/sqlite3/import_examples.rb +0 -232
  126. data/test/synchronize_test.rb +0 -43
  127. data/test/test_helper.rb +0 -91
  128. data/test/trilogy/import_test.rb +0 -7
  129. data/test/value_sets_bytes_parser_test.rb +0 -106
  130. data/test/value_sets_records_parser_test.rb +0 -34
@@ -1,642 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- def should_support_postgresql_import_functionality
4
- should_support_recursive_import
5
-
6
- if ActiveRecord::Base.connection.supports_on_duplicate_key_update?
7
- should_support_postgresql_upsert_functionality
8
- end
9
-
10
- describe "#supports_imports?" do
11
- it "should support import" do
12
- assert ActiveRecord::Base.supports_import?
13
- end
14
- end
15
-
16
- describe "#import" do
17
- it "should import with a single insert" do
18
- # see ActiveRecord::ConnectionAdapters::AbstractAdapter test for more specifics
19
- assert_difference "Topic.count", +10 do
20
- result = Topic.import Build(3, :topics)
21
- assert_equal 1, result.num_inserts
22
-
23
- result = Topic.import Build(7, :topics)
24
- assert_equal 1, result.num_inserts
25
- end
26
- end
27
-
28
- context "setting attributes and marking clean" do
29
- let(:topic) { Build(:topics) }
30
-
31
- setup { Topic.import([topic]) }
32
-
33
- it "assigns ids" do
34
- assert topic.id.present?
35
- end
36
-
37
- it "marks models as clean" do
38
- assert !topic.changed?
39
- end
40
-
41
- it "moves the dirty changes to previous_changes" do
42
- assert topic.previous_changes.present?
43
- end
44
-
45
- it "marks models as persisted" do
46
- assert !topic.new_record?
47
- assert topic.persisted?
48
- end
49
-
50
- it "assigns timestamps" do
51
- assert topic.created_at.present?
52
- assert topic.updated_at.present?
53
- end
54
- end
55
-
56
- describe "with query cache enabled" do
57
- setup do
58
- unless ActiveRecord::Base.connection.query_cache_enabled
59
- ActiveRecord::Base.connection.enable_query_cache!
60
- @disable_cache_on_teardown = true
61
- end
62
- end
63
-
64
- it "clears cache on insert" do
65
- before_import = Topic.all.to_a
66
-
67
- Topic.import(Build(2, :topics), validate: false)
68
-
69
- after_import = Topic.all.to_a
70
- assert_equal 2, after_import.size - before_import.size
71
- end
72
-
73
- teardown do
74
- if @disable_cache_on_teardown
75
- ActiveRecord::Base.connection.disable_query_cache!
76
- end
77
- end
78
- end
79
-
80
- describe "no_returning" do
81
- let(:books) { [Book.new(author_name: "foo", title: "bar")] }
82
-
83
- it "creates records" do
84
- assert_difference "Book.count", +1 do
85
- Book.import books, no_returning: true
86
- end
87
- end
88
-
89
- it "returns no ids" do
90
- assert_equal [], Book.import(books, no_returning: true).ids
91
- end
92
- end
93
-
94
- describe "returning" do
95
- let(:books) { [Book.new(author_name: "King", title: "It")] }
96
- let(:result) { Book.import(books, returning: %w(author_name title)) }
97
- let(:book_id) { books.first.id }
98
- let(:true_returning_value) { true }
99
- let(:false_returning_value) { false }
100
-
101
- it "creates records" do
102
- assert_difference("Book.count", +1) { result }
103
- end
104
-
105
- it "returns ids" do
106
- result
107
- assert_equal [book_id], result.ids
108
- end
109
-
110
- it "returns specified columns" do
111
- assert_equal [%w(King It)], result.results
112
- end
113
-
114
- context "when given an empty array" do
115
- let(:result) { Book.import([], returning: %w(title)) }
116
-
117
- setup { result }
118
-
119
- it "returns empty arrays for ids and results" do
120
- assert_equal [], result.ids
121
- assert_equal [], result.results
122
- end
123
- end
124
-
125
- context "when a returning column is a serialized attribute" do
126
- let(:vendor) { Vendor.new(hours: { monday: '8-5' }) }
127
- let(:result) { Vendor.import([vendor], returning: %w(hours)) }
128
-
129
- it "creates records" do
130
- assert_difference("Vendor.count", +1) { result }
131
- end
132
- end
133
-
134
- context "when primary key and returning overlap" do
135
- let(:result) { Book.import(books, returning: %w(id title)) }
136
-
137
- setup { result }
138
-
139
- it "returns ids" do
140
- assert_equal [book_id], result.ids
141
- end
142
-
143
- it "returns specified columns" do
144
- assert_equal [[book_id, 'It']], result.results
145
- end
146
- end
147
-
148
- context "when returning is raw sql" do
149
- let(:result) { Book.import(books, returning: "title, (xmax = '0') AS inserted") }
150
-
151
- setup { result }
152
-
153
- it "returns ids" do
154
- assert_equal [book_id], result.ids
155
- end
156
-
157
- it "returns specified columns" do
158
- assert_equal [['It', true_returning_value]], result.results
159
- end
160
- end
161
-
162
- context "when returning contains raw sql" do
163
- let(:result) { Book.import(books, returning: [:title, "id, (xmax = '0') AS inserted"]) }
164
-
165
- setup { result }
166
-
167
- it "returns ids" do
168
- assert_equal [book_id], result.ids
169
- end
170
-
171
- it "returns specified columns" do
172
- assert_equal [['It', book_id, true_returning_value]], result.results
173
- end
174
- end
175
-
176
- context "setting model attributes" do
177
- let(:code) { 'abc' }
178
- let(:discount) { 0.10 }
179
- let(:original_promotion) do
180
- Promotion.new(code: code, discount: discount)
181
- end
182
- let(:updated_promotion) do
183
- Promotion.new(code: code, description: 'ABC discount')
184
- end
185
- let(:returning_columns) { %w(discount) }
186
-
187
- setup do
188
- Promotion.import([original_promotion])
189
- Promotion.import([updated_promotion],
190
- on_duplicate_key_update: { conflict_target: %i(code), columns: %i(description) },
191
- returning: returning_columns)
192
- end
193
-
194
- it "sets model attributes" do
195
- assert_equal updated_promotion.discount, discount
196
- end
197
-
198
- context "returning multiple columns" do
199
- let(:returning_columns) { %w(discount description) }
200
-
201
- it "sets model attributes" do
202
- assert_equal updated_promotion.discount, discount
203
- end
204
- end
205
-
206
- context 'returning raw sql' do
207
- let(:returning_columns) { [:discount, "(xmax = '0') AS inserted"] }
208
-
209
- it "sets custom model attributes" do
210
- assert_equal updated_promotion.inserted, false_returning_value
211
- end
212
- end
213
- end
214
- end
215
- end
216
-
217
- describe "with a uuid primary key" do
218
- let(:vendor) { Vendor.new(name: "foo") }
219
- let(:vendors) { [vendor] }
220
-
221
- it "creates records" do
222
- assert_difference "Vendor.count", +1 do
223
- Vendor.import vendors
224
- end
225
- end
226
-
227
- it "assigns an id to the model objects" do
228
- Vendor.import vendors
229
- assert_not_nil vendor.id
230
- end
231
-
232
- describe "with an assigned uuid primary key" do
233
- let(:id) { SecureRandom.uuid }
234
- let(:vendor) { Vendor.new(id: id, name: "foo") }
235
- let(:vendors) { [vendor] }
236
-
237
- it "creates records with correct id" do
238
- assert_difference "Vendor.count", +1 do
239
- Vendor.import vendors
240
- end
241
- assert_equal id, vendor.id
242
- end
243
- end
244
- end
245
-
246
- describe "with store accessor fields" do
247
- it "imports values for json fields" do
248
- vendors = [Vendor.new(name: 'Vendor 1', size: 100)]
249
- assert_difference "Vendor.count", +1 do
250
- Vendor.import vendors
251
- end
252
- assert_equal(100, Vendor.first.size)
253
- end
254
-
255
- it "imports values for hstore fields" do
256
- vendors = [Vendor.new(name: 'Vendor 1', contact: 'John Smith')]
257
- assert_difference "Vendor.count", +1 do
258
- Vendor.import vendors
259
- end
260
- assert_equal('John Smith', Vendor.first.contact)
261
- end
262
-
263
- it "imports values for jsonb fields" do
264
- vendors = [Vendor.new(name: 'Vendor 1', charge_code: '12345')]
265
- assert_difference "Vendor.count", +1 do
266
- Vendor.import vendors
267
- end
268
- assert_equal('12345', Vendor.first.charge_code)
269
- end
270
- end
271
-
272
- describe "with serializable fields" do
273
- it "imports default values as correct data type" do
274
- vendors = [Vendor.new(name: 'Vendor 1')]
275
- assert_difference "Vendor.count", +1 do
276
- Vendor.import vendors
277
- end
278
- assert_equal({}, Vendor.first.json_data)
279
- end
280
-
281
- %w(json jsonb).each do |json_type|
282
- describe "with pure #{json_type} fields" do
283
- let(:data) { { a: :b } }
284
- let(:json_field_name) { "pure_#{json_type}_data" }
285
- it "imports the values from saved records" do
286
- vendor = Vendor.create!(name: 'Vendor 1', json_field_name => data)
287
-
288
- Vendor.import [vendor], on_duplicate_key_update: [json_field_name]
289
- assert_equal(data.as_json, vendor.reload[json_field_name])
290
- end
291
- end
292
- end
293
- end
294
-
295
- describe "with enum field" do
296
- let(:vendor_type) { "retailer" }
297
- it "imports the correct values for enum fields" do
298
- vendor = Vendor.new(name: 'Vendor 1', vendor_type: vendor_type)
299
- assert_difference "Vendor.count", +1 do
300
- Vendor.import [vendor]
301
- end
302
- assert_equal(vendor_type, Vendor.first.vendor_type)
303
- end
304
- end
305
-
306
- describe "with binary field" do
307
- let(:binary_value) { "\xE0'c\xB2\xB0\xB3Bh\\\xC2M\xB1m\\I\xC4r".dup.force_encoding('ASCII-8BIT') }
308
- it "imports the correct values for binary fields" do
309
- alarms = [Alarm.new(device_id: 1, alarm_type: 1, status: 1, secret_key: binary_value)]
310
- assert_difference "Alarm.count", +1 do
311
- Alarm.import alarms
312
- end
313
- assert_equal(binary_value, Alarm.first.secret_key)
314
- end
315
- end
316
-
317
- unless ENV["SKIP_COMPOSITE_PK"]
318
- describe "with composite foreign keys" do
319
- let(:account_id) { 555 }
320
- let(:customer) { Customer.new(account_id: account_id, name: "foo") }
321
- let(:order) { Order.new(account_id: account_id, amount: 100, customer: customer) }
322
-
323
- it "imports and correctly maps foreign keys" do
324
- assert_difference "Customer.count", +1 do
325
- Customer.import [customer]
326
- end
327
-
328
- assert_difference "Order.count", +1 do
329
- Order.import [order]
330
- end
331
-
332
- db_customer = Customer.last
333
- db_order = Order.last
334
-
335
- assert_equal db_customer.orders.last, db_order
336
- assert_not_equal db_order.customer_id, nil
337
- end
338
-
339
- it "should import models with auto-incrementing ID successfully" do
340
- author = Author.create!(name: "Foo Barson")
341
-
342
- books = []
343
- 2.times do |i|
344
- books << CompositeBook.new(author_id: author.id, title: "book #{i}")
345
- end
346
- assert_difference "CompositeBook.count", +2 do
347
- CompositeBook.import books
348
- end
349
- end
350
- end
351
- end
352
- end
353
-
354
- def should_support_postgresql_upsert_functionality
355
- should_support_basic_on_duplicate_key_update
356
- should_support_on_duplicate_key_ignore
357
-
358
- describe "#import" do
359
- extend ActiveSupport::TestCase::ImportAssertions
360
-
361
- macro(:perform_import) { raise "supply your own #perform_import in a context below" }
362
- macro(:updated_topic) { Topic.find(@topic.id) }
363
-
364
- context "with :on_duplicate_key_ignore and validation checks turned off" do
365
- let(:columns) { %w( id title author_name author_email_address parent_id ) }
366
- let(:values) { [[99, "Book", "John Doe", "john@doe.com", 17]] }
367
- let(:updated_values) { [[99, "Book - 2nd Edition", "Author Should Not Change", "johndoe@example.com", 57]] }
368
-
369
- setup do
370
- Topic.import columns, values, validate: false
371
- end
372
-
373
- it "should not update any records" do
374
- result = Topic.import columns, updated_values, on_duplicate_key_ignore: true, validate: false
375
- assert_equal [], result.ids
376
- end
377
- end
378
-
379
- context "with :on_duplicate_key_ignore and :recursive enabled" do
380
- let(:new_topic) { Build(1, :topic_with_book) }
381
- let(:mixed_topics) { Build(1, :topic_with_book) + new_topic + Build(1, :topic_with_book) }
382
-
383
- setup do
384
- Topic.import new_topic, recursive: true
385
- end
386
-
387
- # Recursive import depends on the primary keys of the parent model being returned
388
- # on insert. With on_duplicate_key_ignore enabled, not all ids will be returned
389
- # and it is possible that a model will be assigned the wrong id and then its children
390
- # would be associated with the wrong parent.
391
- it ":on_duplicate_key_ignore is ignored" do
392
- assert_raise ActiveRecord::RecordNotUnique do
393
- Topic.import mixed_topics, recursive: true, on_duplicate_key_ignore: true, validate: false
394
- end
395
- end
396
- end
397
-
398
- context "with :on_duplicate_key_update and validation checks turned off" do
399
- asssertion_group(:should_support_on_duplicate_key_update) do
400
- should_not_update_fields_not_mentioned
401
- should_update_foreign_keys
402
- should_not_update_created_at_on_timestamp_columns
403
- should_update_updated_at_on_timestamp_columns
404
- end
405
-
406
- context "using a hash" do
407
- context "with :columns :all" do
408
- let(:columns) { %w( id title author_name author_email_address parent_id ) }
409
- let(:updated_values) { [[99, "Book - 2nd Edition", "Jane Doe", "janedoe@example.com", 57]] }
410
-
411
- macro(:perform_import) do |*opts|
412
- Topic.import columns, updated_values, opts.extract_options!.merge(on_duplicate_key_update: { conflict_target: :id, columns: :all }, validate: false)
413
- end
414
-
415
- setup do
416
- values = [[99, "Book", "John Doe", "john@doe.com", 17, 3]]
417
- Topic.import columns + ['replies_count'], values, validate: false
418
- end
419
-
420
- it "should update all specified columns" do
421
- perform_import
422
- updated_topic = Topic.find(99)
423
- assert_equal 'Book - 2nd Edition', updated_topic.title
424
- assert_equal 'Jane Doe', updated_topic.author_name
425
- assert_equal 'janedoe@example.com', updated_topic.author_email_address
426
- assert_equal 57, updated_topic.parent_id
427
- assert_equal 3, updated_topic.replies_count
428
- end
429
- end
430
-
431
- context "with :columns a hash" do
432
- let(:columns) { %w( id title author_name author_email_address parent_id ) }
433
- let(:values) { [[99, "Book", "John Doe", "john@doe.com", 17]] }
434
- let(:updated_values) { [[99, "Book - 2nd Edition", "Author Should Not Change", "johndoe@example.com", 57]] }
435
-
436
- macro(:perform_import) do |*opts|
437
- Topic.import columns, updated_values, opts.extract_options!.merge(on_duplicate_key_update: { conflict_target: :id, columns: update_columns }, validate: false)
438
- end
439
-
440
- setup do
441
- Topic.import columns, values, validate: false
442
- @topic = Topic.find 99
443
- end
444
-
445
- it "should not modify the passed in :on_duplicate_key_update columns array" do
446
- assert_nothing_raised do
447
- columns = %w(title author_name).freeze
448
- Topic.import columns, [%w(foo, bar)], { on_duplicate_key_update: { columns: columns }.freeze }.freeze
449
- end
450
- end
451
-
452
- context "using string hash map" do
453
- let(:update_columns) { { "title" => "title", "author_email_address" => "author_email_address", "parent_id" => "parent_id" } }
454
- should_support_on_duplicate_key_update
455
- should_update_fields_mentioned
456
- end
457
-
458
- context "using string hash map, but specifying column mismatches" do
459
- let(:update_columns) { { "title" => "author_email_address", "author_email_address" => "title", "parent_id" => "parent_id" } }
460
- should_support_on_duplicate_key_update
461
- should_update_fields_mentioned_with_hash_mappings
462
- end
463
-
464
- context "using symbol hash map" do
465
- let(:update_columns) { { title: :title, author_email_address: :author_email_address, parent_id: :parent_id } }
466
- should_support_on_duplicate_key_update
467
- should_update_fields_mentioned
468
- end
469
-
470
- context "using symbol hash map, but specifying column mismatches" do
471
- let(:update_columns) { { title: :author_email_address, author_email_address: :title, parent_id: :parent_id } }
472
- should_support_on_duplicate_key_update
473
- should_update_fields_mentioned_with_hash_mappings
474
- end
475
- end
476
-
477
- context 'with :index_predicate' do
478
- let(:columns) { %w( id device_id alarm_type status metadata ) }
479
- let(:values) { [[99, 17, 1, 1, 'foo']] }
480
- let(:updated_values) { [[99, 17, 1, 2, 'bar']] }
481
-
482
- macro(:perform_import) do |*opts|
483
- Alarm.import columns, updated_values, opts.extract_options!.merge(on_duplicate_key_update: { conflict_target: [:device_id, :alarm_type], index_predicate: 'status <> 0', columns: [:status] }, validate: false)
484
- end
485
-
486
- macro(:updated_alarm) { Alarm.find(@alarm.id) }
487
-
488
- setup do
489
- Alarm.import columns, values, validate: false
490
- @alarm = Alarm.find 99
491
- end
492
-
493
- context 'supports on duplicate key update for partial indexes' do
494
- it 'should not update created_at timestamp columns' do
495
- Timecop.freeze Chronic.parse("5 minutes from now") do
496
- perform_import
497
- assert_in_delta @alarm.created_at.to_i, updated_alarm.created_at.to_i, 1
498
- end
499
- end
500
-
501
- it 'should update updated_at timestamp columns' do
502
- time = Chronic.parse("5 minutes from now")
503
- Timecop.freeze time do
504
- perform_import
505
- assert_in_delta time.to_i, updated_alarm.updated_at.to_i, 1
506
- end
507
- end
508
-
509
- it 'should not update fields not mentioned' do
510
- perform_import
511
- assert_equal 'foo', updated_alarm.metadata
512
- end
513
-
514
- it 'should update fields mentioned with hash mappings' do
515
- perform_import
516
- assert_equal 2, updated_alarm.status
517
- end
518
- end
519
- end
520
-
521
- context 'with :condition' do
522
- let(:columns) { %w( id device_id alarm_type status metadata) }
523
- let(:values) { [[99, 17, 1, 1, 'foo']] }
524
- let(:updated_values) { [[99, 17, 1, 1, 'bar']] }
525
-
526
- macro(:perform_import) do |*opts|
527
- Alarm.import(
528
- columns,
529
- updated_values,
530
- opts.extract_options!.merge(
531
- on_duplicate_key_update: {
532
- conflict_target: [:id],
533
- condition: "alarms.metadata NOT LIKE '%foo%'",
534
- columns: [:metadata]
535
- },
536
- validate: false
537
- )
538
- )
539
- end
540
-
541
- macro(:updated_alarm) { Alarm.find(@alarm.id) }
542
-
543
- setup do
544
- Alarm.import columns, values, validate: false
545
- @alarm = Alarm.find 99
546
- end
547
-
548
- it 'should not update fields not matched' do
549
- perform_import
550
- assert_equal 'foo', updated_alarm.metadata
551
- end
552
- end
553
-
554
- context "with :constraint_name" do
555
- let(:columns) { %w( id title author_name author_email_address parent_id ) }
556
- let(:values) { [[100, "Book", "John Doe", "john@doe.com", 17]] }
557
- let(:updated_values) { [[100, "Book - 2nd Edition", "Author Should Not Change", "johndoe@example.com", 57]] }
558
-
559
- macro(:perform_import) do |*opts|
560
- Topic.import columns, updated_values, opts.extract_options!.merge(on_duplicate_key_update: { constraint_name: :topics_pkey, columns: update_columns }, validate: false)
561
- end
562
-
563
- setup do
564
- Topic.import columns, values, validate: false
565
- @topic = Topic.find 100
566
- end
567
-
568
- let(:update_columns) { [:title, :author_email_address, :parent_id] }
569
- should_support_on_duplicate_key_update
570
- should_update_fields_mentioned
571
- end
572
-
573
- context "default to the primary key" do
574
- let(:columns) { %w( id title author_name author_email_address parent_id ) }
575
- let(:values) { [[100, "Book", "John Doe", "john@doe.com", 17]] }
576
- let(:updated_values) { [[100, "Book - 2nd Edition", "Author Should Not Change", "johndoe@example.com", 57]] }
577
- let(:update_columns) { [:title, :author_email_address, :parent_id] }
578
-
579
- setup do
580
- Topic.import columns, values, validate: false
581
- @topic = Topic.find 100
582
- end
583
-
584
- context "with no :conflict_target or :constraint_name" do
585
- macro(:perform_import) do |*opts|
586
- Topic.import columns, updated_values, opts.extract_options!.merge(on_duplicate_key_update: { columns: update_columns }, validate: false)
587
- end
588
-
589
- should_support_on_duplicate_key_update
590
- should_update_fields_mentioned
591
- end
592
-
593
- context "with empty value for :conflict_target" do
594
- macro(:perform_import) do |*opts|
595
- Topic.import columns, updated_values, opts.extract_options!.merge(on_duplicate_key_update: { conflict_target: [], columns: update_columns }, validate: false)
596
- end
597
-
598
- should_support_on_duplicate_key_update
599
- should_update_fields_mentioned
600
- end
601
-
602
- context "with empty value for :constraint_name" do
603
- macro(:perform_import) do |*opts|
604
- Topic.import columns, updated_values, opts.extract_options!.merge(on_duplicate_key_update: { constraint_name: '', columns: update_columns }, validate: false)
605
- end
606
-
607
- should_support_on_duplicate_key_update
608
- should_update_fields_mentioned
609
- end
610
- end
611
-
612
- context "with no :conflict_target or :constraint_name" do
613
- context "with no primary key" do
614
- it "raises ArgumentError" do
615
- error = assert_raises ArgumentError do
616
- Rule.import Build(3, :rules), on_duplicate_key_update: [:condition_text], validate: false
617
- end
618
- assert_match(/Expected :conflict_target or :constraint_name to be specified/, error.message)
619
- end
620
- end
621
- end
622
-
623
- context "with no :columns" do
624
- let(:columns) { %w( id title author_name author_email_address ) }
625
- let(:values) { [[100, "Book", "John Doe", "john@doe.com"]] }
626
- let(:updated_values) { [[100, "Title Should Not Change", "Author Should Not Change", "john@nogo.com"]] }
627
-
628
- macro(:perform_import) do |*opts|
629
- Topic.import columns, updated_values, opts.extract_options!.merge(on_duplicate_key_update: { conflict_target: :id }, validate: false)
630
- end
631
-
632
- setup do
633
- Topic.import columns, values, validate: false
634
- @topic = Topic.find 100
635
- end
636
-
637
- should_update_updated_at_on_timestamp_columns
638
- end
639
- end
640
- end
641
- end
642
- end
@@ -1,45 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- def should_support_on_duplicate_key_ignore
4
- describe "#import" do
5
- extend ActiveSupport::TestCase::ImportAssertions
6
- let(:topic) { Topic.create!(title: "Book", author_name: "John Doe") }
7
- let(:topics) { [topic] }
8
-
9
- context "with :on_duplicate_key_ignore" do
10
- it "should skip duplicates and continue import" do
11
- topics << Topic.new(title: "Book 2", author_name: "Jane Doe")
12
- assert_difference "Topic.count", +1 do
13
- result = Topic.import topics, on_duplicate_key_ignore: true, validate: false
14
- assert_not_equal topics.first.id, result.ids.first
15
- assert_nil topics.last.id
16
- end
17
- end
18
-
19
- unless ENV["SKIP_COMPOSITE_PK"]
20
- context "with composite primary keys" do
21
- it "should import array of values successfully" do
22
- columns = [:tag_id, :publisher_id, :tag]
23
- values = [[1, 1, 'Mystery'], [1, 1, 'Science']]
24
-
25
- assert_difference "Tag.count", +1 do
26
- Tag.import columns, values, on_duplicate_key_ignore: true, validate: false
27
- end
28
- assert_equal 'Mystery', Tag.first.tag
29
- end
30
- end
31
- end
32
- end
33
-
34
- context "with :ignore" do
35
- it "should skip duplicates and continue import" do
36
- topics << Topic.new(title: "Book 2", author_name: "Jane Doe")
37
- assert_difference "Topic.count", +1 do
38
- result = Topic.import topics, ignore: true, validate: false
39
- assert_not_equal topics.first.id, result.ids.first
40
- assert_nil topics.last.id
41
- end
42
- end
43
- end
44
- end
45
- end