bulk_insert 1.8.2 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
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