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 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
@@ -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
- attr_accessor :database
6
- attr_accessor :table
7
- attr_accessor :fields
8
- attr_writer :max_buffer_count
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
- "INSERT INTO #{database}.#{table} (#{fields.join(',')}) VALUES"
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 ;
@@ -1,3 +1,3 @@
1
1
  class Multisert
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -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 = 'multisert_test'
7
- TEST_TABLE = 'test_data'
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
- write_buffer_records = connection.query "DELETE FROM #{TEST_DATABASE}.#{TEST_TABLE}"
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.3
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-13 00:00:00.000000000 Z
12
+ date: 2013-03-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mysql2