multisert 0.0.3 → 0.0.4

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