bulk_insert 1.8.2 → 1.9.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d23ef2d75aeafbefae857d874eff4f18183717fbcd1a63128a83ce407a0dfba1
4
- data.tar.gz: 9bb240020a5559bf12ff4d91be847590142302fce59927707509bfdd20820bfc
3
+ metadata.gz: 385a28475e8a07cdf1cad85aacf241734ce65ed922648417c7beda2b88a5b3c9
4
+ data.tar.gz: a14a05da23f256f3d30cf69fab032ec72c5953fe1ddb3ccc45a0673bef717266
5
5
  SHA512:
6
- metadata.gz: cadb63453d7036005118e8009007deb1e194a0284edbc8580009314fd02348b408bdfb2f640b6bf9eba6352a9eef77b696662463f24dbddaeedaf9dfb60613dd
7
- data.tar.gz: a93e786c4d29bf7a2678d32e657a7c7ae3e0c92a91121b52ce375eaa25eaab7cd73a884624f1315ae0e83fa93bdcb9f70c4160ea26dc07530baee2b28d8ef944
6
+ metadata.gz: cbe45a91a166d6faa3e2899a4fad7ac1af8a6e050249adbc810121ddc3000f0786c277b8c240809935bbb80178bbb1df43044dda32a02cea87c0d5d956e3cee1
7
+ data.tar.gz: 165de20f26c00dfcbe605866576f731199f692b7bccb07e671b8dcb9541552e39d0940a9f6fff73701286586f909e7be1254be6df157f41f76e83b105e88b9f2
data/README.md CHANGED
@@ -191,6 +191,30 @@ end
191
191
  worker.result_sets
192
192
  ```
193
193
 
194
+ ## Ruby and Rails Versions Supported
195
+
196
+ > :warning: The scope of this gem may be somehow covered natively by the `.insert_all` API
197
+ > introduced by [Rails 6](https://apidock.com/rails/v6.0.0/ActiveRecord/Persistence/ClassMethods/insert_all).
198
+ > This gem represents the state of art for rails version < 6 and it is still open to
199
+ > further developments for more recent versions.
200
+
201
+ The current CI prevents regressions on the following versions:
202
+
203
+ ruby / rails | `~>3` | `~>4` | `~>5` | `~>6`
204
+ :-----------:|-------|-------|-------|------
205
+ 2.2 | yes | yes | no | no
206
+ 2.3 | yes | yes | yes | no
207
+ 2.4 | no | yes | yes | no
208
+ 2.5 | no | no | yes | yes
209
+ 2.6 | no | no | yes | yes
210
+ 2.7 | no | no | yes | yes
211
+
212
+ The adapters covered in the CI are:
213
+ * sqlite
214
+ * mysql
215
+ * postgresql
216
+
217
+
194
218
  ## License
195
219
 
196
220
  BulkInsert is released under the MIT license (see MIT-LICENSE) by
@@ -1,7 +1,7 @@
1
1
  module BulkInsert
2
2
  MAJOR = 1
3
- MINOR = 8
4
- TINY = 2
3
+ MINOR = 9
4
+ TINY = 0
5
5
 
6
6
  VERSION = [MAJOR, MINOR, TINY].join(".")
7
7
  end
@@ -93,8 +93,19 @@ module BulkInsert
93
93
 
94
94
  def execute_query
95
95
  if query = compose_insert_query
96
- result_set = @connection.exec_query(query)
97
- @result_sets.push(result_set) if @return_primary_keys
96
+
97
+ # Return primary key support broke mysql compatibility
98
+ # with rails < 5 mysql adapter. (see issue #41)
99
+ if ActiveRecord::VERSION::STRING < "5.0.0" && @statement_adapter.is_a?(StatementAdapters::MySQLAdapter)
100
+ # raise an exception for unsupported return_primary_keys
101
+ raise ArgumentError.new("BulkInsert does not support @return_primary_keys for mysql and rails < 5") if @return_primary_keys
102
+
103
+ # restore v1.6 query execution
104
+ @connection.execute(query)
105
+ else
106
+ result_set = @connection.exec_query(query)
107
+ @result_sets.push(result_set) if @return_primary_keys
108
+ end
98
109
  end
99
110
  end
100
111
 
@@ -1,14 +1,17 @@
1
1
  require 'minitest/mock'
2
2
  require 'test_helper'
3
+ require 'connection_mocks'
3
4
 
4
5
  class BulkInsertWorkerTest < ActiveSupport::TestCase
6
+ include ConnectionMocks
7
+
5
8
  setup do
6
9
  @insert = BulkInsert::Worker.new(
7
10
  Testing.connection,
8
11
  Testing.table_name,
9
12
  'id',
10
13
  %w(greeting age happy created_at updated_at color))
11
- @now = Time.now
14
+ @now = Time.now.utc
12
15
  end
13
16
 
14
17
  test "empty insert is not pending" do
@@ -37,8 +40,8 @@ class BulkInsertWorkerTest < ActiveSupport::TestCase
37
40
  @insert.save!
38
41
 
39
42
  record = Testing.first
40
- assert_operator record.created_at, :>=, now
41
- assert_operator record.updated_at, :>=, now
43
+ assert_operator record.created_at.to_i, :>=, now.to_i
44
+ assert_operator record.updated_at.to_i, :>=, now.to_i
42
45
  end
43
46
 
44
47
  test "default timestamp columns should be equivalent for the entire batch" do
@@ -111,8 +114,8 @@ class BulkInsertWorkerTest < ActiveSupport::TestCase
111
114
  @insert.add ["Hello", 25, true, @now, @now]
112
115
  @insert.save!
113
116
 
114
- yo = Testing.find_by(greeting: 'Yo')
115
- hello = Testing.find_by(greeting: 'Hello')
117
+ yo = Testing.where(greeting: 'Yo').first
118
+ hello = Testing.where(greeting: 'Hello').first
116
119
 
117
120
  assert_not_nil yo
118
121
  assert_equal 15, yo.age
@@ -144,6 +147,10 @@ class BulkInsertWorkerTest < ActiveSupport::TestCase
144
147
  true
145
148
  )
146
149
 
150
+ # return_primary_keys is not supported for mysql and rails < 5
151
+ # skip is not supported in the minitest version used for testing rails 3
152
+ return if ActiveRecord::VERSION::STRING < "5.0.0" && worker.adapter_name =~ /^mysql/i
153
+
147
154
  assert_no_difference -> { worker.result_sets.count } do
148
155
  worker.save!
149
156
  end
@@ -239,8 +246,8 @@ class BulkInsertWorkerTest < ActiveSupport::TestCase
239
246
  @insert.add ["Hello", 25, true, @now, @now]
240
247
  @insert.save!
241
248
 
242
- yo = Testing.find_by(greeting: 'Yo')
243
- hello = Testing.find_by(greeting: 'Hello')
249
+ yo = Testing.where(greeting: 'Yo').first
250
+ hello = Testing.where(greeting: 'Hello').first
244
251
 
245
252
  assert_nil yo
246
253
  assert_not_nil hello
@@ -255,24 +262,35 @@ class BulkInsertWorkerTest < ActiveSupport::TestCase
255
262
  @insert.add ["Hello", 25, true, @now, @now]
256
263
  @insert.save!
257
264
 
258
- yo = Testing.find_by(greeting: 'Yo')
259
- hello = Testing.find_by(greeting: 'Hello')
265
+ yo = Testing.where(greeting: 'Yo').first
266
+ hello = Testing.where(greeting: 'Hello').first
260
267
 
261
268
  assert_nil yo
262
269
  assert_nil hello
263
270
  end
264
271
 
265
- test "adapter dependent default methods" do
266
- assert_equal @insert.adapter_name, 'SQLite'
267
- assert_equal @insert.insert_sql_statement, "INSERT INTO \"testings\" (\"greeting\",\"age\",\"happy\",\"created_at\",\"updated_at\",\"color\") VALUES "
272
+ test "adapter dependent SQLite methods" do
273
+ connection = Testing.connection
274
+ stub_connection_if_needed(connection, 'SQLite') do
275
+ sqlite_worker = BulkInsert::Worker.new(
276
+ connection,
277
+ Testing.table_name,
278
+ 'id',
279
+ %w(greeting age happy created_at updated_at color),
280
+ 500 # batch size
281
+ )
282
+
283
+ assert_equal sqlite_worker.adapter_name, 'SQLite'
284
+ assert_equal sqlite_worker.insert_sql_statement, "INSERT INTO \"testings\" (\"greeting\",\"age\",\"happy\",\"created_at\",\"updated_at\",\"color\") VALUES "
268
285
 
269
- @insert.add ["Yo", 15, false, nil, nil]
270
- assert_equal @insert.compose_insert_query, "INSERT INTO \"testings\" (\"greeting\",\"age\",\"happy\",\"created_at\",\"updated_at\",\"color\") VALUES ('Yo',15,0,NULL,NULL,'chartreuse')"
286
+ sqlite_worker.add ["Yo", 15, false, nil, nil]
287
+ assert_equal sqlite_worker.compose_insert_query, "INSERT INTO \"testings\" (\"greeting\",\"age\",\"happy\",\"created_at\",\"updated_at\",\"color\") VALUES ('Yo',15,0,NULL,NULL,'chartreuse')"
288
+ end
271
289
  end
272
290
 
273
- test "adapter dependent mysql methods" do
291
+ test "adapter dependent MySQL methods" do
274
292
  connection = Testing.connection
275
- connection.stub :adapter_name, 'MySQL' do
293
+ stub_connection_if_needed(connection, 'mysql') do
276
294
  mysql_worker = BulkInsert::Worker.new(
277
295
  connection,
278
296
  Testing.table_name,
@@ -282,20 +300,21 @@ class BulkInsertWorkerTest < ActiveSupport::TestCase
282
300
  true # ignore
283
301
  )
284
302
 
285
- assert_equal mysql_worker.adapter_name, 'MySQL'
286
- assert_equal (mysql_worker.adapter_name == 'MySQL'), true
303
+ assert_equal mysql_worker.adapter_name, 'mysql'
304
+ assert_equal (mysql_worker.adapter_name == 'mysql'), true
287
305
  assert_equal mysql_worker.ignore, true
288
- assert_equal ((mysql_worker.adapter_name == 'MySQL') & mysql_worker.ignore), true
306
+ assert_equal ((mysql_worker.adapter_name == 'mysql') & mysql_worker.ignore), true
289
307
 
290
308
  mysql_worker.add ["Yo", 15, false, nil, nil]
291
309
 
292
- assert_equal mysql_worker.compose_insert_query, "INSERT IGNORE INTO \"testings\" (\"greeting\",\"age\",\"happy\",\"created_at\",\"updated_at\",\"color\") VALUES ('Yo',15,0,NULL,NULL,'chartreuse')"
310
+ assert_statement_adapter mysql_worker, 'BulkInsert::StatementAdapters::MySQLAdapter'
311
+ assert_equal mysql_worker.compose_insert_query, "INSERT IGNORE INTO `testings` (`greeting`,`age`,`happy`,`created_at`,`updated_at`,`color`) VALUES ('Yo',15,FALSE,NULL,NULL,'chartreuse')"
293
312
  end
294
313
  end
295
314
 
296
315
  test "adapter dependent mysql methods work for mysql2" do
297
316
  connection = Testing.connection
298
- connection.stub :adapter_name, 'Mysql2' do
317
+ stub_connection_if_needed(connection, 'mysql2') do
299
318
  mysql_worker = BulkInsert::Worker.new(
300
319
  connection,
301
320
  Testing.table_name,
@@ -305,18 +324,19 @@ class BulkInsertWorkerTest < ActiveSupport::TestCase
305
324
  true, # ignore
306
325
  true) # update_duplicates
307
326
 
308
- assert_equal mysql_worker.adapter_name, 'Mysql2'
327
+ assert_equal mysql_worker.adapter_name, 'mysql2'
309
328
  assert mysql_worker.ignore
310
329
 
311
330
  mysql_worker.add ["Yo", 15, false, nil, nil]
312
331
 
313
- assert_equal mysql_worker.compose_insert_query, "INSERT IGNORE INTO \"testings\" (\"greeting\",\"age\",\"happy\",\"created_at\",\"updated_at\",\"color\") VALUES ('Yo',15,0,NULL,NULL,'chartreuse') ON DUPLICATE KEY UPDATE `greeting`=VALUES(`greeting`), `age`=VALUES(`age`), `happy`=VALUES(`happy`), `created_at`=VALUES(`created_at`), `updated_at`=VALUES(`updated_at`), `color`=VALUES(`color`)"
332
+ assert_statement_adapter mysql_worker, 'BulkInsert::StatementAdapters::MySQLAdapter'
333
+ assert_equal mysql_worker.compose_insert_query, "INSERT IGNORE INTO `testings` (`greeting`,`age`,`happy`,`created_at`,`updated_at`,`color`) VALUES ('Yo',15,FALSE,NULL,NULL,'chartreuse') ON DUPLICATE KEY UPDATE `greeting`=VALUES(`greeting`), `age`=VALUES(`age`), `happy`=VALUES(`happy`), `created_at`=VALUES(`created_at`), `updated_at`=VALUES(`updated_at`), `color`=VALUES(`color`)"
314
334
  end
315
335
  end
316
336
 
317
337
  test "adapter dependent Mysql2Spatial methods" do
318
338
  connection = Testing.connection
319
- connection.stub :adapter_name, 'Mysql2Spatial' do
339
+ stub_connection_if_needed(connection, 'mysql2spatial') do
320
340
  mysql_worker = BulkInsert::Worker.new(
321
341
  connection,
322
342
  Testing.table_name,
@@ -325,17 +345,18 @@ class BulkInsertWorkerTest < ActiveSupport::TestCase
325
345
  500, # batch size
326
346
  true) # ignore
327
347
 
328
- assert_equal mysql_worker.adapter_name, 'Mysql2Spatial'
348
+ assert_equal mysql_worker.adapter_name, 'mysql2spatial'
329
349
 
330
350
  mysql_worker.add ["Yo", 15, false, nil, nil]
331
351
 
332
- assert_equal mysql_worker.compose_insert_query, "INSERT IGNORE INTO \"testings\" (\"greeting\",\"age\",\"happy\",\"created_at\",\"updated_at\",\"color\") VALUES ('Yo',15,0,NULL,NULL,'chartreuse')"
333
- end
352
+ assert_statement_adapter mysql_worker, 'BulkInsert::StatementAdapters::MySQLAdapter'
353
+ assert_equal mysql_worker.compose_insert_query, "INSERT IGNORE INTO `testings` (`greeting`,`age`,`happy`,`created_at`,`updated_at`,`color`) VALUES ('Yo',15,FALSE,NULL,NULL,'chartreuse')"
354
+ end
334
355
  end
335
356
 
336
357
  test "adapter dependent postgresql methods" do
337
358
  connection = Testing.connection
338
- connection.stub :adapter_name, 'PostgreSQL' do
359
+ stub_connection_if_needed(connection, 'PostgreSQL') do
339
360
  pgsql_worker = BulkInsert::Worker.new(
340
361
  connection,
341
362
  Testing.table_name,
@@ -349,13 +370,19 @@ class BulkInsertWorkerTest < ActiveSupport::TestCase
349
370
 
350
371
  pgsql_worker.add ["Yo", 15, false, nil, nil]
351
372
 
352
- 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"
373
+ assert_statement_adapter pgsql_worker, 'BulkInsert::StatementAdapters::PostgreSQLAdapter'
374
+
375
+ if ActiveRecord::VERSION::STRING >= "5.0.0"
376
+ assert_equal pgsql_worker.compose_insert_query, "INSERT INTO \"testings\" (\"greeting\",\"age\",\"happy\",\"created_at\",\"updated_at\",\"color\") VALUES ('Yo',15,FALSE,NULL,NULL,'chartreuse') ON CONFLICT DO NOTHING RETURNING id"
377
+ else
378
+ assert_equal pgsql_worker.compose_insert_query, "INSERT INTO \"testings\" (\"greeting\",\"age\",\"happy\",\"created_at\",\"updated_at\",\"color\") VALUES ('Yo',15,'f',NULL,NULL,'chartreuse') ON CONFLICT DO NOTHING RETURNING id"
379
+ end
353
380
  end
354
381
  end
355
382
 
356
383
  test "adapter dependent postgresql methods (no ignore, no update_duplicates)" do
357
384
  connection = Testing.connection
358
- connection.stub :adapter_name, 'PostgreSQL' do
385
+ stub_connection_if_needed(connection, 'PostgreSQL') do
359
386
  pgsql_worker = BulkInsert::Worker.new(
360
387
  connection,
361
388
  Testing.table_name,
@@ -369,13 +396,19 @@ class BulkInsertWorkerTest < ActiveSupport::TestCase
369
396
 
370
397
  pgsql_worker.add ["Yo", 15, false, nil, nil]
371
398
 
372
- 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') RETURNING id"
399
+ assert_statement_adapter pgsql_worker, 'BulkInsert::StatementAdapters::PostgreSQLAdapter'
400
+
401
+ if ActiveRecord::VERSION::STRING >= "5.0.0"
402
+ assert_equal pgsql_worker.compose_insert_query, "INSERT INTO \"testings\" (\"greeting\",\"age\",\"happy\",\"created_at\",\"updated_at\",\"color\") VALUES ('Yo',15,FALSE,NULL,NULL,'chartreuse') RETURNING id"
403
+ else
404
+ assert_equal pgsql_worker.compose_insert_query, "INSERT INTO \"testings\" (\"greeting\",\"age\",\"happy\",\"created_at\",\"updated_at\",\"color\") VALUES ('Yo',15,'f',NULL,NULL,'chartreuse') RETURNING id"
405
+ end
373
406
  end
374
407
  end
375
408
 
376
409
  test "adapter dependent postgresql methods (with update_duplicates)" do
377
410
  connection = Testing.connection
378
- connection.stub :adapter_name, 'PostgreSQL' do
411
+ stub_connection_if_needed(connection, 'PostgreSQL') do
379
412
  pgsql_worker = BulkInsert::Worker.new(
380
413
  connection,
381
414
  Testing.table_name,
@@ -388,13 +421,19 @@ class BulkInsertWorkerTest < ActiveSupport::TestCase
388
421
  )
389
422
  pgsql_worker.add ["Yo", 15, false, nil, nil]
390
423
 
391
- 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"
424
+ assert_statement_adapter pgsql_worker, 'BulkInsert::StatementAdapters::PostgreSQLAdapter'
425
+
426
+ if ActiveRecord::VERSION::STRING >= "5.0.0"
427
+ assert_equal pgsql_worker.compose_insert_query, "INSERT INTO \"testings\" (\"greeting\",\"age\",\"happy\",\"created_at\",\"updated_at\",\"color\") VALUES ('Yo',15,FALSE,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"
428
+ else
429
+ assert_equal pgsql_worker.compose_insert_query, "INSERT INTO \"testings\" (\"greeting\",\"age\",\"happy\",\"created_at\",\"updated_at\",\"color\") VALUES ('Yo',15,'f',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"
430
+ end
392
431
  end
393
432
  end
394
433
 
395
434
  test "adapter dependent PostGIS methods" do
396
435
  connection = Testing.connection
397
- connection.stub :adapter_name, 'PostGIS' do
436
+ stub_connection_if_needed(connection, 'postgis') do
398
437
  pgsql_worker = BulkInsert::Worker.new(
399
438
  connection,
400
439
  Testing.table_name,
@@ -407,41 +446,55 @@ class BulkInsertWorkerTest < ActiveSupport::TestCase
407
446
  )
408
447
  pgsql_worker.add ["Yo", 15, false, nil, nil]
409
448
 
410
- 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"
449
+ assert_statement_adapter pgsql_worker, 'BulkInsert::StatementAdapters::PostgreSQLAdapter'
450
+
451
+ if ActiveRecord::VERSION::STRING >= "5.0.0"
452
+ assert_equal pgsql_worker.compose_insert_query, "INSERT INTO \"testings\" (\"greeting\",\"age\",\"happy\",\"created_at\",\"updated_at\",\"color\") VALUES ('Yo',15,FALSE,NULL,NULL,'chartreuse') ON CONFLICT DO NOTHING RETURNING id"
453
+ else
454
+ assert_equal pgsql_worker.compose_insert_query, "INSERT INTO \"testings\" (\"greeting\",\"age\",\"happy\",\"created_at\",\"updated_at\",\"color\") VALUES ('Yo',15,'f',NULL,NULL,'chartreuse') ON CONFLICT DO NOTHING RETURNING id"
455
+ end
411
456
  end
412
457
  end
413
458
 
414
459
  test "adapter dependent sqlite3 methods (with lowercase adapter name)" do
415
- sqlite_worker = BulkInsert::Worker.new(
416
- Testing.connection,
417
- Testing.table_name,
418
- 'id',
419
- %w(greeting age happy created_at updated_at color),
420
- 500, # batch size
421
- true) # ignore
422
- sqlite_worker.adapter_name = 'sqlite3'
423
- sqlite_worker.add ["Yo", 15, false, nil, nil]
460
+ connection = Testing.connection
461
+ stub_connection_if_needed(connection, 'sqlite3') do
462
+ sqlite_worker = BulkInsert::Worker.new(
463
+ Testing.connection,
464
+ Testing.table_name,
465
+ 'id',
466
+ %w(greeting age happy created_at updated_at color),
467
+ 500, # batch size
468
+ true) # ignore
469
+ sqlite_worker.adapter_name = 'sqlite3'
470
+ sqlite_worker.add ["Yo", 15, false, nil, nil]
424
471
 
425
- assert_equal sqlite_worker.compose_insert_query, "INSERT OR IGNORE INTO \"testings\" (\"greeting\",\"age\",\"happy\",\"created_at\",\"updated_at\",\"color\") VALUES ('Yo',15,0,NULL,NULL,'chartreuse')"
472
+ assert_statement_adapter sqlite_worker, 'BulkInsert::StatementAdapters::SQLiteAdapter'
473
+ assert_equal sqlite_worker.compose_insert_query, "INSERT OR IGNORE INTO \"testings\" (\"greeting\",\"age\",\"happy\",\"created_at\",\"updated_at\",\"color\") VALUES ('Yo',15,0,NULL,NULL,'chartreuse')"
474
+ end
426
475
  end
427
476
 
428
477
  test "adapter dependent sqlite3 methods (with stylecase adapter name)" do
429
- sqlite_worker = BulkInsert::Worker.new(
430
- Testing.connection,
431
- Testing.table_name,
432
- 'id',
433
- %w(greeting age happy created_at updated_at color),
434
- 500, # batch size
435
- true) # ignore
436
- sqlite_worker.adapter_name = 'SQLite'
437
- sqlite_worker.add ["Yo", 15, false, nil, nil]
478
+ connection = Testing.connection
479
+ stub_connection_if_needed(connection, 'SQLite') do
480
+ sqlite_worker = BulkInsert::Worker.new(
481
+ connection,
482
+ Testing.table_name,
483
+ 'id',
484
+ %w(greeting age happy created_at updated_at color),
485
+ 500, # batch size
486
+ true) # ignore
487
+ sqlite_worker.adapter_name = 'SQLite'
488
+ sqlite_worker.add ["Yo", 15, false, nil, nil]
438
489
 
439
- assert_equal sqlite_worker.compose_insert_query, "INSERT OR IGNORE INTO \"testings\" (\"greeting\",\"age\",\"happy\",\"created_at\",\"updated_at\",\"color\") VALUES ('Yo',15,0,NULL,NULL,'chartreuse')"
490
+ assert_statement_adapter sqlite_worker, 'BulkInsert::StatementAdapters::SQLiteAdapter'
491
+ assert_equal sqlite_worker.compose_insert_query, "INSERT OR IGNORE INTO \"testings\" (\"greeting\",\"age\",\"happy\",\"created_at\",\"updated_at\",\"color\") VALUES ('Yo',15,0,NULL,NULL,'chartreuse')"
492
+ end
440
493
  end
441
494
 
442
495
  test "mysql adapter can update duplicates" do
443
496
  connection = Testing.connection
444
- connection.stub :adapter_name, 'MySQL' do
497
+ stub_connection_if_needed(connection, 'mysql') do
445
498
  mysql_worker = BulkInsert::Worker.new(
446
499
  connection,
447
500
  Testing.table_name,
@@ -453,7 +506,12 @@ class BulkInsertWorkerTest < ActiveSupport::TestCase
453
506
  )
454
507
  mysql_worker.add ["Yo", 15, false, nil, nil]
455
508
 
456
- assert_equal mysql_worker.compose_insert_query, "INSERT INTO \"testings\" (\"greeting\",\"age\",\"happy\",\"created_at\",\"updated_at\",\"color\") VALUES ('Yo',15,0,NULL,NULL,'chartreuse') ON DUPLICATE KEY UPDATE `greeting`=VALUES(`greeting`), `age`=VALUES(`age`), `happy`=VALUES(`happy`), `created_at`=VALUES(`created_at`), `updated_at`=VALUES(`updated_at`), `color`=VALUES(`color`)"
509
+ assert_statement_adapter mysql_worker, 'BulkInsert::StatementAdapters::MySQLAdapter'
510
+ assert_equal mysql_worker.compose_insert_query, "INSERT INTO `testings` (`greeting`,`age`,`happy`,`created_at`,`updated_at`,`color`) VALUES ('Yo',15,FALSE,NULL,NULL,'chartreuse') ON DUPLICATE KEY UPDATE `greeting`=VALUES(`greeting`), `age`=VALUES(`age`), `happy`=VALUES(`happy`), `created_at`=VALUES(`created_at`), `updated_at`=VALUES(`updated_at`), `color`=VALUES(`color`)"
457
511
  end
458
512
  end
513
+
514
+ def assert_statement_adapter(worker, adapter_name)
515
+ assert_equal worker.instance_variable_get(:@statement_adapter).class.to_s, adapter_name
516
+ end
459
517
  end
@@ -30,8 +30,16 @@ class BulkInsertTest < ActiveSupport::TestCase
30
30
  test "with option to return primary keys, worker should have result sets" do
31
31
  worker = Testing.bulk_insert(return_primary_keys: true)
32
32
  worker.add greeting: "yo"
33
- worker.save!
34
- assert_equal 1, worker.result_sets.count
33
+
34
+ # return_primary_keys is not supported for mysql and rails < 5
35
+ # this test ensures that the case is covered in the CI and handled as expected
36
+ if ActiveRecord::VERSION::STRING < "5.0.0" && worker.adapter_name =~ /^mysql/i
37
+ error = assert_raise(ArgumentError) { worker.save! }
38
+ assert_equal error.message, "BulkInsert does not support @return_primary_keys for mysql and rails < 5"
39
+ else
40
+ worker.save!
41
+ assert_equal 1, worker.result_sets.count
42
+ end
35
43
  end
36
44
 
37
45
  test "bulk_insert with array should save the array immediately" do
@@ -0,0 +1,140 @@
1
+ module ConnectionMocks
2
+ DOUBLE_QUOTE_PROC = Proc.new do |value, *_column|
3
+ return value unless value.is_a? String
4
+ "\"#{value}\""
5
+ end
6
+
7
+ BACKTICK_QUOTE_PROC = Proc.new do |value, *_column|
8
+ return value unless value.is_a? String
9
+ '`' + value + '`'
10
+ end
11
+
12
+ BOOLEAN_VALUE_QUOTE_PROC = Proc.new do |value, *_column|
13
+ case value
14
+ when String
15
+ "'" + value + "'"
16
+ when TrueClass
17
+ 'TRUE'
18
+ when FalseClass
19
+ 'FALSE'
20
+ when NilClass
21
+ 'NULL'
22
+ else
23
+ value
24
+ end
25
+ end
26
+
27
+ LITERAL_BOOLEAN_VALUE_QUOTE_PROC = Proc.new do |value, *_column|
28
+ case value
29
+ when String
30
+ "'" + value + "'"
31
+ when TrueClass
32
+ "'t'"
33
+ when FalseClass
34
+ "'f'"
35
+ when NilClass
36
+ 'NULL'
37
+ else
38
+ value
39
+ end
40
+ end
41
+
42
+ DEFAULT_VALUE_QUOTE_PROC = Proc.new do |value, *_column|
43
+ case value
44
+ when String
45
+ "'" + value + "'"
46
+ when TrueClass
47
+ 1
48
+ when FalseClass
49
+ 0
50
+ when NilClass
51
+ 'NULL'
52
+ else
53
+ value
54
+ end
55
+ end
56
+
57
+ ColumnMock = Struct.new(:name, :default)
58
+ COLUMNS_MOCK_PROC = Proc.new do |*_table_name|
59
+ %w(id greeting age happy created_at updated_at color).zip(
60
+ [nil, nil, nil, nil, nil, nil, "chartreuse"]
61
+ ).map do |column_name, default|
62
+ ColumnMock.new(column_name, default)
63
+ end
64
+ end
65
+
66
+ MockTypeSerialize = Struct.new(:column) do
67
+ def serialize(value); value; end
68
+ end
69
+ CAST_COLUMN_MOCK_PROC = Proc.new do |column|
70
+ MockTypeSerialize.new(column)
71
+ end
72
+
73
+ def stub_connection_if_needed(connection, adapter_name)
74
+ raise "You need to provide a block" unless block_given?
75
+ if connection.adapter_name == adapter_name
76
+ yield
77
+ else
78
+ common_mocks(connection, adapter_name) do
79
+ case adapter_name
80
+ when /^mysql/i
81
+ mock_mysql_connection(connection, adapter_name) do
82
+ yield
83
+ end
84
+ when /\APost(?:greSQL|GIS)/i
85
+ mock_postgresql_connection(connection, adapter_name) do
86
+ yield
87
+ end
88
+ else
89
+ connection.stub :quote_table_name, DOUBLE_QUOTE_PROC do
90
+ connection.stub :quote_column_name, DOUBLE_QUOTE_PROC do
91
+ connection.stub :quote, DEFAULT_VALUE_QUOTE_PROC do
92
+ yield
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+ def common_mocks(connection, adapter_name)
102
+ connection.stub :adapter_name, adapter_name do
103
+ connection.stub :columns, COLUMNS_MOCK_PROC do
104
+ if ActiveRecord::VERSION::STRING >= "5.0.0"
105
+ connection.stub :lookup_cast_type_from_column, CAST_COLUMN_MOCK_PROC do
106
+ yield
107
+ end
108
+ else
109
+ yield
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ def mock_mysql_connection(connection, adapter_name)
116
+ connection.stub :quote_table_name, BACKTICK_QUOTE_PROC do
117
+ connection.stub :quote_column_name, BACKTICK_QUOTE_PROC do
118
+ connection.stub :quote, BOOLEAN_VALUE_QUOTE_PROC do
119
+ yield
120
+ end
121
+ end
122
+ end
123
+ end
124
+
125
+ def mock_postgresql_connection(connection, adapter_name)
126
+ connection.stub :quote_table_name, DOUBLE_QUOTE_PROC do
127
+ connection.stub :quote_column_name, DOUBLE_QUOTE_PROC do
128
+ if ActiveRecord::VERSION::STRING >= "5.0.0"
129
+ connection.stub :quote, BOOLEAN_VALUE_QUOTE_PROC do
130
+ yield
131
+ end
132
+ else
133
+ connection.stub :quote, LITERAL_BOOLEAN_VALUE_QUOTE_PROC do
134
+ yield
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end