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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +199 -0
- data/Rakefile +29 -0
- data/lib/bulk_insert.rb +38 -0
- data/lib/bulk_insert/statement_adapters.rb +22 -0
- data/lib/bulk_insert/statement_adapters/base_adapter.rb +21 -0
- data/lib/bulk_insert/statement_adapters/generic_adapter.rb +19 -0
- data/lib/bulk_insert/statement_adapters/mysql_adapter.rb +24 -0
- data/lib/bulk_insert/statement_adapters/postgresql_adapter.rb +28 -0
- data/lib/bulk_insert/statement_adapters/sqlite_adapter.rb +19 -0
- data/lib/bulk_insert/version.rb +7 -0
- data/lib/bulk_insert/worker.rb +136 -0
- data/test/bulk_insert/worker_test.rb +459 -0
- data/test/bulk_insert_test.rb +52 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/models/testing.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +29 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +24 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +41 -0
- data/test/dummy/config/environments/production.rb +79 -0
- data/test/dummy/config/environments/test.rb +42 -0
- data/test/dummy/config/initializers/assets.rb +11 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +56 -0
- data/test/dummy/config/secrets.yml +22 -0
- data/test/dummy/db/migrate/20151008181535_create_testings.rb +11 -0
- data/test/dummy/db/migrate/20151028194232_add_default_value.rb +5 -0
- data/test/dummy/db/schema.rb +25 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/test_helper.rb +19 -0
- 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
|