flydata 0.0.5.6 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,53 @@
1
+ require 'base64'
2
+
3
+ module Flydata
4
+ module Util
5
+ class Encryptor
6
+ SALT = 'ae8k9h10'
7
+
8
+ class << self
9
+ def encrypt(text, key)
10
+ validate_presence(text: text, key: key)
11
+ cipher = build_cipher(:encrypt, key)
12
+ Base64.encode64(cipher.update(text) + cipher.final)
13
+ end
14
+
15
+ def decrypt(text, key, param_name = nil)
16
+ decrypt!(text, key, param_name)
17
+ rescue => e
18
+ text
19
+ end
20
+
21
+ def decrypt!(text, key, param_name = nil)
22
+ validate_presence(text: text, key: key)
23
+ cipher = build_cipher(:decrypt, key)
24
+ cipher.update(Base64.decode64(text)) + cipher.final
25
+ rescue OpenSSL::Cipher::CipherError => e
26
+ raise "Failed to decrypt '#{param_name}' parameter. error:'#{e.to_s}'"
27
+ end
28
+
29
+ private
30
+
31
+ def validate_presence(names)
32
+ names.each do |name, value|
33
+ raise ArgumentError.new("#{name} is required.") if value.nil? or value.empty?
34
+ end
35
+ end
36
+
37
+ def build_cipher(type, key)
38
+ cipher = OpenSSL::Cipher::Cipher.new('AES-128-CBC')
39
+ case type
40
+ when :encrypt
41
+ cipher.encrypt
42
+ when :decrypt
43
+ cipher.decrypt
44
+ else
45
+ raise "Invalid type: #{type}"
46
+ end
47
+ cipher.pkcs5_keyivgen(key, SALT)
48
+ cipher
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,19 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib', 'flydata', 'fluent-plugins'))
2
+ require 'rspec'
3
+ require 'fluent/test'
4
+
5
+ module Fluent
6
+ module Test
7
+ def self.configure_plugin(plugin, str)
8
+ plugin = plugin.new if plugin.kind_of?(Class)
9
+ if str.is_a?(Fluent::Config::Element)
10
+ plugin.instance_variable_set(:@config, str)
11
+ else
12
+ plugin.instance_variable_set(:@config,
13
+ Config.parse(str, "(test)"))
14
+ end
15
+ plugin.configure(plugin.instance_variable_get(:@config))
16
+ self
17
+ end
18
+ end
19
+ end
@@ -3,35 +3,9 @@ require 'spec_helper'
3
3
  module Flydata
4
4
  module Command
5
5
  describe Sender do
6
- before :each do
7
- @sender = Sender.new
8
- end
9
- describe '#start' do
10
- it 'starts fluentd' do
11
- api_client = double('api_client')
12
- api_client.stub_chain(:data_port, :get, :[]).and_return 'active'
13
- ApiClient.stub(:new).and_return api_client
14
-
15
- @sender.start
16
- end
17
- end
18
- describe '#stop' do
19
- it 'stops fluentd' do
20
- @sender.stop
21
- end
22
- end
23
- describe '#restart' do
24
- before :all do
25
- @sender = Sender.new
26
- @sender.start
27
- end
28
- it 'restart fluentd' do
29
- @sender.restart
30
- end
31
- after :all do
32
- @sender.stop
33
- end
34
- end
6
+ pending '#start'
7
+ pending '#stop'
8
+ pending '#restart'
35
9
  end
36
10
  end
37
11
  end
@@ -0,0 +1,1049 @@
1
+ # coding: utf-8
2
+ require 'spec_helper'
3
+ require 'socket'
4
+
5
+ module Flydata
6
+ module Command
7
+ describe Sync do
8
+ end
9
+ end
10
+
11
+ module FileUtil
12
+ describe SyncFileManager do
13
+ let(:default_mysqldump_dir) do
14
+ File.join('/tmp', "sync_dump_#{Time.now.to_i}")
15
+ end
16
+ let(:default_data_entry) do
17
+ {"id"=>93,
18
+ "name"=>"flydata_sync_mysql",
19
+ "data_port_id"=>52,
20
+ "display_name"=>"flydata_sync_mysql",
21
+ "enabled"=>true,
22
+ "heroku_log_type"=>nil,
23
+ "heroku_resource_id"=>nil,
24
+ "log_deletion"=>nil,
25
+ "log_file_delimiter"=>nil,
26
+ "log_file_type"=>nil,
27
+ "log_path"=>nil,
28
+ "redshift_schema_name"=>"",
29
+ "redshift_table_name"=>nil,
30
+ "created_at"=>"2014-01-22T18:58:43Z",
31
+ "updated_at"=>"2014-01-30T02:42:26Z",
32
+ "type"=>"RedshiftMysqlDataEntry",
33
+ "tag_name"=>"flydata.a458c641_dp52.flydata_mysql",
34
+ "tag_name_dev"=>"flydata.a458c641_dp52.flydata_mysql.dev",
35
+ "data_port_key"=>"a458c641",
36
+ "mysql_data_entry_preference" =>
37
+ { "host"=>"localhost", "port"=>3306, "username"=>"masashi",
38
+ "password"=>"welcome", "database"=>"sync_test", "tables"=>nil,
39
+ "mysqldump_dir"=>default_mysqldump_dir, "forwarder" => "tcpforwarder",
40
+ "data_servers"=>"localhost:9905" }
41
+ }
42
+ end
43
+ let(:default_sfm) { SyncFileManager.new(default_data_entry) }
44
+
45
+ let (:status) { 'PARSING' }
46
+ let (:table_name) { 'test_table' }
47
+ let (:last_pos) { 9999 }
48
+ let (:binlog_pos) { {binfile: 'mysqlbin.00001', pos: 111} }
49
+ let (:state) { 'CREATE_TABLE' }
50
+ let (:substate) { nil }
51
+
52
+ after :each do
53
+ if Dir.exists?(default_mysqldump_dir)
54
+ Dir.delete(default_mysqldump_dir) rescue nil
55
+ end
56
+ if File.exists?(default_mysqldump_dir)
57
+ File.delete(default_mysqldump_dir) rescue nil
58
+ end
59
+ end
60
+
61
+ describe '#dump_file_path' do
62
+ context 'when mysqldump_dir param is nil' do
63
+ before do
64
+ default_data_entry['mysql_data_entry_preference'].delete('mysqldump_dir')
65
+ end
66
+ it do
67
+ expect(default_sfm.dump_file_path).to eq(
68
+ File.join(ENV['HOME'], '.flydata', 'dump', 'flydata_sync_mysql.dump'))
69
+ end
70
+ end
71
+ context 'when file exists' do
72
+ before { `touch #{default_mysqldump_dir}`}
73
+ it do
74
+ expect{default_sfm.dump_file_path}.to raise_error
75
+ end
76
+ end
77
+ context 'when directory exists' do
78
+ before { `mkdir -p #{default_mysqldump_dir}`}
79
+ it do
80
+ expect(FileUtils).to receive(:mkdir_p).with(default_mysqldump_dir).never
81
+ expect(default_sfm.dump_file_path).to eq(
82
+ File.join(default_mysqldump_dir, 'flydata_sync_mysql.dump'))
83
+ end
84
+ end
85
+ context 'when directory or file does not exist' do
86
+ it do
87
+ expect(FileUtils).to receive(:mkdir_p).with(default_mysqldump_dir).once
88
+ expect(default_sfm.dump_file_path).to eq(
89
+ File.join(default_mysqldump_dir, 'flydata_sync_mysql.dump'))
90
+ end
91
+ end
92
+ context 'when file name includes "~"' do
93
+ let(:default_mysqldump_dir) { "~/tmp/dump/sync_spec_#{Time.now.to_i}" }
94
+ it do
95
+ expected_dir = File.join(ENV['HOME'], default_mysqldump_dir[1..-1])
96
+ expect(FileUtils).to receive(:mkdir_p).with(expected_dir).once
97
+ expect(default_sfm.dump_file_path).to eq(
98
+ File.join(expected_dir, 'flydata_sync_mysql.dump'))
99
+ end
100
+ end
101
+ end
102
+
103
+ describe '#dump_pos_path' do
104
+ it { expect(default_sfm.dump_pos_path).to eq(
105
+ File.join(default_mysqldump_dir, 'flydata_sync_mysql.dump.pos')) }
106
+ end
107
+
108
+ describe '#save_dump_pos' do
109
+ context 'without mysql marshal data' do
110
+ it do
111
+ expect{default_sfm.save_dump_pos(
112
+ status, table_name, last_pos, binlog_pos, state, substate)}.not_to raise_error
113
+ expect(default_sfm.load_dump_pos).to eq({
114
+ status: status, table_name: table_name, last_pos: last_pos,
115
+ binlog_pos: binlog_pos, state: state, substate: substate,
116
+ mysql_table: nil
117
+ })
118
+ end
119
+ end
120
+ end
121
+
122
+ describe '#load_dump_pos' do
123
+ let (:mysql_table) do
124
+ Flydata::Mysql::MysqlTable.new(
125
+ table_name, { 'id' => { format_type: 'int' }, 'value' => { format_type: 'text' } }
126
+ )
127
+ end
128
+
129
+ context 'with mysql marshal data' do
130
+ before do
131
+ default_sfm.save_mysql_table_marshal_dump(mysql_table)
132
+ default_sfm.save_dump_pos(status, table_name, last_pos, binlog_pos, state, substate)
133
+ end
134
+ it do
135
+ ret = default_sfm.load_dump_pos
136
+ mt = ret.delete(:mysql_table)
137
+ expect(ret).to eq({
138
+ status: status, table_name: table_name, last_pos: last_pos,
139
+ binlog_pos: binlog_pos, state: state, substate: substate,
140
+ })
141
+ expect(mt.table_name).to eq(table_name)
142
+ expect(mt.columns).to eq({
143
+ 'id' => { format_type: 'int' },
144
+ 'value' => { format_type: 'text' },
145
+ })
146
+ end
147
+ end
148
+ end
149
+
150
+ describe '#save_binlog' do
151
+ let(:binfile) { 'mysqlbinlog.000001' }
152
+ let(:pos) { 107 }
153
+ let(:binlog_pos) { {binfile: binfile, pos: pos} }
154
+ it do
155
+ default_sfm.save_binlog(binlog_pos)
156
+ expect(`cat #{default_sfm.binlog_path}`).to eq("#{binfile}\t#{pos}")
157
+ end
158
+ end
159
+
160
+ describe '#binlog_path' do
161
+ it { expect(default_sfm.binlog_path).to eq("#{FLYDATA_HOME}/flydata_sync_mysql.binlog.pos") }
162
+ end
163
+ end
164
+ end
165
+
166
+ module Output
167
+ describe ForwarderFactory do
168
+ let(:forwarder) do
169
+ ForwarderFactory.create('tcpforwarder', 'test_tag', ['localhost:3456', 'localhost:4567'])
170
+ end
171
+ before :each do
172
+ conn = double(TCPSocket)
173
+ allow(conn).to receive(:setsockopt)
174
+ allow(conn).to receive(:write)
175
+ allow(conn).to receive(:close)
176
+ allow(TCPSocket).to receive(:new).and_return(conn)
177
+ allow(StringIO).to receive(:open)
178
+ end
179
+
180
+ describe '.create' do
181
+ context 'with nil forwarder_key' do
182
+ it 'should return TcpForwarder object' do
183
+ forwarder = ForwarderFactory.create(nil, 'test_tag', ['localhost:3456', 'localhost:4567'])
184
+ expect(forwarder.kind_of?(TcpForwarder)).to be_true
185
+ end
186
+ end
187
+
188
+ context 'with tcpforwarder forwarder_key' do
189
+ it 'should return TcpForwarder object' do
190
+ forwarder = ForwarderFactory.create('tcpforwarder', 'test_tag', ['localhost:3456', 'localhost:4567'])
191
+ expect(forwarder.kind_of?(TcpForwarder)).to be_true
192
+ end
193
+ end
194
+
195
+ context 'with sslforwarder forwarder_key' do
196
+ it 'should return SslForwarder object' do
197
+ forwarder = ForwarderFactory.create('sslforwarder', 'test_tag', ['localhost:3456', 'localhost:4567'])
198
+ expect(forwarder.kind_of?(SslForwarder)).to be_true
199
+ end
200
+ end
201
+ end
202
+
203
+ describe '#initialize' do
204
+ context 'servers is empty' do
205
+ it do
206
+ expect{ForwarderFactory.create('tcpforwarder', 'test_tag', [])}.to raise_error
207
+ end
208
+ end
209
+ context 'servers is nil' do
210
+ it do
211
+ expect{ForwarderFactory.create('tcpforwarder', 'test_tag', nil)}.to raise_error
212
+ end
213
+ end
214
+ end
215
+
216
+ describe '#emit' do
217
+ let(:record) { {table_name: 'test_table_name', log: '{"key":"value"}'} }
218
+ before :each do
219
+ forwarder.set_options({buffer_size_limit: ([Time.now.to_i,record].to_msgpack.bytesize * 2.5)})
220
+ end
221
+ context 'when the buffer size is less than threthold' do
222
+ it do
223
+ expect(forwarder.emit(record)).to be(false)
224
+ expect(forwarder.buffer_record_count).to be(1)
225
+ end
226
+ end
227
+ context 'when the buffer size exceeds threthold' do
228
+ it do
229
+ expect(forwarder.emit(record)).to be(false)
230
+ expect(forwarder.emit(record)).to be(false)
231
+ expect(forwarder.buffer_record_count).to be(2)
232
+ expect(forwarder.emit(record)).to be(true)
233
+ expect(forwarder.buffer_record_count).to be(0)
234
+ end
235
+ end
236
+ end
237
+
238
+ describe '#pickup_server' do
239
+ context 'with only one server' do
240
+ let(:servers) { ['localhost:1111'] }
241
+ let(:forwarder) {
242
+ ForwarderFactory.create('tcpforwarder', 'test_tag', servers)
243
+ }
244
+ it 'expect to return same server' do
245
+ expect(forwarder.pickup_server).to eq('localhost:1111')
246
+ expect(forwarder.pickup_server).to eq('localhost:1111')
247
+ expect(forwarder.pickup_server).to eq('localhost:1111')
248
+ end
249
+ end
250
+ context 'with servers' do
251
+ let(:servers) { ['localhost:1111', 'localhost:2222', 'localhost:3333'] }
252
+ let(:forwarder) {
253
+ ForwarderFactory.create('tcpforwarder', 'test_tag', servers)
254
+ }
255
+ it 'expect to return same server' do
256
+ expect(forwarder.pickup_server).to eq('localhost:1111')
257
+ expect(forwarder.pickup_server).to eq('localhost:2222')
258
+ expect(forwarder.pickup_server).to eq('localhost:3333')
259
+ expect(forwarder.pickup_server).to eq('localhost:1111')
260
+ end
261
+ end
262
+ end
263
+ end
264
+ end
265
+
266
+ module Mysql
267
+ describe MysqlDumpGenerator do
268
+ let(:stdout) { double(:stdout) }
269
+ let(:stderr) { double(:stderr) }
270
+ let(:status) { double(:status) }
271
+ let(:file_path) { File.join('/tmp', "flydata_sync_spec_mysqldump_#{Time.now.to_i}") }
272
+ let(:default_conf) { {
273
+ 'host' => 'localhost', 'port' => 3306, 'username' => 'admin',
274
+ 'password' => 'pass', 'database' => 'dev', 'tables' => 'users,groups',
275
+ } }
276
+ let(:default_dump_generator) { MysqlDumpGenerator.new(default_conf) }
277
+
278
+ describe '#initialize' do
279
+ context 'with password' do
280
+ subject { default_dump_generator.instance_variable_get(:@dump_cmd) }
281
+ it { should eq('mysqldump -h localhost -P 3306 -uadmin -ppass --skip-lock-tables ' +
282
+ '--single-transaction --flush-logs --hex-blob --master-data=2 dev users groups') }
283
+ end
284
+ context 'without password' do
285
+ let (:dump_generator) do
286
+ MysqlDumpGenerator.new(default_conf.merge({'password' => ''}))
287
+ end
288
+ subject { dump_generator.instance_variable_get(:@dump_cmd) }
289
+ it { should eq('mysqldump -h localhost -P 3306 -uadmin --skip-lock-tables ' +
290
+ '--single-transaction --flush-logs --hex-blob --master-data=2 dev users groups') }
291
+ end
292
+ end
293
+
294
+ describe '#dump' do
295
+ context 'when exit status is not 0' do
296
+ before do
297
+ `touch #{file_path}`
298
+ expect(status).to receive(:exitstatus).and_return 1
299
+ expect(Open3).to receive(:capture3).and_return(
300
+ ['(dummy std out)', '(dummy std err)', status]
301
+ )
302
+ end
303
+ it do
304
+ expect{ default_dump_generator.dump(file_path) }.to raise_error
305
+ expect(File.exists?(file_path)).to be_false
306
+ end
307
+ end
308
+ context 'when exit status is 0 but no file' do
309
+ before do
310
+ expect(status).to receive(:exitstatus).and_return 0
311
+ expect(Open3).to receive(:capture3).and_return(
312
+ ['(dummy std out)', '(dummy std err)', status]
313
+ )
314
+ end
315
+ it do
316
+ expect{ default_dump_generator.dump(file_path) }.to raise_error
317
+ expect(File.exists?(file_path)).to be_false
318
+ end
319
+ end
320
+ context 'when exit status is 0 but file size is 0' do
321
+ before do
322
+ `touch #{file_path}`
323
+ expect(status).to receive(:exitstatus).and_return 0
324
+ expect(Open3).to receive(:capture3).and_return(
325
+ ['(dummy std out)', '(dummy std err)', status]
326
+ )
327
+ end
328
+ it do
329
+ expect{ default_dump_generator.dump(file_path) }.to raise_error
330
+ expect(File.exists?(file_path)).to be_true
331
+ end
332
+ end
333
+ context 'when exit status is 0' do
334
+ before do
335
+ `echo "something..." > #{file_path}`
336
+ expect(status).to receive(:exitstatus).and_return 0
337
+ expect(Open3).to receive(:capture3).and_return(
338
+ ['(dummy std out)', '(dummy std err)', status]
339
+ )
340
+ end
341
+ it do
342
+ expect(default_dump_generator.dump(file_path)).to be_true
343
+ expect(File.exists?(file_path)).to be_true
344
+ end
345
+ end
346
+ after :each do
347
+ File.delete(file_path) if File.exists?(file_path)
348
+ end
349
+ end
350
+ end
351
+
352
+ describe MysqlDumpParser do
353
+ let(:file_path) { File.join('/tmp', "flydata_sync_spec_mysqldump_parse_#{Time.now.to_i}") }
354
+ let(:default_parser) { MysqlDumpParser.new(file_path) }
355
+
356
+ def generate_dump_file(content)
357
+ File.open(file_path, 'w') {|f| f.write(content)}
358
+ end
359
+
360
+ after do
361
+ File.delete(file_path) if File.exists?(file_path)
362
+ end
363
+
364
+ describe '#initialize' do
365
+ context 'when file does not exist' do
366
+ it do
367
+ expect{ MysqlDumpParser.new(file_path) }.to raise_error
368
+ end
369
+ end
370
+ context 'when file exists' do
371
+ before { generate_dump_file('') }
372
+ it do
373
+ expect(MysqlDumpParser.new(file_path)).to be_an_instance_of(MysqlDumpParser)
374
+ end
375
+ end
376
+ end
377
+
378
+ describe '#parse' do
379
+ DUMP_HEADER = <<EOT
380
+ -- MySQL dump 10.13 Distrib 5.6.13, for osx10.9 (x86_64)
381
+ --
382
+ -- Host: localhost Database: sync_test
383
+ -- ------------------------------------------------------
384
+ -- Server version>--5.6.13-log
385
+
386
+ /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
387
+ /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
388
+ /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
389
+ /*!40101 SET NAMES utf8 */;
390
+ /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
391
+ /*!40103 SET TIME_ZONE='+00:00' */;
392
+ /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
393
+ /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
394
+ /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
395
+ /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
396
+
397
+ --
398
+ -- Position to start replication or point-in-time recovery from
399
+ --
400
+
401
+ -- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000267', MASTER_LOG_POS=120;
402
+ EOT
403
+
404
+ def index_after(content, string)
405
+ content.index(string) + string.bytesize + 1
406
+ end
407
+
408
+ let(:default_parser) { MysqlDumpParser.new(file_path) }
409
+ let(:default_binlog_pos) { {binfile: 'mysql-bin.000267', pos: 120 } }
410
+ let(:dump_pos_after_binlog_pos) { index_after(DUMP_HEADER, 'MASTER_LOG_POS=120;') }
411
+
412
+ let(:create_table_block) { double('create_table_block') }
413
+ let(:insert_record_block) { double('insert_record_block') }
414
+ let(:check_point_block) { double('check_point_block') }
415
+
416
+ before do
417
+ generate_dump_file('')
418
+ end
419
+
420
+ context 'when dump does not contain binlog pos' do
421
+ before { generate_dump_file('dummy content') }
422
+ it do
423
+ expect(create_table_block).to receive(:call).never
424
+ expect(insert_record_block).to receive(:call).never
425
+ expect(check_point_block).to receive(:call).never
426
+ binlog_pos = default_parser.parse(
427
+ create_table_block, insert_record_block, check_point_block
428
+ )
429
+ expect(binlog_pos).to be_nil
430
+ end
431
+ end
432
+
433
+ context 'when dump contains only binlog pos' do
434
+ before { generate_dump_file(<<EOT
435
+ #{DUMP_HEADER}
436
+ EOT
437
+ ) }
438
+ it do
439
+ expect(create_table_block).to receive(:call).never
440
+ expect(insert_record_block).to receive(:call).never
441
+ expect(check_point_block).to receive(:call).once
442
+ binlog_pos = default_parser.parse(
443
+ create_table_block, insert_record_block, check_point_block
444
+ )
445
+ expect(binlog_pos).to eq(default_binlog_pos)
446
+ end
447
+ end
448
+
449
+ context 'when dump contains create table without records' do
450
+ let(:dump_content) { <<EOT
451
+ #{DUMP_HEADER}
452
+
453
+ DROP TABLE IF EXISTS `users_login`;
454
+ /*!40101 SET @saved_cs_client = @@character_set_client */;
455
+ /*!40101 SET character_set_client = utf8 */;
456
+ CREATE TABLE `users_login` (
457
+ `user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'users id',
458
+ `login_count` int(10) unsigned DEFAULT '0' COMMENT 'login count',
459
+ `create_time` datetime NOT NULL COMMENT 'create time',
460
+ `update_time` datetime NOT NULL COMMENT 'update time',
461
+ PRIMARY KEY (`user_id`)
462
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='';
463
+ /*!40101 SET character_set_client = @saved_cs_client */;
464
+
465
+ --
466
+ -- Dumping data for table `users_login`
467
+ --
468
+
469
+ LOCK TABLES `users_login` WRITE;
470
+ /*!40000 ALTER TABLE `users_login` DISABLE KEYS */;
471
+ /*!40000 ALTER TABLE `users_login` ENABLE KEYS */;
472
+ UNLOCK TABLES;
473
+
474
+ EOT
475
+ }
476
+ before { generate_dump_file(dump_content) }
477
+ it do
478
+ expect(create_table_block).to receive(:call) { |mysql_table|
479
+ expect(mysql_table.table_name).to eq('users_login')
480
+ }.once
481
+ expect(insert_record_block).to receive(:call).never
482
+ expect(check_point_block).to receive(:call) { |mysql_table, last_pos, binlog_pos, state, substate|
483
+ expect(mysql_table).to be_nil
484
+ expect(last_pos).to eq(dump_pos_after_binlog_pos)
485
+ expect(binlog_pos).to eq(default_binlog_pos)
486
+ expect(state).to eq(Flydata::Mysql::MysqlDumpParser::State::CREATE_TABLE)
487
+ expect(substate).to be_nil
488
+ }
489
+ expect(check_point_block).to receive(:call) { |mysql_table, last_pos, binlog_pos, state, substate|
490
+ expect(mysql_table.table_name).to eq('users_login')
491
+ expect(mysql_table.columns.keys).to eq(['user_id', 'login_count', 'create_time', 'update_time'])
492
+ expect(last_pos).to eq(index_after(dump_content, 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=\'\';'))
493
+ expect(binlog_pos).to eq(default_binlog_pos)
494
+ expect(state).to eq(Flydata::Mysql::MysqlDumpParser::State::INSERT_RECORD)
495
+ expect(substate).to be_nil
496
+ }
497
+ expect(check_point_block).to receive(:call) { |mysql_table, last_pos, binlog_pos, state, substate|
498
+ expect(mysql_table.table_name).to eq('users_login')
499
+ expect(last_pos).to eq(index_after(dump_content, 'UNLOCK TABLES;'))
500
+ expect(binlog_pos).to eq(default_binlog_pos)
501
+ expect(state).to eq(Flydata::Mysql::MysqlDumpParser::State::CREATE_TABLE)
502
+ expect(substate).to be_nil
503
+ }
504
+ expect(check_point_block).to receive(:call).never
505
+
506
+ binlog_pos = default_parser.parse(
507
+ create_table_block, insert_record_block, check_point_block
508
+ )
509
+ expect(binlog_pos).to eq(default_binlog_pos)
510
+ end
511
+ end
512
+
513
+ context 'when dump contains create table with multi inserts' do
514
+ let(:dump_content) { <<EOT
515
+ #{DUMP_HEADER}
516
+
517
+ DROP TABLE IF EXISTS `users_login`;
518
+ /*!40101 SET @saved_cs_client = @@character_set_client */;
519
+ /*!40101 SET character_set_client = utf8 */;
520
+ CREATE TABLE `users_login` (
521
+ `user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'users id',
522
+ `login_count` int(10) unsigned DEFAULT '0' COMMENT 'login count',
523
+ `comment` varchar(255) DEFAULT NULL COMMENT 'comment',
524
+ `create_time` datetime NOT NULL COMMENT 'create time',
525
+ `update_time` datetime NOT NULL COMMENT 'update time',
526
+ PRIMARY KEY (`user_id`)
527
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
528
+ /*!40101 SET character_set_client = @saved_cs_client */;
529
+
530
+ --
531
+ -- Dumping data for table `users_login`
532
+ --
533
+
534
+ LOCK TABLES `users_login` WRITE;
535
+ /*!40000 ALTER TABLE `users_login` DISABLE KEYS */;
536
+ INSERT INTO `users_login` VALUES (15,46,'moodier','2001-10-02 08:18:08','1986-06-11 22:50:10'),(35,6,'missあいうえおteer','1991-10-15 19:38:07','1970-10-01 22:03:10'),(52,33,'sub\\\\field','1972-08-23 20:16:08','1974-10-10 23:28:11');
537
+ INSERT INTO `users_login` VALUES (373,31,'out\\'swearing','1979-10-07 08:10:08','2006-02-22 16:26:04'),(493,8,'schizophrenic','1979-07-06 07:34:07','1970-08-09 01:21:01');
538
+ /*!40000 ALTER TABLE `users_login` ENABLE KEYS */;
539
+ UNLOCK TABLES;
540
+ /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
541
+
542
+ EOT
543
+ }
544
+ before { generate_dump_file(dump_content) }
545
+ it do
546
+ # create_table_block
547
+ expect(create_table_block).to receive(:call) { |mysql_table|
548
+ expect(mysql_table.table_name).to eq('users_login')
549
+ expect(mysql_table.columns.keys).to eq(['user_id', 'login_count', 'comment', 'create_time', 'update_time'])
550
+ }.once
551
+ expect(create_table_block).to receive(:call).never
552
+
553
+ # insert_record_block
554
+ [
555
+ [ %w(15 46 moodier 2001-10-02\ 08:18:08 1986-06-11\ 22:50:10),
556
+ %w(35 6 missあいうえおteer 1991-10-15\ 19:38:07 1970-10-01\ 22:03:10),
557
+ %w(52 33 sub\\field 1972-08-23\ 20:16:08 1974-10-10\ 23:28:11),],
558
+ [ %w(373 31 out'swearing 1979-10-07\ 08:10:08 2006-02-22\ 16:26:04),
559
+ %w(493 8 schizophrenic 1979-07-06\ 07:34:07 1970-08-09\ 01:21:01),]
560
+ ].each do |expected_values|
561
+ expect(insert_record_block).to receive(:call) { |mysql_table, values_set|
562
+ expect(mysql_table.table_name).to eq('users_login')
563
+ expect(values_set).to eq(expected_values)
564
+ nil
565
+ }.once
566
+ end
567
+ expect(insert_record_block).to receive(:call).never
568
+
569
+ # insert_record_block
570
+ [
571
+ {state: Flydata::Mysql::MysqlDumpParser::State::CREATE_TABLE},
572
+ {state: Flydata::Mysql::MysqlDumpParser::State::INSERT_RECORD},
573
+ {state: Flydata::Mysql::MysqlDumpParser::State::CREATE_TABLE,
574
+ last_pos: index_after(dump_content, 'UNLOCK TABLES;') + 10}
575
+ ].each do |expected_params|
576
+ expect(check_point_block).to receive(:call) { |mysql_table, last_pos, binlog_pos, state, substate|
577
+ expect(mysql_table.table_name).to eq('users_login') if mysql_table
578
+ expect(state).to eq(expected_params[:state])
579
+ if expected_params[:last_pos]
580
+ expect(last_pos).to eq(expected_params[:last_pos])
581
+ end
582
+ expect(binlog_pos).to eq(default_binlog_pos)
583
+ expect(substate).to eq(expected_params[:substate])
584
+ expected_params[:result]
585
+ }.once
586
+ end
587
+ expect(check_point_block).to receive(:call).never
588
+
589
+ binlog_pos = default_parser.parse(
590
+ create_table_block, insert_record_block, check_point_block
591
+ )
592
+ expect(binlog_pos).to eq(default_binlog_pos)
593
+ end
594
+ end
595
+
596
+ context 'when dump contains create table with records' do
597
+ let(:dump_content) { <<EOT
598
+ #{DUMP_HEADER}
599
+
600
+ DROP TABLE IF EXISTS `users_login`;
601
+ /*!40101 SET @saved_cs_client = @@character_set_client */;
602
+ /*!40101 SET character_set_client = utf8 */;
603
+ CREATE TABLE `users_login` (
604
+ `user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'users id',
605
+ `login_count` int(10) unsigned DEFAULT '0' COMMENT 'login count',
606
+ `comment` varchar(255) DEFAULT NULL COMMENT 'comment',
607
+ `create_time` datetime NOT NULL COMMENT 'create time',
608
+ `update_time` datetime NOT NULL COMMENT 'update time',
609
+ PRIMARY KEY (`user_id`)
610
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
611
+ /*!40101 SET character_set_client = @saved_cs_client */;
612
+
613
+ --
614
+ -- Dumping data for table `users_login`
615
+ --
616
+
617
+ LOCK TABLES `users_login` WRITE;
618
+ /*!40000 ALTER TABLE `users_login` DISABLE KEYS */;
619
+ INSERT INTO `users_login` VALUES (15,46,'moodier','2001-10-02 08:18:08','1986-06-11 22:50:10'),(35,6,NULL,'1991-10-15 19:38:07','1970-10-01 22:03:10'),(52,33,'subfield','1972-08-23 20:16:08','1974-10-10 23:28:11');
620
+ /*!40000 ALTER TABLE `users_login` ENABLE KEYS */;
621
+ UNLOCK TABLES;
622
+ /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
623
+
624
+ EOT
625
+ }
626
+ before { generate_dump_file(dump_content) }
627
+ it do
628
+ # create_table_block
629
+ expect(create_table_block).to receive(:call) { |mysql_table|
630
+ expect(mysql_table.table_name).to eq('users_login')
631
+ expect(mysql_table.columns.keys).to eq(['user_id', 'login_count', 'comment', 'create_time', 'update_time'])
632
+ }.once
633
+ expect(create_table_block).to receive(:call).never
634
+
635
+ # insert_record_block
636
+ [
637
+ [ %w(15 46 moodier 2001-10-02\ 08:18:08 1986-06-11\ 22:50:10),
638
+ ['35', '6', nil, '1991-10-15 19:38:07', '1970-10-01 22:03:10'],
639
+ %w(52 33 subfield 1972-08-23\ 20:16:08 1974-10-10\ 23:28:11),],
640
+ ].each do |expected_values|
641
+ expect(insert_record_block).to receive(:call) { |mysql_table, values_set|
642
+ expect(mysql_table.table_name).to eq('users_login')
643
+ expect(values_set).to eq(expected_values)
644
+ nil
645
+ }.once
646
+ end
647
+ expect(insert_record_block).to receive(:call).never
648
+
649
+ # insert_record_block
650
+ [
651
+ {state: Flydata::Mysql::MysqlDumpParser::State::CREATE_TABLE},
652
+ {state: Flydata::Mysql::MysqlDumpParser::State::INSERT_RECORD},
653
+ {state: Flydata::Mysql::MysqlDumpParser::State::CREATE_TABLE,
654
+ last_pos: index_after(dump_content, 'UNLOCK TABLES;')}
655
+ ].each do |expected_params|
656
+ expect(check_point_block).to receive(:call) { |mysql_table, last_pos, binlog_pos, state, substate|
657
+ expect(mysql_table.table_name).to eq('users_login') if mysql_table
658
+ expect(state).to eq(expected_params[:state])
659
+ if expected_params[:last_pos]
660
+ expect(last_pos).to eq(expected_params[:last_pos])
661
+ end
662
+ expect(binlog_pos).to eq(default_binlog_pos)
663
+ expect(substate).to eq(expected_params[:substate])
664
+ expected_params[:result]
665
+ }.once
666
+ end
667
+ expect(check_point_block).to receive(:call).never
668
+
669
+ binlog_pos = default_parser.parse(
670
+ create_table_block, insert_record_block, check_point_block
671
+ )
672
+ expect(binlog_pos).to eq(default_binlog_pos)
673
+ end
674
+ end
675
+
676
+ context 'with resume after creating table' do
677
+ let(:dump_content_head) { <<EOT
678
+ #{DUMP_HEADER}
679
+
680
+ DROP TABLE IF EXISTS `users_login`;
681
+ /*!40101 SET @saved_cs_client = @@character_set_client */;
682
+ /*!40101 SET character_set_client = utf8 */;
683
+ CREATE TABLE `users_login` (
684
+ `user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'users id',
685
+ `login_count` int(10) unsigned DEFAULT '0' COMMENT 'login count',
686
+ `comment` varchar(255) DEFAULT NULL COMMENT 'comment',
687
+ `create_time` datetime NOT NULL COMMENT 'create time',
688
+ `update_time` datetime NOT NULL COMMENT 'update time',
689
+ PRIMARY KEY (`user_id`)
690
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
691
+ /*!40101 SET character_set_client = @saved_cs_client */;
692
+
693
+ EOT
694
+ }
695
+ let(:dump_content_all) { <<EOT
696
+ #{dump_content_head}
697
+ --
698
+ -- Dumping data for table `users_login`
699
+ --
700
+
701
+ LOCK TABLES `users_login` WRITE;
702
+ /*!40000 ALTER TABLE `users_login` DISABLE KEYS */;
703
+ INSERT INTO `users_login` VALUES (15,46,'moodier','2001-10-02 08:18:08','1986-06-11 22:50:10'),(35,6,'missteer','1991-10-15 19:38:07','1970-10-01 22:03:10'),(52,33,'subfield','1972-08-23 20:16:08','1974-10-10 23:28:11');
704
+ /*!40000 ALTER TABLE `users_login` ENABLE KEYS */;
705
+ UNLOCK TABLES;
706
+ /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
707
+ EOT
708
+ }
709
+ before do
710
+ generate_dump_file(dump_content_head)
711
+ default_parser.parse(
712
+ Proc.new{},
713
+ Proc.new{
714
+ raise "Should not be called"
715
+ },
716
+ Proc.new{ |mysql_table, last_pos, binlog_pos, state, substate|
717
+ if mysql_table
718
+ @mysql_table = mysql_table
719
+ @option = { status: Flydata::Command::Sync::STATUS_PARSING,
720
+ table_name: mysql_table.table_name,
721
+ last_pos: last_pos,
722
+ binlog_pos: binlog_pos,
723
+ state: state,
724
+ substate: substate,
725
+ mysql_table: mysql_table }
726
+ end
727
+ }
728
+ )
729
+ expect(@option).to eq({
730
+ status: Flydata::Command::Sync::STATUS_PARSING,
731
+ table_name: 'users_login',
732
+ last_pos: index_after(dump_content_head, 'ENGINE=InnoDB DEFAULT CHARSET=utf8;'),
733
+ binlog_pos: default_binlog_pos,
734
+ state: Flydata::Mysql::MysqlDumpParser::State::INSERT_RECORD,
735
+ substate: nil,
736
+ mysql_table: @mysql_table
737
+ })
738
+ end
739
+ it do
740
+ generate_dump_file(dump_content_all)
741
+ parser_for_resume = MysqlDumpParser.new(file_path, @option)
742
+
743
+ # create_table_block
744
+ expect(create_table_block).to receive(:call).never
745
+
746
+ # insert_record_block
747
+ [
748
+ [ %w(15 46 moodier 2001-10-02\ 08:18:08 1986-06-11\ 22:50:10),
749
+ %w(35 6 missteer 1991-10-15\ 19:38:07 1970-10-01\ 22:03:10),
750
+ %w(52 33 subfield 1972-08-23\ 20:16:08 1974-10-10\ 23:28:11),],
751
+ ].each do |expected_values|
752
+ expect(insert_record_block).to receive(:call) { |mysql_table, values|
753
+ expect(mysql_table.table_name).to eq('users_login')
754
+ expect(values).to eq(expected_values)
755
+ nil
756
+ }.once
757
+ end
758
+ expect(insert_record_block).to receive(:call).never
759
+
760
+ # check_point_block
761
+ [
762
+ {state: Flydata::Mysql::MysqlDumpParser::State::CREATE_TABLE,
763
+ last_pos: index_after(dump_content_all, 'UNLOCK TABLES;')}
764
+ ].each do |expected_params|
765
+ expect(check_point_block).to receive(:call) { |mysql_table, last_pos, binlog_pos, state, substate|
766
+ expect(mysql_table.table_name).to eq('users_login') if mysql_table
767
+ expect(state).to eq(expected_params[:state])
768
+ if expected_params[:last_pos]
769
+ expect(last_pos).to eq(expected_params[:last_pos])
770
+ end
771
+ expect(binlog_pos).to eq(default_binlog_pos)
772
+ expect(substate).to eq(expected_params[:substate])
773
+ expected_params[:result]
774
+ }.once
775
+ end
776
+ expect(check_point_block).to receive(:call).never
777
+
778
+ binlog_pos = parser_for_resume.parse(
779
+ create_table_block, insert_record_block, check_point_block
780
+ )
781
+ expect(binlog_pos).to eq(default_binlog_pos)
782
+ end
783
+ end
784
+
785
+ context 'with resume during insert records' do
786
+ let(:dump_content) { <<EOT
787
+ #{DUMP_HEADER}
788
+
789
+ DROP TABLE IF EXISTS `users_login`;
790
+ /*!40101 SET @saved_cs_client = @@character_set_client */;
791
+ /*!40101 SET character_set_client = utf8 */;
792
+ CREATE TABLE `users_login` (
793
+ `user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'users id',
794
+ `login_count` int(10) unsigned DEFAULT '0' COMMENT 'login count',
795
+ `comment` varchar(255) DEFAULT NULL COMMENT 'comment',
796
+ `create_time` datetime NOT NULL COMMENT 'create time',
797
+ `update_time` datetime NOT NULL COMMENT 'update time',
798
+ PRIMARY KEY (`user_id`)
799
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
800
+ /*!40101 SET character_set_client = @saved_cs_client */;
801
+
802
+ --
803
+ -- Dumping data for table `users_login`
804
+ --
805
+
806
+ LOCK TABLES `users_login` WRITE;
807
+ /*!40000 ALTER TABLE `users_login` DISABLE KEYS */;
808
+ INSERT INTO `users_login` VALUES (15,46,'moodier','2001-10-02 08:18:08','1986-06-11 22:50:10'),(35,6,'missteer','1991-10-15 19:38:07','1970-10-01 22:03:10'),(52,33,'subfield','1972-08-23 20:16:08','1974-10-10 23:28:11');
809
+ INSERT INTO `users_login` VALUES (194,11,'pandemonium','2008-01-22 22:15:10','1991-04-04 17:30:05'),(230,7,'cloudburst','2010-12-28 11:46:11','1971-06-22 13:08:01');
810
+ /*!40000 ALTER TABLE `users_login` ENABLE KEYS */;
811
+ UNLOCK TABLES;
812
+ /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
813
+ EOT
814
+ }
815
+ before do
816
+ generate_dump_file(dump_content)
817
+
818
+ insert_record_block_for_resume = double('insert_record_block_for_resume')
819
+ expect(insert_record_block_for_resume).to receive(:call) { true }.once
820
+ expect(insert_record_block_for_resume).to receive(:call) { false }.once
821
+
822
+ default_parser.parse(
823
+ Proc.new{},
824
+ insert_record_block_for_resume,
825
+ Proc.new{ |mysql_table, last_pos, binlog_pos, state, substate|
826
+ if last_pos == index_after(dump_content, "'1972-08-23 20:16:08','1974-10-10 23:28:11');")
827
+ @mysql_table = mysql_table
828
+ @option = { status: Flydata::Command::Sync::STATUS_PARSING,
829
+ table_name: mysql_table.table_name,
830
+ last_pos: last_pos,
831
+ binlog_pos: binlog_pos,
832
+ state: state,
833
+ substate: substate,
834
+ mysql_table: mysql_table }
835
+ end
836
+ }
837
+ )
838
+ expect(@option).to eq({
839
+ status: Flydata::Command::Sync::STATUS_PARSING,
840
+ table_name: 'users_login',
841
+ last_pos: index_after(dump_content, "'1972-08-23 20:16:08','1974-10-10 23:28:11');"),
842
+ binlog_pos: default_binlog_pos,
843
+ state: Flydata::Mysql::MysqlDumpParser::State::INSERT_RECORD,
844
+ substate: nil,
845
+ mysql_table: @mysql_table
846
+ })
847
+ end
848
+ it do
849
+ generate_dump_file(dump_content)
850
+ parser_for_resume = MysqlDumpParser.new(file_path, @option)
851
+
852
+ # create_table_block
853
+ expect(create_table_block).to receive(:call).never
854
+
855
+ # insert_record_block
856
+ [
857
+ [ %w(194 11 pandemonium 2008-01-22\ 22:15:10 1991-04-04\ 17:30:05),
858
+ %w(230 7 cloudburst 2010-12-28\ 11:46:11 1971-06-22\ 13:08:01),],
859
+ ].each do |expected_values|
860
+ expect(insert_record_block).to receive(:call) { |mysql_table, values_set|
861
+ expect(mysql_table.table_name).to eq('users_login')
862
+ expect(values_set).to eq(expected_values)
863
+ nil
864
+ }.once
865
+ end
866
+ expect(insert_record_block).to receive(:call).never
867
+
868
+ # check_point_block
869
+ [
870
+ {state: Flydata::Mysql::MysqlDumpParser::State::CREATE_TABLE,
871
+ last_pos: index_after(dump_content, 'UNLOCK TABLES;')}
872
+ ].each do |expected_params|
873
+ expect(check_point_block).to receive(:call) { |mysql_table, last_pos, binlog_pos, state, substate|
874
+ expect(mysql_table.table_name).to eq('users_login') if mysql_table
875
+ expect(state).to eq(expected_params[:state])
876
+ if expected_params[:last_pos]
877
+ expect(last_pos).to eq(expected_params[:last_pos])
878
+ end
879
+ expect(binlog_pos).to eq(default_binlog_pos)
880
+ expect(substate).to eq(expected_params[:substate])
881
+ expected_params[:result]
882
+ }.once
883
+ end
884
+ expect(check_point_block).to receive(:call).never
885
+
886
+ binlog_pos = parser_for_resume.parse(
887
+ create_table_block, insert_record_block, check_point_block
888
+ )
889
+ expect(binlog_pos).to eq(default_binlog_pos)
890
+ end
891
+ end
892
+
893
+ context 'when dump contains contain escape characters' do
894
+ let(:dump_content) { <<EOT
895
+ #{DUMP_HEADER}
896
+
897
+ DROP TABLE IF EXISTS `users_login`;
898
+ /*!40101 SET @saved_cs_client = @@character_set_client */;
899
+ /*!40101 SET character_set_client = utf8 */;
900
+ CREATE TABLE `users_login` (
901
+ `user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'users id',
902
+ `login_count` int(10) unsigned DEFAULT '0' COMMENT 'login count',
903
+ `comment` varchar(255) DEFAULT NULL COMMENT 'comment',
904
+ `create_time` datetime NOT NULL COMMENT 'create time',
905
+ `update_time` datetime NOT NULL COMMENT 'update time',
906
+ PRIMARY KEY (`user_id`)
907
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
908
+ /*!40101 SET character_set_client = @saved_cs_client */;
909
+
910
+ --
911
+ -- Dumping data for table `users_login`
912
+ --
913
+
914
+ LOCK TABLES `users_login` WRITE;
915
+ /*!40000 ALTER TABLE `users_login` DISABLE KEYS */;
916
+ INSERT INTO `users_login` VALUES (15,46,'moodier)','2001-10-02 08:18:08','1986-06-11 22:50:10'),(35,6,'miss,teer','1991-10-15 19:38:07','1970-10-01 22:03:10'),(52,33,'subfield\\'','1972-08-23 20:16:08','1974-10-10 23:28:11');
917
+ INSERT INTO `users_login` VALUES (373,31,'outs\\nwearing','1979-10-07 08:10:08','2006-02-22 16:26:04'),(493,8,'schiz\tophrenic','1979-07-06 07:34:07\\'),','1970-08-09,01:21:01,\\')');
918
+ /*!40000 ALTER TABLE `users_login` ENABLE KEYS */;
919
+ UNLOCK TABLES;
920
+ /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
921
+
922
+ EOT
923
+ }
924
+ before { generate_dump_file(dump_content) }
925
+ it do
926
+ # create_table_block
927
+ expect(create_table_block).to receive(:call) { |mysql_table|
928
+ expect(mysql_table.table_name).to eq('users_login')
929
+ expect(mysql_table.columns.keys).to eq(['user_id', 'login_count', 'comment', 'create_time', 'update_time'])
930
+ }.once
931
+ expect(create_table_block).to receive(:call).never
932
+
933
+ # insert_record_block
934
+ [
935
+ [ %w(15 46 moodier\) 2001-10-02\ 08:18:08 1986-06-11\ 22:50:10),
936
+ %w(35 6 miss,teer 1991-10-15\ 19:38:07 1970-10-01\ 22:03:10),
937
+ %w(52 33 subfield' 1972-08-23\ 20:16:08 1974-10-10\ 23:28:11),],
938
+ [ ['373','31',"outs\nwearing",'1979-10-07 08:10:08','2006-02-22 16:26:04'],
939
+ ['493','8',"schiz\tophrenic",'1979-07-06 07:34:07\'),','1970-08-09,01:21:01,\')'] ]
940
+ ].each do |expected_values|
941
+ expect(insert_record_block).to receive(:call) { |mysql_table, values_set|
942
+ expect(mysql_table.table_name).to eq('users_login')
943
+ expect(values_set).to eq(expected_values)
944
+ nil
945
+ }.once
946
+ end
947
+ expect(insert_record_block).to receive(:call).never
948
+
949
+ # insert_record_block
950
+ [
951
+ {state: Flydata::Mysql::MysqlDumpParser::State::CREATE_TABLE},
952
+ {state: Flydata::Mysql::MysqlDumpParser::State::INSERT_RECORD},
953
+ {state: Flydata::Mysql::MysqlDumpParser::State::CREATE_TABLE,
954
+ last_pos: index_after(dump_content, 'UNLOCK TABLES;')}
955
+ ].each do |expected_params|
956
+ expect(check_point_block).to receive(:call) { |mysql_table, last_pos, binlog_pos, state, substate|
957
+ expect(mysql_table.table_name).to eq('users_login') if mysql_table
958
+ expect(state).to eq(expected_params[:state])
959
+ if expected_params[:last_pos]
960
+ expect(last_pos).to eq(expected_params[:last_pos])
961
+ end
962
+ expect(binlog_pos).to eq(default_binlog_pos)
963
+ expect(substate).to eq(expected_params[:substate])
964
+ expected_params[:result]
965
+ }.once
966
+ end
967
+ expect(check_point_block).to receive(:call).never
968
+
969
+ binlog_pos = default_parser.parse(
970
+ create_table_block, insert_record_block, check_point_block
971
+ )
972
+ expect(binlog_pos).to eq(default_binlog_pos)
973
+ end
974
+ end
975
+ end
976
+ end
977
+ describe MysqlDumpParser::InsertParser do
978
+ describe '#parse' do
979
+ let(:target_line) { '' }
980
+ let(:parser) { MysqlDumpParser::InsertParser.new(StringIO.new(target_line)) }
981
+ subject { parser.parse }
982
+
983
+ context 'when single record' do
984
+ let(:target_line) { "INSERT INTO `test_table` VALUES (1,'hogehoge','2014-04-15 13:49:14');" }
985
+ it 'should return one element array' do
986
+ expect(subject).to eq([['1','hogehoge','2014-04-15 13:49:14']])
987
+ end
988
+ end
989
+
990
+ context 'when 2 records' do
991
+ let(:target_line) { "INSERT INTO `test_table` VALUES (1,'foo','2014-04-15 13:49:14'),(2,'var','2014-04-15 14:21:05');" }
992
+ it 'should return two element array' do
993
+ expect(subject).to eq([['1','foo','2014-04-15 13:49:14'],['2','var','2014-04-15 14:21:05']])
994
+ end
995
+ end
996
+
997
+ context 'when data includes carriage return' do
998
+ let(:target_line) { "INSERT INTO `test_table` VALUES (1,'abcd\\refgh','2014-04-15 13:49:14');" }
999
+ it 'should escape return carriage' do
1000
+ expect(subject).to eq([['1',"abcd\refgh",'2014-04-15 13:49:14']])
1001
+ end
1002
+ end
1003
+
1004
+ context 'when data includes new line' do
1005
+ let(:target_line) { "INSERT INTO `test_table` VALUES (1,'abcd\\nefgh','2014-04-15 13:49:14');" }
1006
+ it 'should escape new line' do
1007
+ expect(subject).to eq([['1',"abcd\nefgh",'2014-04-15 13:49:14']])
1008
+ end
1009
+ end
1010
+
1011
+ context 'when data includes escaped single quotation' do
1012
+ let(:target_line) { "INSERT INTO `test_table` VALUES (1,'abcd\\',\\'efgh','2014-04-15 13:49:14');" }
1013
+ it 'should escape single quotation' do
1014
+ expect(subject).to eq([['1',"abcd','efgh",'2014-04-15 13:49:14']])
1015
+ end
1016
+ end
1017
+
1018
+ context 'when data includes back slash' do
1019
+ let(:target_line) { "INSERT INTO `test_table` VALUES (1,'abcd\\\\efgh','2014-04-15 13:49:14');" }
1020
+ it 'should escape back slash' do
1021
+ expect(subject).to eq([['1',"abcd\\efgh",'2014-04-15 13:49:14']])
1022
+ end
1023
+ end
1024
+
1025
+ context 'when data includes mixed escaped characters' do
1026
+ let(:target_line) { "INSERT INTO `test_table` VALUES (1,'ab\\rcd\\\\e\\nf\\',\\'gh','2014-04-15 13:49:14');" }
1027
+ it 'should escape all' do
1028
+ expect(subject).to eq([['1',"ab\rcd\\e\nf','gh",'2014-04-15 13:49:14']])
1029
+ end
1030
+ end
1031
+
1032
+ context 'when data includes mixed escaped characters in a row' do
1033
+ let(:target_line) { "INSERT INTO `test_table` VALUES (1,'ab\\\\ncd\\\\\\nefgh','2014-04-15 13:49:14');" }
1034
+ it 'should escape all' do
1035
+ expect(subject).to eq([['1',"ab\\ncd\\\nefgh",'2014-04-15 13:49:14']])
1036
+ end
1037
+ end
1038
+
1039
+ context 'when data end with back slash' do
1040
+ let(:target_line) { "INSERT INTO `test_table` VALUES (1,'D:\\\\download\\\\','2014-04-15 13:49:14');" }
1041
+ it 'should escape back slash' do
1042
+ expect(subject).to eq([['1',"D:\\download\\",'2014-04-15 13:49:14']])
1043
+ end
1044
+ end
1045
+ end
1046
+ end
1047
+ end
1048
+
1049
+ end