multisert 0.0.3 → 0.0.4
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.
- data/README.md +16 -0
- data/lib/multisert.rb +23 -6
- data/lib/multisert/version.rb +1 -1
- data/spec/multisert_spec.rb +98 -13
- metadata +2 -2
data/README.md
CHANGED
@@ -71,6 +71,22 @@ script. This ensures that any pending entries are written to the database table
|
|
71
71
|
that were not automatically taken care of by the auto-write that will kick in
|
72
72
|
during the iteration.
|
73
73
|
|
74
|
+
## Insert Strategies
|
75
|
+
|
76
|
+
Multisert defaults to using `INSERT INTO` on `#write!`, but you can set the
|
77
|
+
insert strategy to `REPLACE INTO` or `INSERT IGNORE`:
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
buffer = Multisert.new
|
81
|
+
#=> would use INSERT INTO on #write! by default
|
82
|
+
|
83
|
+
buffer.insert_strategy = :replace
|
84
|
+
#=> would now use REPLACE INTO on #write!
|
85
|
+
|
86
|
+
buffer.insert_strategy = :ignore
|
87
|
+
#=> would now use INSERT IGNORE on #write!
|
88
|
+
```
|
89
|
+
|
74
90
|
## Performance
|
75
91
|
|
76
92
|
### Individual vs Buffer
|
data/lib/multisert.rb
CHANGED
@@ -1,11 +1,14 @@
|
|
1
1
|
class Multisert
|
2
2
|
MAX_BUFFER_COUNT_DEFAULT = 10_000
|
3
|
+
INSERT_OPERATION_DEFAULT = 'INSERT INTO'
|
3
4
|
|
4
|
-
attr_accessor :connection
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
attr_accessor :connection,
|
6
|
+
:database,
|
7
|
+
:table,
|
8
|
+
:fields,
|
9
|
+
:insert_strategy
|
10
|
+
|
11
|
+
attr_writer :max_buffer_count
|
9
12
|
|
10
13
|
def initialize attrs = {}
|
11
14
|
attrs.each do |attr, value|
|
@@ -42,6 +45,10 @@ class Multisert
|
|
42
45
|
|
43
46
|
private
|
44
47
|
|
48
|
+
def insert_strategy?
|
49
|
+
!!insert_strategy
|
50
|
+
end
|
51
|
+
|
45
52
|
def buffer
|
46
53
|
@buffer ||= []
|
47
54
|
end
|
@@ -63,7 +70,7 @@ private
|
|
63
70
|
end
|
64
71
|
|
65
72
|
def multisert_preamble
|
66
|
-
"
|
73
|
+
"#{insert_operation} #{database}.#{table} (#{fields.join(',')}) VALUES"
|
67
74
|
end
|
68
75
|
|
69
76
|
def multisert_values
|
@@ -73,6 +80,16 @@ private
|
|
73
80
|
}.join(",")
|
74
81
|
end
|
75
82
|
|
83
|
+
def insert_operation
|
84
|
+
return INSERT_OPERATION_DEFAULT unless insert_strategy?
|
85
|
+
|
86
|
+
case insert_strategy.to_s.downcase
|
87
|
+
when 'replace' then 'REPLACE INTO'
|
88
|
+
when 'ignore' then 'INSERT IGNORE'
|
89
|
+
else raise "no operation for \"#{insert_strategy}\" insert strategy"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
76
93
|
def cast value
|
77
94
|
case value
|
78
95
|
# TODO: want to escape the string too, checking for " and ;
|
data/lib/multisert/version.rb
CHANGED
data/spec/multisert_spec.rb
CHANGED
@@ -3,8 +3,9 @@ require './spec/spec_helper'
|
|
3
3
|
require './lib/multisert'
|
4
4
|
|
5
5
|
# TODO: allow overriding in yaml config
|
6
|
-
TEST_DATABASE
|
7
|
-
TEST_TABLE
|
6
|
+
TEST_DATABASE = 'multisert_test'
|
7
|
+
TEST_TABLE = 'test_data'
|
8
|
+
TEST_INDEXED_TABLE = 'test_indexed_data'
|
8
9
|
|
9
10
|
# TODO: make into yaml config
|
10
11
|
$connection = Mysql2::Client.new(host: 'localhost', username: 'root')
|
@@ -18,8 +19,13 @@ $cleaner = MultisertSpec::MrClean.new(database: TEST_DATABASE, connection: $conn
|
|
18
19
|
test_field_int_4 int default null,
|
19
20
|
test_field_varchar varchar(10) default null,
|
20
21
|
test_field_date DATE default null,
|
21
|
-
test_field_datetime DATETIME default null
|
22
|
-
|
22
|
+
test_field_datetime DATETIME default null)]
|
23
|
+
|
24
|
+
mgr.create_table_schemas << %[
|
25
|
+
CREATE TABLE IF NOT EXISTS #{mgr.database}.#{TEST_INDEXED_TABLE} (
|
26
|
+
test_id int not null,
|
27
|
+
test_field varchar(15) default null,
|
28
|
+
primary key (test_id))]
|
23
29
|
end
|
24
30
|
|
25
31
|
describe Multisert do
|
@@ -48,8 +54,7 @@ describe Multisert do
|
|
48
54
|
end
|
49
55
|
|
50
56
|
it "does not fall over when there are no entries" do
|
51
|
-
|
52
|
-
expect(write_buffer_records.to_a).to eq []
|
57
|
+
connection.query "DELETE FROM #{TEST_DATABASE}.#{TEST_TABLE}"
|
53
58
|
|
54
59
|
buffer.write_buffer!
|
55
60
|
|
@@ -58,7 +63,7 @@ describe Multisert do
|
|
58
63
|
expect(buffer.entries).to eq []
|
59
64
|
end
|
60
65
|
|
61
|
-
it "multi-inserts all added entries" do
|
66
|
+
it "multi-inserts all added entries and clears #entries" do
|
62
67
|
pre_write_buffer_records = connection.query "SELECT * FROM #{TEST_DATABASE}.#{TEST_TABLE}"
|
63
68
|
expect(pre_write_buffer_records.to_a).to eq []
|
64
69
|
|
@@ -89,8 +94,7 @@ describe Multisert do
|
|
89
94
|
{'test_field_int_1' => 1, 'test_field_int_2' => 3, 'test_field_int_3' => 4, 'test_field_int_4' => 5},
|
90
95
|
{'test_field_int_1' => 6, 'test_field_int_2' => 7, 'test_field_int_3' => 8, 'test_field_int_4' => 9},
|
91
96
|
{'test_field_int_1' => 10, 'test_field_int_2' => 11, 'test_field_int_3' => 12, 'test_field_int_4' => 13},
|
92
|
-
{'test_field_int_1' => 14, 'test_field_int_2' => 15, 'test_field_int_3' => 16, 'test_field_int_4' => 17}
|
93
|
-
]
|
97
|
+
{'test_field_int_1' => 14, 'test_field_int_2' => 15, 'test_field_int_3' => 16, 'test_field_int_4' => 17}]
|
94
98
|
|
95
99
|
expect(buffer.entries).to eq []
|
96
100
|
end
|
@@ -116,8 +120,7 @@ describe Multisert do
|
|
116
120
|
{'test_field_varchar' => 'a'},
|
117
121
|
{'test_field_varchar' => 'b'},
|
118
122
|
{'test_field_varchar' => 'c'},
|
119
|
-
{'test_field_varchar' => 'd'}
|
120
|
-
]
|
123
|
+
{'test_field_varchar' => 'd'}]
|
121
124
|
|
122
125
|
expect(buffer.entries).to eq []
|
123
126
|
end
|
@@ -146,8 +149,7 @@ describe Multisert do
|
|
146
149
|
{'test_field_date' => Date.parse('2013-01-15')},
|
147
150
|
{'test_field_date' => Date.parse('2013-01-16')},
|
148
151
|
{'test_field_date' => Date.parse('2013-01-17')},
|
149
|
-
{'test_field_date' => Date.parse('2013-01-18')}
|
150
|
-
]
|
152
|
+
{'test_field_date' => Date.parse('2013-01-18')}]
|
151
153
|
|
152
154
|
expect(buffer.entries).to eq []
|
153
155
|
end
|
@@ -195,4 +197,87 @@ describe Multisert do
|
|
195
197
|
expect(flush_method).to eq instance.method(:write_buffer!)
|
196
198
|
end
|
197
199
|
end
|
200
|
+
|
201
|
+
describe "#insert_strategy" do
|
202
|
+
let(:connection) { $connection }
|
203
|
+
let(:buffer) { described_class.new }
|
204
|
+
|
205
|
+
before do
|
206
|
+
$cleaner.ensure_clean_database! teardown_tables: (!!ENV['TEARDOWN'] || false)
|
207
|
+
end
|
208
|
+
|
209
|
+
context "set to replace" do
|
210
|
+
it "writes over an existing record with the same primary / unique key" do
|
211
|
+
connection.query %[INSERT INTO #{TEST_DATABASE}.#{TEST_INDEXED_TABLE} (test_id, test_field)
|
212
|
+
VALUES (1, 'ONE'), (2, 'TWO')]
|
213
|
+
|
214
|
+
buffer.connection = connection
|
215
|
+
buffer.database = TEST_DATABASE
|
216
|
+
buffer.table = TEST_INDEXED_TABLE
|
217
|
+
buffer.fields = ['test_id', 'test_field']
|
218
|
+
buffer.insert_strategy = :replace
|
219
|
+
|
220
|
+
buffer << [1, 'SOMETHING NEW']
|
221
|
+
|
222
|
+
buffer.write_buffer!
|
223
|
+
|
224
|
+
post_write_buffer_records = connection.query %[
|
225
|
+
SELECT
|
226
|
+
test_id
|
227
|
+
, test_field
|
228
|
+
FROM #{TEST_DATABASE}.#{TEST_INDEXED_TABLE}]
|
229
|
+
|
230
|
+
expect(post_write_buffer_records.to_a).to eq [
|
231
|
+
{'test_id' => 1, 'test_field' => 'SOMETHING NEW'},
|
232
|
+
{'test_id' => 2, 'test_field' => 'TWO'}]
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
context "set to ignore" do
|
237
|
+
before do
|
238
|
+
connection.query %[INSERT INTO #{TEST_DATABASE}.#{TEST_INDEXED_TABLE} (test_id, test_field)
|
239
|
+
VALUES (1, 'ONE'), (2, 'TWO')]
|
240
|
+
|
241
|
+
buffer.connection = connection
|
242
|
+
buffer.database = TEST_DATABASE
|
243
|
+
buffer.table = TEST_INDEXED_TABLE
|
244
|
+
buffer.fields = ['test_id', 'test_field']
|
245
|
+
buffer.insert_strategy = :ignore
|
246
|
+
|
247
|
+
buffer << [1, 'NEW']
|
248
|
+
end
|
249
|
+
|
250
|
+
it "does not raise an error" do
|
251
|
+
expect { buffer.write_buffer! }.to_not raise_error
|
252
|
+
end
|
253
|
+
|
254
|
+
it "writes over an existing record with the same primary / unique key" do
|
255
|
+
buffer.write_buffer!
|
256
|
+
|
257
|
+
post_write_buffer_records = connection.query %[
|
258
|
+
SELECT
|
259
|
+
test_id
|
260
|
+
, test_field
|
261
|
+
FROM #{TEST_DATABASE}.#{TEST_INDEXED_TABLE}]
|
262
|
+
|
263
|
+
expect(post_write_buffer_records.to_a).to eq [
|
264
|
+
{'test_id' => 1, 'test_field' => 'ONE'},
|
265
|
+
{'test_id' => 2, 'test_field' => 'TWO'}]
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
context "set to non-supported insert strategy" do
|
270
|
+
it "tells of an error" do
|
271
|
+
buffer.connection = connection
|
272
|
+
buffer.database = TEST_DATABASE
|
273
|
+
buffer.table = TEST_INDEXED_TABLE
|
274
|
+
buffer.fields = ['test_id', 'test_field']
|
275
|
+
buffer.insert_strategy = :some_bogus_operation
|
276
|
+
|
277
|
+
buffer << [1, 'NEW']
|
278
|
+
|
279
|
+
expect { buffer.write_buffer! }.to raise_error
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
198
283
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: multisert
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-03-
|
12
|
+
date: 2013-03-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mysql2
|