bulk_insert2 1.0.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 (55) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +199 -0
  4. data/Rakefile +29 -0
  5. data/lib/bulk_insert.rb +38 -0
  6. data/lib/bulk_insert/statement_adapters.rb +22 -0
  7. data/lib/bulk_insert/statement_adapters/base_adapter.rb +21 -0
  8. data/lib/bulk_insert/statement_adapters/generic_adapter.rb +19 -0
  9. data/lib/bulk_insert/statement_adapters/mysql_adapter.rb +24 -0
  10. data/lib/bulk_insert/statement_adapters/postgresql_adapter.rb +28 -0
  11. data/lib/bulk_insert/statement_adapters/sqlite_adapter.rb +19 -0
  12. data/lib/bulk_insert/version.rb +7 -0
  13. data/lib/bulk_insert/worker.rb +136 -0
  14. data/test/bulk_insert/worker_test.rb +459 -0
  15. data/test/bulk_insert_test.rb +52 -0
  16. data/test/dummy/README.rdoc +28 -0
  17. data/test/dummy/Rakefile +6 -0
  18. data/test/dummy/app/assets/javascripts/application.js +13 -0
  19. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  20. data/test/dummy/app/controllers/application_controller.rb +5 -0
  21. data/test/dummy/app/helpers/application_helper.rb +2 -0
  22. data/test/dummy/app/models/testing.rb +2 -0
  23. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  24. data/test/dummy/bin/bundle +3 -0
  25. data/test/dummy/bin/rails +4 -0
  26. data/test/dummy/bin/rake +4 -0
  27. data/test/dummy/bin/setup +29 -0
  28. data/test/dummy/config.ru +4 -0
  29. data/test/dummy/config/application.rb +24 -0
  30. data/test/dummy/config/boot.rb +5 -0
  31. data/test/dummy/config/database.yml +25 -0
  32. data/test/dummy/config/environment.rb +5 -0
  33. data/test/dummy/config/environments/development.rb +41 -0
  34. data/test/dummy/config/environments/production.rb +79 -0
  35. data/test/dummy/config/environments/test.rb +42 -0
  36. data/test/dummy/config/initializers/assets.rb +11 -0
  37. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  38. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  39. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  40. data/test/dummy/config/initializers/inflections.rb +16 -0
  41. data/test/dummy/config/initializers/mime_types.rb +4 -0
  42. data/test/dummy/config/initializers/session_store.rb +3 -0
  43. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  44. data/test/dummy/config/locales/en.yml +23 -0
  45. data/test/dummy/config/routes.rb +56 -0
  46. data/test/dummy/config/secrets.yml +22 -0
  47. data/test/dummy/db/migrate/20151008181535_create_testings.rb +11 -0
  48. data/test/dummy/db/migrate/20151028194232_add_default_value.rb +5 -0
  49. data/test/dummy/db/schema.rb +25 -0
  50. data/test/dummy/public/404.html +67 -0
  51. data/test/dummy/public/422.html +67 -0
  52. data/test/dummy/public/500.html +66 -0
  53. data/test/dummy/public/favicon.ico +0 -0
  54. data/test/test_helper.rb +19 -0
  55. metadata +181 -0
@@ -0,0 +1,136 @@
1
+ require_relative 'statement_adapters'
2
+
3
+ module BulkInsert
4
+ class Worker
5
+ attr_reader :connection
6
+ attr_accessor :set_size
7
+ attr_accessor :before_save_callback
8
+ attr_accessor :after_save_callback
9
+ attr_accessor :adapter_name
10
+ attr_reader :ignore, :update_duplicates, :result_sets
11
+
12
+ def initialize(connection, table_name, primary_key, column_names, set_size=500, ignore=false, update_duplicates=false, return_primary_keys=false)
13
+ @statement_adapter = StatementAdapters.adapter_for(connection)
14
+
15
+ @connection = connection
16
+ @set_size = set_size
17
+
18
+ @adapter_name = connection.adapter_name
19
+ # INSERT IGNORE only fails inserts with duplicate keys or unallowed nulls not the whole set of inserts
20
+ @ignore = ignore
21
+ @update_duplicates = update_duplicates
22
+ @return_primary_keys = return_primary_keys
23
+
24
+ columns = connection.columns(table_name)
25
+ column_map = columns.inject({}) { |h, c| h.update(c.name => c) }
26
+
27
+ @primary_key = primary_key
28
+ @columns = column_names.map { |name| column_map[name.to_s] }
29
+ @table_name = connection.quote_table_name(table_name)
30
+ @column_names = column_names.map { |name| connection.quote_column_name(name) }.join(",")
31
+
32
+ @before_save_callback = nil
33
+ @after_save_callback = nil
34
+
35
+ @result_sets = []
36
+ @set = []
37
+ end
38
+
39
+ def pending?
40
+ @set.any?
41
+ end
42
+
43
+ def pending_count
44
+ @set.count
45
+ end
46
+
47
+ def add(values)
48
+ save! if @set.length >= set_size
49
+
50
+ values = values.with_indifferent_access if values.is_a?(Hash)
51
+ mapped = @columns.map.with_index do |column, index|
52
+ value_exists = values.is_a?(Hash) ? values.key?(column.name) : (index < values.length)
53
+ if !value_exists
54
+ if column.default.present?
55
+ column.default
56
+ elsif column.name == "created_at" || column.name == "updated_at"
57
+ :__timestamp_placeholder
58
+ else
59
+ nil
60
+ end
61
+ else
62
+ values.is_a?(Hash) ? values[column.name] : values[index]
63
+ end
64
+ end
65
+
66
+ @set.push(mapped)
67
+ self
68
+ end
69
+
70
+ def add_all(rows)
71
+ rows.each { |row| add(row) }
72
+ self
73
+ end
74
+
75
+ def before_save(&block)
76
+ @before_save_callback = block
77
+ end
78
+
79
+ def after_save(&block)
80
+ @after_save_callback = block
81
+ end
82
+
83
+ def save!
84
+ if pending?
85
+ @before_save_callback.(@set) if @before_save_callback
86
+ execute_query
87
+ @after_save_callback.() if @after_save_callback
88
+ @set.clear
89
+ end
90
+
91
+ self
92
+ end
93
+
94
+ def execute_query
95
+ if query = compose_insert_query
96
+ result_set = @connection.exec_query(query)
97
+ @result_sets.push(result_set) if @return_primary_keys
98
+ end
99
+ end
100
+
101
+ def compose_insert_query
102
+ sql = insert_sql_statement
103
+ @now = Time.now
104
+ rows = []
105
+
106
+ @set.each do |row|
107
+ values = []
108
+ @columns.zip(row) do |column, value|
109
+ value = @now if value == :__timestamp_placeholder
110
+
111
+ if ActiveRecord::VERSION::STRING >= "5.0.0"
112
+ value = @connection.lookup_cast_type_from_column(column, value) if column
113
+ values << @connection.quote(value)
114
+ else
115
+ values << @connection.quote(value, column)
116
+ end
117
+ end
118
+ rows << "(#{values.join(',')})"
119
+ end
120
+
121
+ if !rows.empty?
122
+ sql << rows.join(",")
123
+ sql << @statement_adapter.on_conflict_statement(@columns, ignore, update_duplicates)
124
+ sql << @statement_adapter.primary_key_return_statement(@primary_key) if @return_primary_keys
125
+ sql
126
+ else
127
+ false
128
+ end
129
+ end
130
+
131
+ def insert_sql_statement
132
+ insert_ignore = @ignore ? @statement_adapter.insert_ignore_statement : ''
133
+ "INSERT #{insert_ignore} INTO #{@table_name} (#{@column_names}) VALUES "
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,459 @@
1
+ require 'minitest/mock'
2
+ require 'test_helper'
3
+
4
+ class BulkInsertWorkerTest < ActiveSupport::TestCase
5
+ setup do
6
+ @insert = BulkInsert::Worker.new(
7
+ Testing.connection,
8
+ Testing.table_name,
9
+ 'id',
10
+ %w(greeting age happy created_at updated_at color))
11
+ @now = Time.now
12
+ end
13
+
14
+ test "empty insert is not pending" do
15
+ assert_equal false, @insert.pending?
16
+ end
17
+
18
+ test "pending_count should describe size of pending set" do
19
+ assert_equal 0, @insert.pending_count
20
+ @insert.add ["Hello", 15, true, @now, @now]
21
+ assert_equal 1, @insert.pending_count
22
+ end
23
+
24
+ test "default set size" do
25
+ assert_equal 500, @insert.set_size
26
+ end
27
+
28
+ test "adding row to insert makes insert pending" do
29
+ @insert.add ["Hello", 15, true, @now, @now]
30
+ assert_equal true, @insert.pending?
31
+ end
32
+
33
+ test "add should default timestamp columns to current time" do
34
+ now = Time.now
35
+
36
+ @insert.add ["Hello", 15, true]
37
+ @insert.save!
38
+
39
+ record = Testing.first
40
+ assert_operator record.created_at, :>=, now
41
+ assert_operator record.updated_at, :>=, now
42
+ end
43
+
44
+ test "default timestamp columns should be equivalent for the entire batch" do
45
+ @insert.add ["Hello", 15, true]
46
+ @insert.add ["Howdy", 20, false]
47
+ @insert.save!
48
+
49
+ first, second = Testing.all
50
+ assert_equal first.created_at.to_f, second.created_at.to_f
51
+ assert_equal first.created_at.to_f, first.updated_at.to_f
52
+ end
53
+
54
+ test "add should use database default values when present" do
55
+ @insert.add greeting: "Hello", age: 20, happy: false
56
+ @insert.save!
57
+
58
+ record = Testing.first
59
+ assert_equal record.color, "chartreuse"
60
+ end
61
+
62
+ test "explicit nil should override defaults" do
63
+ @insert.add greeting: "Hello", age: 20, happy: false, color: nil
64
+ @insert.save!
65
+
66
+ record = Testing.first
67
+ assert_nil record.color
68
+ end
69
+
70
+ test "add should allow values given as Hash" do
71
+ @insert.add greeting: "Yo", age: 20, happy: false, created_at: @now, updated_at: @now
72
+ @insert.save!
73
+
74
+ record = Testing.first
75
+ assert_not_nil record
76
+ assert_equal "Yo", record.greeting
77
+ assert_equal 20, record.age
78
+ assert_equal false, record.happy?
79
+ end
80
+
81
+ test "add should save automatically when overflowing set size" do
82
+ @insert.set_size = 1
83
+ @insert.add ["Hello", 15, true, @now, @now]
84
+ @insert.add ["Yo", 20, false, @now, @now]
85
+ assert_equal 1, Testing.count
86
+ assert_equal "Hello", Testing.first.greeting
87
+ end
88
+
89
+ test "add_all should append all items to the set" do
90
+ @insert.add_all [
91
+ [ "Hello", 15, true ],
92
+ { greeting: "Hi", age: 55, happy: true }
93
+ ]
94
+ assert_equal 2, @insert.pending_count
95
+ end
96
+
97
+ test "save! makes insert not pending" do
98
+ @insert.add ["Hello", 15, true, @now, @now]
99
+ @insert.save!
100
+ assert_equal false, @insert.pending?
101
+ end
102
+
103
+ test "save! when not pending should do nothing" do
104
+ assert_no_difference 'Testing.count' do
105
+ @insert.save!
106
+ end
107
+ end
108
+
109
+ test "save! inserts pending records" do
110
+ @insert.add ["Yo", 15, false, @now, @now]
111
+ @insert.add ["Hello", 25, true, @now, @now]
112
+ @insert.save!
113
+
114
+ yo = Testing.find_by(greeting: 'Yo')
115
+ hello = Testing.find_by(greeting: 'Hello')
116
+
117
+ assert_not_nil yo
118
+ assert_equal 15, yo.age
119
+ assert_equal false, yo.happy?
120
+
121
+ assert_not_nil hello
122
+ assert_equal 25, hello.age
123
+ assert_equal true, hello.happy?
124
+ end
125
+
126
+ test "save! does not add to result sets when not returning primary keys" do
127
+ @insert.add greeting: "first"
128
+ @insert.add greeting: "second"
129
+ @insert.save!
130
+
131
+ assert_equal 0, @insert.result_sets.count
132
+ end
133
+
134
+
135
+ test "save! adds to result sets when returning primary keys" do
136
+ worker = BulkInsert::Worker.new(
137
+ Testing.connection,
138
+ Testing.table_name,
139
+ 'id',
140
+ %w(greeting age happy created_at updated_at color),
141
+ 500,
142
+ false,
143
+ false,
144
+ true
145
+ )
146
+
147
+ assert_no_difference -> { worker.result_sets.count } do
148
+ worker.save!
149
+ end
150
+
151
+ worker.add greeting: "first"
152
+ worker.add greeting: "second"
153
+ worker.save!
154
+ assert_equal 1, worker.result_sets.count
155
+
156
+ worker.add greeting: "third"
157
+ worker.add greeting: "fourth"
158
+ worker.save!
159
+ assert_equal 2, worker.result_sets.count
160
+ end
161
+
162
+ test "initialized with empty result sets array" do
163
+ new_worker = BulkInsert::Worker.new(
164
+ Testing.connection,
165
+ Testing.table_name,
166
+ 'id',
167
+ %w(greeting age happy created_at updated_at color)
168
+ )
169
+ assert_instance_of(Array, new_worker.result_sets)
170
+ assert_empty new_worker.result_sets
171
+ end
172
+
173
+ test "save! calls the after_save handler" do
174
+ x = 41
175
+
176
+ @insert.after_save do
177
+ x += 1
178
+ end
179
+
180
+ @insert.add ["Yo", 15, false, @now, @now]
181
+ @insert.add ["Hello", 25, true, @now, @now]
182
+ @insert.save!
183
+
184
+ assert_equal 42, x
185
+ end
186
+
187
+ test "after_save stores a block as a proc" do
188
+ @insert.after_save do
189
+ "hello"
190
+ end
191
+
192
+ assert_equal "hello", @insert.after_save_callback.()
193
+ end
194
+
195
+ test "after_save_callback can be set as a proc" do
196
+ @insert.after_save_callback = -> do
197
+ "hello"
198
+ end
199
+
200
+ assert_equal "hello", @insert.after_save_callback.()
201
+ end
202
+
203
+ test "save! calls the before_save handler" do
204
+ x = 41
205
+
206
+ @insert.before_save do
207
+ x += 1
208
+ end
209
+
210
+ @insert.add ["Yo", 15, false, @now, @now]
211
+ @insert.add ["Hello", 25, true, @now, @now]
212
+ @insert.save!
213
+
214
+ assert_equal 42, x
215
+ end
216
+
217
+ test "before_save stores a block as a proc" do
218
+ @insert.before_save do
219
+ "hello"
220
+ end
221
+
222
+ assert_equal "hello", @insert.before_save_callback.()
223
+ end
224
+
225
+ test "before_save_callback can be set as a proc" do
226
+ @insert.before_save_callback = -> do
227
+ "hello"
228
+ end
229
+
230
+ assert_equal "hello", @insert.before_save_callback.()
231
+ end
232
+
233
+ test "before_save can manipulate the set" do
234
+ @insert.before_save do |set|
235
+ set.reject!{|row| row[0] == "Yo"}
236
+ end
237
+
238
+ @insert.add ["Yo", 15, false, @now, @now]
239
+ @insert.add ["Hello", 25, true, @now, @now]
240
+ @insert.save!
241
+
242
+ yo = Testing.find_by(greeting: 'Yo')
243
+ hello = Testing.find_by(greeting: 'Hello')
244
+
245
+ assert_nil yo
246
+ assert_not_nil hello
247
+ end
248
+
249
+ test "save! doesn't blow up if before_save emptying the set" do
250
+ @insert.before_save do |set|
251
+ set.clear
252
+ end
253
+
254
+ @insert.add ["Yo", 15, false, @now, @now]
255
+ @insert.add ["Hello", 25, true, @now, @now]
256
+ @insert.save!
257
+
258
+ yo = Testing.find_by(greeting: 'Yo')
259
+ hello = Testing.find_by(greeting: 'Hello')
260
+
261
+ assert_nil yo
262
+ assert_nil hello
263
+ end
264
+
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 "
268
+
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')"
271
+ end
272
+
273
+ test "adapter dependent mysql methods" do
274
+ connection = Testing.connection
275
+ connection.stub :adapter_name, 'MySQL' do
276
+ mysql_worker = BulkInsert::Worker.new(
277
+ connection,
278
+ Testing.table_name,
279
+ 'id',
280
+ %w(greeting age happy created_at updated_at color),
281
+ 500, # batch size
282
+ true # ignore
283
+ )
284
+
285
+ assert_equal mysql_worker.adapter_name, 'MySQL'
286
+ assert_equal (mysql_worker.adapter_name == 'MySQL'), true
287
+ assert_equal mysql_worker.ignore, true
288
+ assert_equal ((mysql_worker.adapter_name == 'MySQL') & mysql_worker.ignore), true
289
+
290
+ mysql_worker.add ["Yo", 15, false, nil, nil]
291
+
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')"
293
+ end
294
+ end
295
+
296
+ test "adapter dependent mysql methods work for mysql2" do
297
+ connection = Testing.connection
298
+ connection.stub :adapter_name, 'Mysql2' do
299
+ mysql_worker = BulkInsert::Worker.new(
300
+ connection,
301
+ Testing.table_name,
302
+ 'id',
303
+ %w(greeting age happy created_at updated_at color),
304
+ 500, # batch size
305
+ true, # ignore
306
+ true) # update_duplicates
307
+
308
+ assert_equal mysql_worker.adapter_name, 'Mysql2'
309
+ assert mysql_worker.ignore
310
+
311
+ mysql_worker.add ["Yo", 15, false, nil, nil]
312
+
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`)"
314
+ end
315
+ end
316
+
317
+ test "adapter dependent Mysql2Spatial methods" do
318
+ connection = Testing.connection
319
+ connection.stub :adapter_name, 'Mysql2Spatial' do
320
+ mysql_worker = BulkInsert::Worker.new(
321
+ connection,
322
+ Testing.table_name,
323
+ 'id',
324
+ %w(greeting age happy created_at updated_at color),
325
+ 500, # batch size
326
+ true) # ignore
327
+
328
+ assert_equal mysql_worker.adapter_name, 'Mysql2Spatial'
329
+
330
+ mysql_worker.add ["Yo", 15, false, nil, nil]
331
+
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
334
+ end
335
+
336
+ test "adapter dependent postgresql methods" do
337
+ connection = Testing.connection
338
+ connection.stub :adapter_name, 'PostgreSQL' do
339
+ pgsql_worker = BulkInsert::Worker.new(
340
+ connection,
341
+ Testing.table_name,
342
+ 'id',
343
+ %w(greeting age happy created_at updated_at color),
344
+ 500, # batch size
345
+ true, # ignore
346
+ false, # update duplicates
347
+ true # return primary keys
348
+ )
349
+
350
+ pgsql_worker.add ["Yo", 15, false, nil, nil]
351
+
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"
353
+ end
354
+ end
355
+
356
+ test "adapter dependent postgresql methods (no ignore, no update_duplicates)" do
357
+ connection = Testing.connection
358
+ connection.stub :adapter_name, 'PostgreSQL' do
359
+ pgsql_worker = BulkInsert::Worker.new(
360
+ connection,
361
+ Testing.table_name,
362
+ 'id',
363
+ %w(greeting age happy created_at updated_at color),
364
+ 500, # batch size
365
+ false, # ignore
366
+ false, # update duplicates
367
+ true # return primary keys
368
+ )
369
+
370
+ pgsql_worker.add ["Yo", 15, false, nil, nil]
371
+
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"
373
+ end
374
+ end
375
+
376
+ test "adapter dependent postgresql methods (with update_duplicates)" do
377
+ connection = Testing.connection
378
+ connection.stub :adapter_name, 'PostgreSQL' do
379
+ pgsql_worker = BulkInsert::Worker.new(
380
+ connection,
381
+ Testing.table_name,
382
+ 'id',
383
+ %w(greeting age happy created_at updated_at color),
384
+ 500, # batch size
385
+ false, # ignore
386
+ %w(greeting age happy), # update duplicates
387
+ true # return primary keys
388
+ )
389
+ pgsql_worker.add ["Yo", 15, false, nil, nil]
390
+
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"
392
+ end
393
+ end
394
+
395
+ test "adapter dependent PostGIS methods" do
396
+ connection = Testing.connection
397
+ connection.stub :adapter_name, 'PostGIS' do
398
+ pgsql_worker = BulkInsert::Worker.new(
399
+ connection,
400
+ Testing.table_name,
401
+ 'id',
402
+ %w(greeting age happy created_at updated_at color),
403
+ 500, # batch size
404
+ true, # ignore
405
+ false, # update duplicates
406
+ true # return primary keys
407
+ )
408
+ pgsql_worker.add ["Yo", 15, false, nil, nil]
409
+
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"
411
+ end
412
+ end
413
+
414
+ 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]
424
+
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')"
426
+ end
427
+
428
+ 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]
438
+
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')"
440
+ end
441
+
442
+ test "mysql adapter can update duplicates" do
443
+ connection = Testing.connection
444
+ connection.stub :adapter_name, 'MySQL' do
445
+ mysql_worker = BulkInsert::Worker.new(
446
+ connection,
447
+ Testing.table_name,
448
+ 'id',
449
+ %w(greeting age happy created_at updated_at color),
450
+ 500, # batch size
451
+ false, # ignore
452
+ true # update_duplicates
453
+ )
454
+ mysql_worker.add ["Yo", 15, false, nil, nil]
455
+
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`)"
457
+ end
458
+ end
459
+ end