flydata 0.2.8 → 0.2.9
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 +4 -4
- data/VERSION +1 -1
- data/flydata.gemspec +9 -5
- data/lib/flydata/command/sync.rb +89 -988
- data/lib/flydata/fluent-plugins/in_mysql_binlog_flydata.rb +6 -1
- data/lib/flydata/helpers.rb +11 -0
- data/lib/flydata/output/forwarder.rb +166 -0
- data/lib/flydata/parser/mysql/dump_parser.rb +729 -0
- data/lib/flydata/parser/mysql/mysql_alter_table.treetop +214 -2
- data/lib/flydata/sync_file_manager.rb +1 -1
- data/lib/flydata/table_def/mysql_table_def.rb +61 -47
- data/lib/flydata/table_def/redshift_table_def.rb +30 -26
- data/spec/flydata/command/sync_spec.rb +0 -1160
- data/spec/flydata/output/forwarder_spec.rb +105 -0
- data/spec/flydata/parser/mysql/alter_table_parser_spec.rb +224 -23
- data/spec/flydata/parser/mysql/dump_parser_spec.rb +900 -0
- data/spec/flydata/sync_file_manager_spec.rb +159 -0
- data/spec/flydata/table_def/mysql_table_def_spec.rb +2 -2
- data/spec/flydata/table_def/redshift_table_def_spec.rb +199 -44
- metadata +8 -3
@@ -0,0 +1,105 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module Flydata
|
5
|
+
module Output
|
6
|
+
describe ForwarderFactory do
|
7
|
+
let(:forwarder) do
|
8
|
+
ForwarderFactory.create('tcpforwarder', 'test_tag', ['localhost:3456', 'localhost:4567'])
|
9
|
+
end
|
10
|
+
before :each do
|
11
|
+
conn = double(TCPSocket)
|
12
|
+
allow(conn).to receive(:setsockopt)
|
13
|
+
allow(conn).to receive(:write)
|
14
|
+
allow(conn).to receive(:close)
|
15
|
+
allow(TCPSocket).to receive(:new).and_return(conn)
|
16
|
+
allow(StringIO).to receive(:open)
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '.create' do
|
20
|
+
context 'with nil forwarder_key' do
|
21
|
+
it 'should return TcpForwarder object' do
|
22
|
+
forwarder = ForwarderFactory.create(nil, 'test_tag', ['localhost:3456', 'localhost:4567'])
|
23
|
+
expect(forwarder.kind_of?(TcpForwarder)).to be_truthy
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'with tcpforwarder forwarder_key' do
|
28
|
+
it 'should return TcpForwarder object' do
|
29
|
+
forwarder = ForwarderFactory.create('tcpforwarder', 'test_tag', ['localhost:3456', 'localhost:4567'])
|
30
|
+
expect(forwarder.kind_of?(TcpForwarder)).to be_truthy
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'with sslforwarder forwarder_key' do
|
35
|
+
it 'should return SslForwarder object' do
|
36
|
+
forwarder = ForwarderFactory.create('sslforwarder', 'test_tag', ['localhost:3456', 'localhost:4567'])
|
37
|
+
expect(forwarder.kind_of?(SslForwarder)).to be_truthy
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#initialize' do
|
43
|
+
context 'servers is empty' do
|
44
|
+
it do
|
45
|
+
expect{ForwarderFactory.create('tcpforwarder', 'test_tag', [])}.to raise_error
|
46
|
+
end
|
47
|
+
end
|
48
|
+
context 'servers is nil' do
|
49
|
+
it do
|
50
|
+
expect{ForwarderFactory.create('tcpforwarder', 'test_tag', nil)}.to raise_error
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe '#emit' do
|
56
|
+
let(:record) { {table_name: 'test_table_name', log: '{"key":"value"}'} }
|
57
|
+
before :each do
|
58
|
+
forwarder.set_options({buffer_size_limit: ([Time.now.to_i,record].to_msgpack.bytesize * 2.5)})
|
59
|
+
end
|
60
|
+
context 'when the buffer size is less than threthold' do
|
61
|
+
it do
|
62
|
+
expect(forwarder.emit(record)).to be(false)
|
63
|
+
expect(forwarder.buffer_record_count).to be(1)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
context 'when the buffer size exceeds threthold' do
|
67
|
+
it do
|
68
|
+
expect(forwarder.emit(record)).to be(false)
|
69
|
+
expect(forwarder.emit(record)).to be(false)
|
70
|
+
expect(forwarder.buffer_record_count).to be(2)
|
71
|
+
expect(forwarder.emit(record)).to be(true)
|
72
|
+
expect(forwarder.buffer_record_count).to be(0)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe '#pickup_server' do
|
78
|
+
context 'with only one server' do
|
79
|
+
let(:servers) { ['localhost:1111'] }
|
80
|
+
let(:forwarder) {
|
81
|
+
ForwarderFactory.create('tcpforwarder', 'test_tag', servers)
|
82
|
+
}
|
83
|
+
it 'expect to return same server' do
|
84
|
+
expect(forwarder.pickup_server).to eq('localhost:1111')
|
85
|
+
expect(forwarder.pickup_server).to eq('localhost:1111')
|
86
|
+
expect(forwarder.pickup_server).to eq('localhost:1111')
|
87
|
+
end
|
88
|
+
end
|
89
|
+
context 'with servers' do
|
90
|
+
let(:servers) { ['localhost:1111', 'localhost:2222', 'localhost:3333'] }
|
91
|
+
let(:forwarder) {
|
92
|
+
ForwarderFactory.create('tcpforwarder', 'test_tag', servers)
|
93
|
+
}
|
94
|
+
it 'expect to return same server' do
|
95
|
+
expect(forwarder.pickup_server).to eq('localhost:1111')
|
96
|
+
expect(forwarder.pickup_server).to eq('localhost:2222')
|
97
|
+
expect(forwarder.pickup_server).to eq('localhost:3333')
|
98
|
+
expect(forwarder.pickup_server).to eq('localhost:1111')
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
@@ -556,6 +556,11 @@ describe 'MysqlAlterTableParser' do
|
|
556
556
|
})
|
557
557
|
end
|
558
558
|
end
|
559
|
+
shared_examples "a parser raising an error" do
|
560
|
+
it do
|
561
|
+
expect { subject }.to raise_error
|
562
|
+
end
|
563
|
+
end
|
559
564
|
shared_examples "a parser parsing a nonbreaking query" do
|
560
565
|
let(:action_hash) {
|
561
566
|
{
|
@@ -710,23 +715,36 @@ describe 'MysqlAlterTableParser' do
|
|
710
715
|
end
|
711
716
|
end
|
712
717
|
end
|
713
|
-
shared_examples "test
|
714
|
-
|
715
|
-
|
716
|
-
let(:field_ident) { " #{ident}" }
|
717
|
-
end
|
718
|
+
shared_examples "test identifier combination without ." do |*examples|
|
719
|
+
it_behaves_like "test identifier", *examples do
|
720
|
+
let(:identifier) { " #{ident}" }
|
718
721
|
end
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
722
|
+
end
|
723
|
+
shared_examples "test identifier combination with single ." do |*examples|
|
724
|
+
it_behaves_like "test identifier", *examples do
|
725
|
+
let(:identifier) { " #{ident}.#{ident}" }
|
723
726
|
end
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
727
|
+
end
|
728
|
+
shared_examples "test identifier combination with single . starting with ." do |*examples|
|
729
|
+
it_behaves_like "test identifier", *examples do
|
730
|
+
let(:identifier) { " .#{ident}" }
|
731
|
+
end
|
732
|
+
end
|
733
|
+
shared_examples "test identifier combination with two ." do |*examples|
|
734
|
+
it_behaves_like "test identifier", *examples do
|
735
|
+
let(:identifier) { " #{ident}.#{ident}.#{ident}" }
|
736
|
+
end
|
737
|
+
end
|
738
|
+
shared_examples "test identifier combination with two . starting with ." do |*examples|
|
739
|
+
it_behaves_like "test identifier", *examples do
|
740
|
+
let(:identifier) { " .#{ident}.#{ident}" }
|
728
741
|
end
|
729
742
|
end
|
743
|
+
shared_examples "test field identifier" do |*examples|
|
744
|
+
it_behaves_like "test identifier combination without .", *examples
|
745
|
+
it_behaves_like "test identifier combination with single .", *examples
|
746
|
+
it_behaves_like "test identifier combination with two .", *examples
|
747
|
+
end
|
730
748
|
shared_examples "test identifier" do |*examples|
|
731
749
|
context "unquoted identifier" do
|
732
750
|
it_behaves_like *examples do
|
@@ -739,7 +757,109 @@ describe 'MysqlAlterTableParser' do
|
|
739
757
|
end
|
740
758
|
end
|
741
759
|
end
|
742
|
-
|
760
|
+
shared_examples "test optional to" do |*examples|
|
761
|
+
context "with to" do
|
762
|
+
it_behaves_like *examples do
|
763
|
+
let(:opt_to) {" TO "}
|
764
|
+
end
|
765
|
+
end
|
766
|
+
context "with =" do
|
767
|
+
it_behaves_like *examples do
|
768
|
+
let(:opt_to) {" = "}
|
769
|
+
end
|
770
|
+
end
|
771
|
+
context "with = without spaces" do
|
772
|
+
it_behaves_like *examples do
|
773
|
+
let(:opt_to) {"="}
|
774
|
+
end
|
775
|
+
end
|
776
|
+
context "with AS" do
|
777
|
+
it_behaves_like *examples do
|
778
|
+
let(:opt_to) {" as "}
|
779
|
+
end
|
780
|
+
end
|
781
|
+
context "with none" do
|
782
|
+
it_behaves_like *examples do
|
783
|
+
let(:opt_to) { "" }
|
784
|
+
end
|
785
|
+
end
|
786
|
+
end
|
787
|
+
shared_examples "test table identifier" do |*examples|
|
788
|
+
it_behaves_like "test identifier combination without .", *examples
|
789
|
+
it_behaves_like "test identifier combination with single .", *examples
|
790
|
+
it_behaves_like "test identifier combination with single . starting with .", *examples
|
791
|
+
end
|
792
|
+
shared_examples "test simple identifier" do |*examples|
|
793
|
+
it_behaves_like "test identifier combination without .", *examples
|
794
|
+
it_behaves_like "test identifier combination with single .", *examples
|
795
|
+
it_behaves_like "test identifier combination with two .", *examples
|
796
|
+
it_behaves_like "test identifier combination with two . starting with .", *examples
|
797
|
+
end
|
798
|
+
shared_examples "test order" do |*examples|
|
799
|
+
context "ascending" do
|
800
|
+
it_behaves_like *examples do
|
801
|
+
let(:order){" ASC"}
|
802
|
+
end
|
803
|
+
end
|
804
|
+
context "descending" do
|
805
|
+
it_behaves_like *examples do
|
806
|
+
let(:order){" desc"}
|
807
|
+
end
|
808
|
+
end
|
809
|
+
context "no order" do
|
810
|
+
it_behaves_like *examples do
|
811
|
+
let(:order){""}
|
812
|
+
end
|
813
|
+
end
|
814
|
+
end
|
815
|
+
shared_examples "test charset" do |*examples|
|
816
|
+
["CHARACTER SET", "charset"].each do |charset|
|
817
|
+
context "charset is #{charset}" do
|
818
|
+
it_behaves_like *examples do
|
819
|
+
let(:charset){"#{charset}"}
|
820
|
+
end
|
821
|
+
end
|
822
|
+
end
|
823
|
+
end
|
824
|
+
shared_examples "test charsetname" do |*examples|
|
825
|
+
["default","binary","utf8","latin1"].each do |charsetname|
|
826
|
+
context "charsetname is #{charsetname}" do
|
827
|
+
it_behaves_like *examples do
|
828
|
+
let(:charsetname){"#{charsetname}"}
|
829
|
+
end
|
830
|
+
end
|
831
|
+
end
|
832
|
+
end
|
833
|
+
shared_examples "test collate" do |*examples|
|
834
|
+
["collate default", "collate utf8_general_ci", "collate latin1_bin"].each do |collate|
|
835
|
+
context "collate is #{collate}" do
|
836
|
+
it_behaves_like *examples do
|
837
|
+
let(:collate){"#{collate}"}
|
838
|
+
end
|
839
|
+
end
|
840
|
+
end
|
841
|
+
end
|
842
|
+
shared_examples "test collate with equals" do |*examples|
|
843
|
+
["collate=default", "collate = utf8_general_ci"].each do |collate|
|
844
|
+
context "collate is #{collate}" do
|
845
|
+
it_behaves_like *examples do
|
846
|
+
let(:collate){"#{collate}"}
|
847
|
+
end
|
848
|
+
end
|
849
|
+
end
|
850
|
+
end
|
851
|
+
shared_examples "test optional default" do |*examples|
|
852
|
+
context "with default" do
|
853
|
+
it_behaves_like *examples do
|
854
|
+
let(:default){"default "}
|
855
|
+
end
|
856
|
+
end
|
857
|
+
context "without default" do
|
858
|
+
it_behaves_like *examples do
|
859
|
+
let(:default){""}
|
860
|
+
end
|
861
|
+
end
|
862
|
+
end
|
743
863
|
context 'add index' do
|
744
864
|
let(:action) { :add_index }
|
745
865
|
context "with a simple add index" do
|
@@ -911,13 +1031,12 @@ describe 'MysqlAlterTableParser' do
|
|
911
1031
|
context "enable keys" do
|
912
1032
|
let(:alter_table_action) { "ENABLE KEYS" }
|
913
1033
|
let(:action) { :enable_keys }
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
}
|
1034
|
+
it_behaves_like "a parser parsing a nonbreaking query"
|
1035
|
+
end
|
1036
|
+
context "disable keys" do
|
1037
|
+
let(:alter_table_action) { "DISABLE KEYS" }
|
1038
|
+
let(:action) { :disable_keys }
|
1039
|
+
it_behaves_like "a parser parsing a nonbreaking query"
|
921
1040
|
end
|
922
1041
|
context "drop primary key" do
|
923
1042
|
let(:alter_table_action) { "DROP PRIMARY KEY" }
|
@@ -926,16 +1045,98 @@ describe 'MysqlAlterTableParser' do
|
|
926
1045
|
it_behaves_like "a parser parsing a breaking query"
|
927
1046
|
end
|
928
1047
|
context "drop foreign key" do
|
929
|
-
let(:alter_table_action) { "DROP FOREIGN KEY#{
|
1048
|
+
let(:alter_table_action) { "DROP FOREIGN KEY#{identifier}" }
|
930
1049
|
let(:action) { :drop_foreign_key }
|
931
1050
|
|
932
1051
|
it_behaves_like "test field identifier", "a parser parsing a nonbreaking query"
|
933
1052
|
end
|
934
1053
|
context "drop key or index" do
|
935
|
-
let(:alter_table_action) { "DROP#{key_or_index}#{
|
1054
|
+
let(:alter_table_action) { "DROP#{key_or_index}#{identifier}" }
|
936
1055
|
let(:action) { "drop_#{key_or_index.strip.downcase}".to_sym }
|
937
1056
|
|
938
1057
|
it_behaves_like "test key or index", "test field identifier", "a parser parsing a nonbreaking query"
|
939
1058
|
end
|
1059
|
+
context "rename table" do
|
1060
|
+
let(:alter_table_action) { "rename#{opt_to}#{identifier}" }
|
1061
|
+
let(:action) { :rename_table }
|
1062
|
+
|
1063
|
+
it_behaves_like "test optional to", "test table identifier", "a parser parsing a breaking query"
|
1064
|
+
context "exception cases" do
|
1065
|
+
context "with as followed by quoted identifier and no space in between" do
|
1066
|
+
let(:alter_table_action) { "rename as`test_ident1`" } #strange, but valid
|
1067
|
+
it_behaves_like "a parser parsing a breaking query"
|
1068
|
+
end
|
1069
|
+
context "with to followed by quoted identifier and no space in between" do
|
1070
|
+
let(:alter_table_action) { "rename to`test_ident1`" }
|
1071
|
+
it_behaves_like "a parser parsing a breaking query"
|
1072
|
+
end
|
1073
|
+
context "no space between rename and to" do
|
1074
|
+
let(:alter_table_action) { "renameto `test_ident1`" }
|
1075
|
+
it_behaves_like "a parser raising an error"
|
1076
|
+
end
|
1077
|
+
context "no space between to and table name" do
|
1078
|
+
let(:alter_table_action) { "rename totest_ident1" }
|
1079
|
+
describe "'totest_ident1' is treated as a table name" do
|
1080
|
+
it_behaves_like "a parser parsing a breaking query"
|
1081
|
+
end
|
1082
|
+
end
|
1083
|
+
end
|
1084
|
+
end
|
1085
|
+
context "order by" do
|
1086
|
+
let(:action) { :order_table }
|
1087
|
+
context "with single column" do
|
1088
|
+
let(:alter_table_action) { "order by#{identifier}#{order}" }
|
1089
|
+
it_behaves_like "test simple identifier", "test order", "a parser parsing a breaking query"
|
1090
|
+
end
|
1091
|
+
context "with multiple columns" do
|
1092
|
+
let(:alter_table_action) { "order by#{identifier}#{order},#{identifier}#{order},#{identifier}#{order}" }
|
1093
|
+
it_behaves_like "test simple identifier", "test order", "a parser parsing a breaking query"
|
1094
|
+
end
|
1095
|
+
end
|
1096
|
+
context "convert to charset" do
|
1097
|
+
let(:action) { :convert_charset }
|
1098
|
+
context "test with collate" do
|
1099
|
+
let(:alter_table_action) { "convert to #{charset} #{charsetname} #{collate}" }
|
1100
|
+
it_behaves_like "test charset", "test charsetname", "test collate", "a parser parsing a breaking query"
|
1101
|
+
end
|
1102
|
+
context "test without collate" do
|
1103
|
+
let(:alter_table_action) { "convert to #{charset} #{charsetname}" }
|
1104
|
+
it_behaves_like "test charset", "test charsetname", "a parser parsing a breaking query"
|
1105
|
+
end
|
1106
|
+
end
|
1107
|
+
context "default charset" do
|
1108
|
+
let(:action) { :default_charset }
|
1109
|
+
context "with collate" do
|
1110
|
+
let(:alter_table_action) { "#{default}#{charset}=#{charsetname} #{collate}" }
|
1111
|
+
it_behaves_like "test optional default", "test charset", "test charsetname", "test collate", "a parser parsing a breaking query"
|
1112
|
+
it_behaves_like "test optional default", "test charset", "test charsetname", "test collate with equals", "a parser parsing a breaking query"
|
1113
|
+
end
|
1114
|
+
context "without collate" do
|
1115
|
+
let(:alter_table_action) { "#{default}#{charset} #{charsetname}" }
|
1116
|
+
it_behaves_like "test optional default", "test charset", "test charsetname", "a parser parsing a breaking query"
|
1117
|
+
end
|
1118
|
+
context "without charset" do
|
1119
|
+
#The doc at http://dev.mysql.com/doc/refman/5.6/en/alter-table.html does not indicate that
|
1120
|
+
#charset is optional. But this works (tested on mysql 5.5.38)
|
1121
|
+
let(:alter_table_action) { "#{default}#{collate}" }
|
1122
|
+
it_behaves_like "test optional default","test collate", "a parser parsing a breaking query"
|
1123
|
+
it_behaves_like "test optional default","test collate with equals", "a parser parsing a breaking query"
|
1124
|
+
end
|
1125
|
+
end
|
1126
|
+
context "force" do
|
1127
|
+
let(:alter_table_action) { "force" }
|
1128
|
+
let(:action) { :force }
|
1129
|
+
it_behaves_like "a parser parsing a nonbreaking query"
|
1130
|
+
end
|
1131
|
+
context "discard tablespace" do
|
1132
|
+
let(:alter_table_action) { "discard tablespace" }
|
1133
|
+
let(:action) { :discard_tablespace }
|
1134
|
+
it_behaves_like "a parser parsing a breaking query"
|
1135
|
+
end
|
1136
|
+
context "import tablespace" do
|
1137
|
+
let(:alter_table_action) { "import tablespace" }
|
1138
|
+
let(:action) { :import_tablespace }
|
1139
|
+
it_behaves_like "a parser parsing a breaking query"
|
1140
|
+
end
|
940
1141
|
end
|
941
1142
|
end
|
@@ -0,0 +1,900 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module Flydata
|
5
|
+
module Parser
|
6
|
+
module Mysql
|
7
|
+
describe MysqlDumpGeneratorMasterData do
|
8
|
+
let(:stdout) { double(:stdout) }
|
9
|
+
let(:stderr) { double(:stderr) }
|
10
|
+
let(:status) { double(:status) }
|
11
|
+
let(:file_path) { File.join('/tmp', "flydata_sync_spec_mysqldump_#{Time.now.to_i}") }
|
12
|
+
let(:dump_io) { File.open(file_path, 'r', encoding: "utf-8") }
|
13
|
+
let(:default_conf) { {
|
14
|
+
'host' => 'localhost', 'port' => 3306, 'username' => 'admin',
|
15
|
+
'password' => 'pass', 'database' => 'dev', 'tables' => 'users,groups',
|
16
|
+
} }
|
17
|
+
let(:default_dump_generator) { MysqlDumpGeneratorMasterData.new(default_conf) }
|
18
|
+
|
19
|
+
describe '#initialize' do
|
20
|
+
context 'with password' do
|
21
|
+
subject { default_dump_generator.instance_variable_get(:@dump_cmd) }
|
22
|
+
it { is_expected.to eq('mysqldump --protocol=tcp -h localhost -P 3306 -uadmin -ppass --skip-lock-tables ' +
|
23
|
+
'--single-transaction --hex-blob --flush-logs --master-data=2 dev users groups') }
|
24
|
+
end
|
25
|
+
context 'without password' do
|
26
|
+
let (:dump_generator) do
|
27
|
+
MysqlDumpGeneratorMasterData.new(default_conf.merge({'password' => ''}))
|
28
|
+
end
|
29
|
+
subject { dump_generator.instance_variable_get(:@dump_cmd) }
|
30
|
+
it { is_expected.to eq('mysqldump --protocol=tcp -h localhost -P 3306 -uadmin --skip-lock-tables ' +
|
31
|
+
'--single-transaction --hex-blob --flush-logs --master-data=2 dev users groups') }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#dump' do
|
36
|
+
context 'when exit status is not 0' do
|
37
|
+
before do
|
38
|
+
`touch #{file_path}`
|
39
|
+
expect(status).to receive(:exitstatus).and_return 1
|
40
|
+
expect(Open3).to receive(:capture3).and_return(
|
41
|
+
['(dummy std out)', '(dummy std err)', status]
|
42
|
+
)
|
43
|
+
end
|
44
|
+
it do
|
45
|
+
expect{ default_dump_generator.dump(file_path) }.to raise_error
|
46
|
+
expect(File.exists?(file_path)).to be_falsey
|
47
|
+
end
|
48
|
+
end
|
49
|
+
context 'when exit status is 0 but no file' do
|
50
|
+
before do
|
51
|
+
expect(status).to receive(:exitstatus).and_return 0
|
52
|
+
expect(Open3).to receive(:capture3).and_return(
|
53
|
+
['(dummy std out)', '(dummy std err)', status]
|
54
|
+
)
|
55
|
+
end
|
56
|
+
it do
|
57
|
+
expect{ default_dump_generator.dump(file_path) }.to raise_error
|
58
|
+
expect(File.exists?(file_path)).to be_falsey
|
59
|
+
end
|
60
|
+
end
|
61
|
+
context 'when exit status is 0 but file size is 0' do
|
62
|
+
before do
|
63
|
+
`touch #{file_path}`
|
64
|
+
expect(status).to receive(:exitstatus).and_return 0
|
65
|
+
expect(Open3).to receive(:capture3).and_return(
|
66
|
+
['(dummy std out)', '(dummy std err)', status]
|
67
|
+
)
|
68
|
+
end
|
69
|
+
it do
|
70
|
+
expect{ default_dump_generator.dump(file_path) }.to raise_error
|
71
|
+
expect(File.exists?(file_path)).to be_truthy
|
72
|
+
end
|
73
|
+
end
|
74
|
+
context 'when exit status is 0' do
|
75
|
+
before do
|
76
|
+
`echo "something..." > #{file_path}`
|
77
|
+
expect(status).to receive(:exitstatus).and_return 0
|
78
|
+
expect(Open3).to receive(:capture3).and_return(
|
79
|
+
['(dummy std out)', '(dummy std err)', status]
|
80
|
+
)
|
81
|
+
end
|
82
|
+
it do
|
83
|
+
expect(default_dump_generator.dump(file_path)).to be_truthy
|
84
|
+
expect(File.exists?(file_path)).to be_truthy
|
85
|
+
end
|
86
|
+
end
|
87
|
+
after :each do
|
88
|
+
File.delete(file_path) if File.exists?(file_path)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe MysqlDumpParser do
|
94
|
+
let(:file_path) { File.join('/tmp', "flydata_sync_spec_mysqldump_parse_#{Time.now.to_i}") }
|
95
|
+
let(:dump_io) { File.open(file_path, 'r', encoding: "utf-8") }
|
96
|
+
let(:default_parser) { MysqlDumpParser.new }
|
97
|
+
|
98
|
+
def generate_dump_file(content)
|
99
|
+
File.open(file_path, 'w') {|f| f.write(content)}
|
100
|
+
end
|
101
|
+
|
102
|
+
after do
|
103
|
+
File.delete(file_path) if File.exists?(file_path)
|
104
|
+
end
|
105
|
+
|
106
|
+
describe '#parse' do
|
107
|
+
DUMP_HEADER = <<EOT
|
108
|
+
-- MySQL dump 10.13 Distrib 5.6.13, for osx10.9 (x86_64)
|
109
|
+
--
|
110
|
+
-- Host: localhost Database: sync_test
|
111
|
+
-- ------------------------------------------------------
|
112
|
+
-- Server version>--5.6.13-log
|
113
|
+
|
114
|
+
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
115
|
+
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
116
|
+
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
117
|
+
/*!40101 SET NAMES utf8 */;
|
118
|
+
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
119
|
+
/*!40103 SET TIME_ZONE='+00:00' */;
|
120
|
+
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
121
|
+
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
122
|
+
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
123
|
+
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
124
|
+
|
125
|
+
--
|
126
|
+
-- Position to start replication or point-in-time recovery from
|
127
|
+
--
|
128
|
+
|
129
|
+
-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000267', MASTER_LOG_POS=120;
|
130
|
+
EOT
|
131
|
+
|
132
|
+
def index_after(content, string)
|
133
|
+
content.index(string) + string.bytesize + 1
|
134
|
+
end
|
135
|
+
|
136
|
+
let(:default_parser) { MysqlDumpParser.new }
|
137
|
+
let(:default_binlog_pos) { {binfile: 'mysql-bin.000267', pos: 120 } }
|
138
|
+
let(:dump_pos_after_binlog_pos) { index_after(DUMP_HEADER, 'MASTER_LOG_POS=120;') }
|
139
|
+
|
140
|
+
let(:create_table_block) { double('create_table_block') }
|
141
|
+
let(:insert_record_block) { double('insert_record_block') }
|
142
|
+
let(:check_point_block) { double('check_point_block') }
|
143
|
+
|
144
|
+
before do
|
145
|
+
generate_dump_file('')
|
146
|
+
end
|
147
|
+
|
148
|
+
context 'when dump does not contain binlog pos' do
|
149
|
+
before { generate_dump_file('dummy content') }
|
150
|
+
it do
|
151
|
+
expect(create_table_block).to receive(:call).never
|
152
|
+
expect(insert_record_block).to receive(:call).never
|
153
|
+
expect(check_point_block).to receive(:call).never
|
154
|
+
binlog_pos = default_parser.parse(
|
155
|
+
dump_io, create_table_block, insert_record_block, check_point_block
|
156
|
+
)
|
157
|
+
expect(binlog_pos).to be_nil
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
context 'when dump contains only binlog pos' do
|
162
|
+
before { generate_dump_file(<<EOT
|
163
|
+
#{DUMP_HEADER}
|
164
|
+
EOT
|
165
|
+
) }
|
166
|
+
it do
|
167
|
+
expect(create_table_block).to receive(:call).never
|
168
|
+
expect(insert_record_block).to receive(:call).never
|
169
|
+
expect(check_point_block).to receive(:call).once
|
170
|
+
binlog_pos = default_parser.parse(
|
171
|
+
dump_io, create_table_block, insert_record_block, check_point_block
|
172
|
+
)
|
173
|
+
expect(binlog_pos).to eq(default_binlog_pos)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
context 'when dump contains create table without records' do
|
178
|
+
let(:dump_content) { <<EOT
|
179
|
+
#{DUMP_HEADER}
|
180
|
+
|
181
|
+
DROP TABLE IF EXISTS `users_login`;
|
182
|
+
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
183
|
+
/*!40101 SET character_set_client = utf8 */;
|
184
|
+
CREATE TABLE `users_login` (
|
185
|
+
`user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'users id',
|
186
|
+
`login_count` int(10) unsigned DEFAULT '0' COMMENT 'login count',
|
187
|
+
`create_time` datetime NOT NULL COMMENT 'create time',
|
188
|
+
`update_time` datetime NOT NULL COMMENT 'update time',
|
189
|
+
PRIMARY KEY (`user_id`)
|
190
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='';
|
191
|
+
/*!40101 SET character_set_client = @saved_cs_client */;
|
192
|
+
|
193
|
+
--
|
194
|
+
-- Dumping data for table `users_login`
|
195
|
+
--
|
196
|
+
|
197
|
+
LOCK TABLES `users_login` WRITE;
|
198
|
+
/*!40000 ALTER TABLE `users_login` DISABLE KEYS */;
|
199
|
+
/*!40000 ALTER TABLE `users_login` ENABLE KEYS */;
|
200
|
+
UNLOCK TABLES;
|
201
|
+
|
202
|
+
EOT
|
203
|
+
}
|
204
|
+
before { generate_dump_file(dump_content) }
|
205
|
+
it do
|
206
|
+
expect(create_table_block).to receive(:call) { |mysql_table|
|
207
|
+
expect(mysql_table.table_name).to eq('users_login')
|
208
|
+
}.once
|
209
|
+
expect(insert_record_block).to receive(:call).never
|
210
|
+
expect(check_point_block).to receive(:call) { |mysql_table, last_pos, bytesize, binlog_pos, state, substate|
|
211
|
+
expect(mysql_table).to be_nil
|
212
|
+
expect(last_pos).to eq(dump_pos_after_binlog_pos)
|
213
|
+
expect(binlog_pos).to eq(default_binlog_pos)
|
214
|
+
expect(state).to eq(MysqlDumpParser::State::CREATE_TABLE)
|
215
|
+
expect(substate).to be_nil
|
216
|
+
}
|
217
|
+
expect(check_point_block).to receive(:call) { |mysql_table, last_pos, bytesize, binlog_pos, state, substate|
|
218
|
+
expect(mysql_table.table_name).to eq('users_login')
|
219
|
+
expect(mysql_table.columns.keys).to eq(['user_id', 'login_count', 'create_time', 'update_time'])
|
220
|
+
expect(last_pos).to eq(index_after(dump_content, 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=\'\';'))
|
221
|
+
expect(binlog_pos).to eq(default_binlog_pos)
|
222
|
+
expect(state).to eq(MysqlDumpParser::State::INSERT_RECORD)
|
223
|
+
expect(substate).to be_nil
|
224
|
+
}
|
225
|
+
expect(check_point_block).to receive(:call) { |mysql_table, last_pos, bytesize, binlog_pos, state, substate|
|
226
|
+
expect(mysql_table.table_name).to eq('users_login')
|
227
|
+
expect(last_pos).to eq(index_after(dump_content, 'UNLOCK TABLES;'))
|
228
|
+
expect(binlog_pos).to eq(default_binlog_pos)
|
229
|
+
expect(state).to eq(MysqlDumpParser::State::CREATE_TABLE)
|
230
|
+
expect(substate).to be_nil
|
231
|
+
}
|
232
|
+
expect(check_point_block).to receive(:call).never
|
233
|
+
|
234
|
+
binlog_pos = default_parser.parse(
|
235
|
+
dump_io, create_table_block, insert_record_block, check_point_block
|
236
|
+
)
|
237
|
+
expect(binlog_pos).to eq(default_binlog_pos)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
context 'when dump contains create table with multi inserts' do
|
242
|
+
let(:dump_content) { <<EOT
|
243
|
+
#{DUMP_HEADER}
|
244
|
+
|
245
|
+
DROP TABLE IF EXISTS `users_login`;
|
246
|
+
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
247
|
+
/*!40101 SET character_set_client = utf8 */;
|
248
|
+
CREATE TABLE `users_login` (
|
249
|
+
`user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'users id',
|
250
|
+
`login_count` int(10) unsigned DEFAULT '0' COMMENT 'login count',
|
251
|
+
`comment` varchar(255) DEFAULT NULL COMMENT 'comment',
|
252
|
+
`create_time` datetime NOT NULL COMMENT 'create time',
|
253
|
+
`update_time` datetime NOT NULL COMMENT 'update time',
|
254
|
+
PRIMARY KEY (`user_id`)
|
255
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
256
|
+
/*!40101 SET character_set_client = @saved_cs_client */;
|
257
|
+
|
258
|
+
--
|
259
|
+
-- Dumping data for table `users_login`
|
260
|
+
--
|
261
|
+
|
262
|
+
LOCK TABLES `users_login` WRITE;
|
263
|
+
/*!40000 ALTER TABLE `users_login` DISABLE KEYS */;
|
264
|
+
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');
|
265
|
+
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');
|
266
|
+
/*!40000 ALTER TABLE `users_login` ENABLE KEYS */;
|
267
|
+
UNLOCK TABLES;
|
268
|
+
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
269
|
+
|
270
|
+
EOT
|
271
|
+
}
|
272
|
+
before { generate_dump_file(dump_content) }
|
273
|
+
it do
|
274
|
+
# create_table_block
|
275
|
+
expect(create_table_block).to receive(:call) { |mysql_table|
|
276
|
+
expect(mysql_table.table_name).to eq('users_login')
|
277
|
+
expect(mysql_table.columns.keys).to eq(['user_id', 'login_count', 'comment', 'create_time', 'update_time'])
|
278
|
+
}.once
|
279
|
+
expect(create_table_block).to receive(:call).never
|
280
|
+
|
281
|
+
# insert_record_block
|
282
|
+
[
|
283
|
+
[ %w(15 46 moodier 2001-10-02\ 08:18:08 1986-06-11\ 22:50:10),
|
284
|
+
%w(35 6 missあいうえおteer 1991-10-15\ 19:38:07 1970-10-01\ 22:03:10),
|
285
|
+
%w(52 33 sub\\field 1972-08-23\ 20:16:08 1974-10-10\ 23:28:11),],
|
286
|
+
[ %w(373 31 out'swearing 1979-10-07\ 08:10:08 2006-02-22\ 16:26:04),
|
287
|
+
%w(493 8 schizophrenic 1979-07-06\ 07:34:07 1970-08-09\ 01:21:01),]
|
288
|
+
].each do |expected_values|
|
289
|
+
expect(insert_record_block).to receive(:call) { |mysql_table, values_set|
|
290
|
+
expect(mysql_table.table_name).to eq('users_login')
|
291
|
+
expect(values_set).to eq(expected_values)
|
292
|
+
nil
|
293
|
+
}.once
|
294
|
+
end
|
295
|
+
expect(insert_record_block).to receive(:call).never
|
296
|
+
|
297
|
+
# insert_record_block
|
298
|
+
[
|
299
|
+
{state: MysqlDumpParser::State::CREATE_TABLE},
|
300
|
+
{state: MysqlDumpParser::State::INSERT_RECORD},
|
301
|
+
{state: MysqlDumpParser::State::CREATE_TABLE,
|
302
|
+
last_pos: index_after(dump_content, 'UNLOCK TABLES;') + 10}
|
303
|
+
].each do |expected_params|
|
304
|
+
expect(check_point_block).to receive(:call) { |mysql_table, last_pos, bytesize, binlog_pos, state, substate|
|
305
|
+
expect(mysql_table.table_name).to eq('users_login') if mysql_table
|
306
|
+
expect(state).to eq(expected_params[:state])
|
307
|
+
if expected_params[:last_pos]
|
308
|
+
expect(last_pos).to eq(expected_params[:last_pos])
|
309
|
+
end
|
310
|
+
expect(binlog_pos).to eq(default_binlog_pos)
|
311
|
+
expect(substate).to eq(expected_params[:substate])
|
312
|
+
expected_params[:result]
|
313
|
+
}.once
|
314
|
+
end
|
315
|
+
expect(check_point_block).to receive(:call).never
|
316
|
+
|
317
|
+
binlog_pos = default_parser.parse(
|
318
|
+
dump_io, create_table_block, insert_record_block, check_point_block
|
319
|
+
)
|
320
|
+
expect(binlog_pos).to eq(default_binlog_pos)
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
context 'when dump contains create table with records' do
|
325
|
+
let(:dump_content) { <<EOT
|
326
|
+
#{DUMP_HEADER}
|
327
|
+
|
328
|
+
DROP TABLE IF EXISTS `users_login`;
|
329
|
+
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
330
|
+
/*!40101 SET character_set_client = utf8 */;
|
331
|
+
CREATE TABLE `users_login` (
|
332
|
+
`user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'users id',
|
333
|
+
`login_count` int(10) unsigned DEFAULT '0' COMMENT 'login count',
|
334
|
+
`comment` varchar(255) DEFAULT NULL COMMENT 'comment',
|
335
|
+
`create_time` datetime NOT NULL COMMENT 'create time',
|
336
|
+
`update_time` datetime NOT NULL COMMENT 'update time',
|
337
|
+
PRIMARY KEY (`user_id`)
|
338
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
339
|
+
/*!40101 SET character_set_client = @saved_cs_client */;
|
340
|
+
|
341
|
+
--
|
342
|
+
-- Dumping data for table `users_login`
|
343
|
+
--
|
344
|
+
|
345
|
+
LOCK TABLES `users_login` WRITE;
|
346
|
+
/*!40000 ALTER TABLE `users_login` DISABLE KEYS */;
|
347
|
+
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');
|
348
|
+
/*!40000 ALTER TABLE `users_login` ENABLE KEYS */;
|
349
|
+
UNLOCK TABLES;
|
350
|
+
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
351
|
+
|
352
|
+
EOT
|
353
|
+
}
|
354
|
+
before { generate_dump_file(dump_content) }
|
355
|
+
it do
|
356
|
+
# create_table_block
|
357
|
+
expect(create_table_block).to receive(:call) { |mysql_table|
|
358
|
+
expect(mysql_table.table_name).to eq('users_login')
|
359
|
+
expect(mysql_table.columns.keys).to eq(['user_id', 'login_count', 'comment', 'create_time', 'update_time'])
|
360
|
+
}.once
|
361
|
+
expect(create_table_block).to receive(:call).never
|
362
|
+
|
363
|
+
# insert_record_block
|
364
|
+
[
|
365
|
+
[ %w(15 46 moodier 2001-10-02\ 08:18:08 1986-06-11\ 22:50:10),
|
366
|
+
['35', '6', nil, '1991-10-15 19:38:07', '1970-10-01 22:03:10'],
|
367
|
+
%w(52 33 subfield 1972-08-23\ 20:16:08 1974-10-10\ 23:28:11),],
|
368
|
+
].each do |expected_values|
|
369
|
+
expect(insert_record_block).to receive(:call) { |mysql_table, values_set|
|
370
|
+
expect(mysql_table.table_name).to eq('users_login')
|
371
|
+
expect(values_set).to eq(expected_values)
|
372
|
+
nil
|
373
|
+
}.once
|
374
|
+
end
|
375
|
+
expect(insert_record_block).to receive(:call).never
|
376
|
+
|
377
|
+
# insert_record_block
|
378
|
+
[
|
379
|
+
{state: MysqlDumpParser::State::CREATE_TABLE},
|
380
|
+
{state: MysqlDumpParser::State::INSERT_RECORD},
|
381
|
+
{state: MysqlDumpParser::State::CREATE_TABLE,
|
382
|
+
last_pos: index_after(dump_content, 'UNLOCK TABLES;')}
|
383
|
+
].each do |expected_params|
|
384
|
+
expect(check_point_block).to receive(:call) { |mysql_table, last_pos, bytesize, binlog_pos, state, substate|
|
385
|
+
expect(mysql_table.table_name).to eq('users_login') if mysql_table
|
386
|
+
expect(state).to eq(expected_params[:state])
|
387
|
+
if expected_params[:last_pos]
|
388
|
+
expect(last_pos).to eq(expected_params[:last_pos])
|
389
|
+
end
|
390
|
+
expect(binlog_pos).to eq(default_binlog_pos)
|
391
|
+
expect(substate).to eq(expected_params[:substate])
|
392
|
+
expected_params[:result]
|
393
|
+
}.once
|
394
|
+
end
|
395
|
+
expect(check_point_block).to receive(:call).never
|
396
|
+
|
397
|
+
binlog_pos = default_parser.parse(
|
398
|
+
dump_io, create_table_block, insert_record_block, check_point_block
|
399
|
+
)
|
400
|
+
expect(binlog_pos).to eq(default_binlog_pos)
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
context 'with resume after creating table' do
|
405
|
+
let(:dump_content_head) { <<EOT
|
406
|
+
#{DUMP_HEADER}
|
407
|
+
|
408
|
+
DROP TABLE IF EXISTS `users_login`;
|
409
|
+
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
410
|
+
/*!40101 SET character_set_client = utf8 */;
|
411
|
+
CREATE TABLE `users_login` (
|
412
|
+
`user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'users id',
|
413
|
+
`login_count` int(10) unsigned DEFAULT '0' COMMENT 'login count',
|
414
|
+
`comment` varchar(255) DEFAULT NULL COMMENT 'comment',
|
415
|
+
`create_time` datetime NOT NULL COMMENT 'create time',
|
416
|
+
`update_time` datetime NOT NULL COMMENT 'update time',
|
417
|
+
PRIMARY KEY (`user_id`)
|
418
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
419
|
+
/*!40101 SET character_set_client = @saved_cs_client */;
|
420
|
+
|
421
|
+
EOT
|
422
|
+
}
|
423
|
+
let(:dump_content_all) { <<EOT
|
424
|
+
#{dump_content_head}
|
425
|
+
--
|
426
|
+
-- Dumping data for table `users_login`
|
427
|
+
--
|
428
|
+
|
429
|
+
LOCK TABLES `users_login` WRITE;
|
430
|
+
/*!40000 ALTER TABLE `users_login` DISABLE KEYS */;
|
431
|
+
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');
|
432
|
+
/*!40000 ALTER TABLE `users_login` ENABLE KEYS */;
|
433
|
+
UNLOCK TABLES;
|
434
|
+
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
435
|
+
EOT
|
436
|
+
}
|
437
|
+
before do
|
438
|
+
generate_dump_file(dump_content_head)
|
439
|
+
default_parser.parse(
|
440
|
+
dump_io,
|
441
|
+
Proc.new{},
|
442
|
+
Proc.new{
|
443
|
+
raise "Should not be called"
|
444
|
+
},
|
445
|
+
Proc.new{ |mysql_table, last_pos, bytesize, binlog_pos, state, substate|
|
446
|
+
if mysql_table
|
447
|
+
@mysql_table = mysql_table
|
448
|
+
@option = { status: Flydata::Command::Sync::STATUS_PARSING,
|
449
|
+
table_name: mysql_table.table_name,
|
450
|
+
last_pos: last_pos,
|
451
|
+
binlog_pos: binlog_pos,
|
452
|
+
state: state,
|
453
|
+
substate: substate,
|
454
|
+
mysql_table: mysql_table }
|
455
|
+
end
|
456
|
+
}
|
457
|
+
)
|
458
|
+
expect(@option).to eq({
|
459
|
+
status: Flydata::Command::Sync::STATUS_PARSING,
|
460
|
+
table_name: 'users_login',
|
461
|
+
last_pos: index_after(dump_content_head, 'ENGINE=InnoDB DEFAULT CHARSET=utf8;'),
|
462
|
+
binlog_pos: default_binlog_pos,
|
463
|
+
state: MysqlDumpParser::State::INSERT_RECORD,
|
464
|
+
substate: nil,
|
465
|
+
mysql_table: @mysql_table
|
466
|
+
})
|
467
|
+
end
|
468
|
+
it do
|
469
|
+
generate_dump_file(dump_content_all)
|
470
|
+
parser_for_resume = MysqlDumpParser.new(@option)
|
471
|
+
|
472
|
+
# create_table_block
|
473
|
+
expect(create_table_block).to receive(:call).never
|
474
|
+
|
475
|
+
# insert_record_block
|
476
|
+
[
|
477
|
+
[ %w(15 46 moodier 2001-10-02\ 08:18:08 1986-06-11\ 22:50:10),
|
478
|
+
%w(35 6 missteer 1991-10-15\ 19:38:07 1970-10-01\ 22:03:10),
|
479
|
+
%w(52 33 subfield 1972-08-23\ 20:16:08 1974-10-10\ 23:28:11),],
|
480
|
+
].each do |expected_values|
|
481
|
+
expect(insert_record_block).to receive(:call) { |mysql_table, values|
|
482
|
+
expect(mysql_table.table_name).to eq('users_login')
|
483
|
+
expect(values).to eq(expected_values)
|
484
|
+
nil
|
485
|
+
}.once
|
486
|
+
end
|
487
|
+
expect(insert_record_block).to receive(:call).never
|
488
|
+
|
489
|
+
# check_point_block
|
490
|
+
[
|
491
|
+
{state: MysqlDumpParser::State::CREATE_TABLE,
|
492
|
+
last_pos: index_after(dump_content_all, 'UNLOCK TABLES;')}
|
493
|
+
].each do |expected_params|
|
494
|
+
expect(check_point_block).to receive(:call) { |mysql_table, last_pos, bytesize, binlog_pos, state, substate|
|
495
|
+
expect(mysql_table.table_name).to eq('users_login') if mysql_table
|
496
|
+
expect(state).to eq(expected_params[:state])
|
497
|
+
if expected_params[:last_pos]
|
498
|
+
expect(last_pos).to eq(expected_params[:last_pos])
|
499
|
+
end
|
500
|
+
expect(binlog_pos).to eq(default_binlog_pos)
|
501
|
+
expect(substate).to eq(expected_params[:substate])
|
502
|
+
expected_params[:result]
|
503
|
+
}.once
|
504
|
+
end
|
505
|
+
expect(check_point_block).to receive(:call).never
|
506
|
+
|
507
|
+
binlog_pos = parser_for_resume.parse(
|
508
|
+
dump_io, create_table_block, insert_record_block, check_point_block
|
509
|
+
)
|
510
|
+
expect(binlog_pos).to eq(default_binlog_pos)
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
context 'with resume during insert records' do
|
515
|
+
let(:dump_content) { <<EOT
|
516
|
+
#{DUMP_HEADER}
|
517
|
+
|
518
|
+
DROP TABLE IF EXISTS `users_login`;
|
519
|
+
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
520
|
+
/*!40101 SET character_set_client = utf8 */;
|
521
|
+
CREATE TABLE `users_login` (
|
522
|
+
`user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'users id',
|
523
|
+
`login_count` int(10) unsigned DEFAULT '0' COMMENT 'login count',
|
524
|
+
`comment` varchar(255) DEFAULT NULL COMMENT 'comment',
|
525
|
+
`create_time` datetime NOT NULL COMMENT 'create time',
|
526
|
+
`update_time` datetime NOT NULL COMMENT 'update time',
|
527
|
+
PRIMARY KEY (`user_id`)
|
528
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
529
|
+
/*!40101 SET character_set_client = @saved_cs_client */;
|
530
|
+
|
531
|
+
--
|
532
|
+
-- Dumping data for table `users_login`
|
533
|
+
--
|
534
|
+
|
535
|
+
LOCK TABLES `users_login` WRITE;
|
536
|
+
/*!40000 ALTER TABLE `users_login` DISABLE KEYS */;
|
537
|
+
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');
|
538
|
+
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');
|
539
|
+
/*!40000 ALTER TABLE `users_login` ENABLE KEYS */;
|
540
|
+
UNLOCK TABLES;
|
541
|
+
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
542
|
+
EOT
|
543
|
+
}
|
544
|
+
before do
|
545
|
+
generate_dump_file(dump_content)
|
546
|
+
|
547
|
+
insert_record_block_for_resume = double('insert_record_block_for_resume')
|
548
|
+
expect(insert_record_block_for_resume).to receive(:call) { true }.once
|
549
|
+
expect(insert_record_block_for_resume).to receive(:call) { false }.once
|
550
|
+
|
551
|
+
default_parser.parse(
|
552
|
+
dump_io,
|
553
|
+
Proc.new{},
|
554
|
+
insert_record_block_for_resume,
|
555
|
+
Proc.new{ |mysql_table, last_pos, bytesize, binlog_pos, state, substate|
|
556
|
+
if last_pos == index_after(dump_content, "'1972-08-23 20:16:08','1974-10-10 23:28:11');")
|
557
|
+
@mysql_table = mysql_table
|
558
|
+
@option = { status: Flydata::Command::Sync::STATUS_PARSING,
|
559
|
+
table_name: mysql_table.table_name,
|
560
|
+
last_pos: last_pos,
|
561
|
+
binlog_pos: binlog_pos,
|
562
|
+
state: state,
|
563
|
+
substate: substate,
|
564
|
+
mysql_table: mysql_table }
|
565
|
+
end
|
566
|
+
}
|
567
|
+
)
|
568
|
+
expect(@option).to eq({
|
569
|
+
status: Flydata::Command::Sync::STATUS_PARSING,
|
570
|
+
table_name: 'users_login',
|
571
|
+
last_pos: index_after(dump_content, "'1972-08-23 20:16:08','1974-10-10 23:28:11');"),
|
572
|
+
binlog_pos: default_binlog_pos,
|
573
|
+
state: MysqlDumpParser::State::INSERT_RECORD,
|
574
|
+
substate: nil,
|
575
|
+
mysql_table: @mysql_table
|
576
|
+
})
|
577
|
+
end
|
578
|
+
it do
|
579
|
+
generate_dump_file(dump_content)
|
580
|
+
parser_for_resume = MysqlDumpParser.new(@option)
|
581
|
+
|
582
|
+
# create_table_block
|
583
|
+
expect(create_table_block).to receive(:call).never
|
584
|
+
|
585
|
+
# insert_record_block
|
586
|
+
[
|
587
|
+
[ %w(194 11 pandemonium 2008-01-22\ 22:15:10 1991-04-04\ 17:30:05),
|
588
|
+
%w(230 7 cloudburst 2010-12-28\ 11:46:11 1971-06-22\ 13:08:01),],
|
589
|
+
].each do |expected_values|
|
590
|
+
expect(insert_record_block).to receive(:call) { |mysql_table, values_set|
|
591
|
+
expect(mysql_table.table_name).to eq('users_login')
|
592
|
+
expect(values_set).to eq(expected_values)
|
593
|
+
nil
|
594
|
+
}.once
|
595
|
+
end
|
596
|
+
expect(insert_record_block).to receive(:call).never
|
597
|
+
|
598
|
+
# check_point_block
|
599
|
+
[
|
600
|
+
{state: MysqlDumpParser::State::CREATE_TABLE,
|
601
|
+
last_pos: index_after(dump_content, 'UNLOCK TABLES;')}
|
602
|
+
].each do |expected_params|
|
603
|
+
expect(check_point_block).to receive(:call) { |mysql_table, last_pos, bytesize, binlog_pos, state, substate|
|
604
|
+
expect(mysql_table.table_name).to eq('users_login') if mysql_table
|
605
|
+
expect(state).to eq(expected_params[:state])
|
606
|
+
if expected_params[:last_pos]
|
607
|
+
expect(last_pos).to eq(expected_params[:last_pos])
|
608
|
+
end
|
609
|
+
expect(binlog_pos).to eq(default_binlog_pos)
|
610
|
+
expect(substate).to eq(expected_params[:substate])
|
611
|
+
expected_params[:result]
|
612
|
+
}.once
|
613
|
+
end
|
614
|
+
expect(check_point_block).to receive(:call).never
|
615
|
+
|
616
|
+
binlog_pos = parser_for_resume.parse(
|
617
|
+
dump_io, create_table_block, insert_record_block, check_point_block
|
618
|
+
)
|
619
|
+
expect(binlog_pos).to eq(default_binlog_pos)
|
620
|
+
end
|
621
|
+
end
|
622
|
+
|
623
|
+
context 'when dump contains contain escape characters' do
|
624
|
+
let(:dump_content) { <<EOT
|
625
|
+
#{DUMP_HEADER}
|
626
|
+
|
627
|
+
DROP TABLE IF EXISTS `users_login`;
|
628
|
+
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
629
|
+
/*!40101 SET character_set_client = utf8 */;
|
630
|
+
CREATE TABLE `users_login` (
|
631
|
+
`user_id` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'users id',
|
632
|
+
`login_count` int(10) unsigned DEFAULT '0' COMMENT 'login count',
|
633
|
+
`comment` varchar(255) DEFAULT NULL COMMENT 'comment',
|
634
|
+
`create_time` datetime NOT NULL COMMENT 'create time',
|
635
|
+
`update_time` datetime NOT NULL COMMENT 'update time',
|
636
|
+
PRIMARY KEY (`user_id`)
|
637
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
638
|
+
/*!40101 SET character_set_client = @saved_cs_client */;
|
639
|
+
|
640
|
+
--
|
641
|
+
-- Dumping data for table `users_login`
|
642
|
+
--
|
643
|
+
|
644
|
+
LOCK TABLES `users_login` WRITE;
|
645
|
+
/*!40000 ALTER TABLE `users_login` DISABLE KEYS */;
|
646
|
+
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');
|
647
|
+
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,\\')');
|
648
|
+
/*!40000 ALTER TABLE `users_login` ENABLE KEYS */;
|
649
|
+
UNLOCK TABLES;
|
650
|
+
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
651
|
+
|
652
|
+
EOT
|
653
|
+
}
|
654
|
+
before { generate_dump_file(dump_content) }
|
655
|
+
it do
|
656
|
+
# create_table_block
|
657
|
+
expect(create_table_block).to receive(:call) { |mysql_table|
|
658
|
+
expect(mysql_table.table_name).to eq('users_login')
|
659
|
+
expect(mysql_table.columns.keys).to eq(['user_id', 'login_count', 'comment', 'create_time', 'update_time'])
|
660
|
+
}.once
|
661
|
+
expect(create_table_block).to receive(:call).never
|
662
|
+
|
663
|
+
# insert_record_block
|
664
|
+
[
|
665
|
+
[ %w(15 46 moodier\) 2001-10-02\ 08:18:08 1986-06-11\ 22:50:10),
|
666
|
+
%w(35 6 miss,teer 1991-10-15\ 19:38:07 1970-10-01\ 22:03:10),
|
667
|
+
%w(52 33 subfield' 1972-08-23\ 20:16:08 1974-10-10\ 23:28:11),],
|
668
|
+
[ ['373','31',"outs\nwearing",'1979-10-07 08:10:08','2006-02-22 16:26:04'],
|
669
|
+
['493','8',"schiz\tophrenic",'1979-07-06 07:34:07\'),','1970-08-09,01:21:01,\')'] ]
|
670
|
+
].each do |expected_values|
|
671
|
+
expect(insert_record_block).to receive(:call) { |mysql_table, values_set|
|
672
|
+
expect(mysql_table.table_name).to eq('users_login')
|
673
|
+
expect(values_set).to eq(expected_values)
|
674
|
+
nil
|
675
|
+
}.once
|
676
|
+
end
|
677
|
+
expect(insert_record_block).to receive(:call).never
|
678
|
+
|
679
|
+
# insert_record_block
|
680
|
+
[
|
681
|
+
{state: MysqlDumpParser::State::CREATE_TABLE},
|
682
|
+
{state: MysqlDumpParser::State::INSERT_RECORD},
|
683
|
+
{state: MysqlDumpParser::State::CREATE_TABLE,
|
684
|
+
last_pos: index_after(dump_content, 'UNLOCK TABLES;')}
|
685
|
+
].each do |expected_params|
|
686
|
+
expect(check_point_block).to receive(:call) { |mysql_table, last_pos, bytesize, binlog_pos, state, substate|
|
687
|
+
expect(mysql_table.table_name).to eq('users_login') if mysql_table
|
688
|
+
expect(state).to eq(expected_params[:state])
|
689
|
+
if expected_params[:last_pos]
|
690
|
+
expect(last_pos).to eq(expected_params[:last_pos])
|
691
|
+
end
|
692
|
+
expect(binlog_pos).to eq(default_binlog_pos)
|
693
|
+
expect(substate).to eq(expected_params[:substate])
|
694
|
+
expected_params[:result]
|
695
|
+
}.once
|
696
|
+
end
|
697
|
+
expect(check_point_block).to receive(:call).never
|
698
|
+
|
699
|
+
binlog_pos = default_parser.parse(
|
700
|
+
dump_io, create_table_block, insert_record_block, check_point_block
|
701
|
+
)
|
702
|
+
expect(binlog_pos).to eq(default_binlog_pos)
|
703
|
+
end
|
704
|
+
end
|
705
|
+
end
|
706
|
+
end
|
707
|
+
describe MysqlDumpParser::InsertParser do
|
708
|
+
describe '#parse' do
|
709
|
+
let(:target_line) { '' }
|
710
|
+
let(:parser) { MysqlDumpParser::InsertParser.new }
|
711
|
+
subject { parser.parse(target_line) }
|
712
|
+
|
713
|
+
context 'when single record' do
|
714
|
+
let(:target_line) { "INSERT INTO `test_table` VALUES (1,'hogehoge','2014-04-15 13:49:14');" }
|
715
|
+
it 'should return one element array' do
|
716
|
+
expect(subject).to eq([['1','hogehoge','2014-04-15 13:49:14']])
|
717
|
+
end
|
718
|
+
end
|
719
|
+
|
720
|
+
context 'when 2 records' do
|
721
|
+
let(:target_line) { "INSERT INTO `test_table` VALUES (1,'foo','2014-04-15 13:49:14'),(2,'var','2014-04-15 14:21:05');" }
|
722
|
+
it 'should return two element array' do
|
723
|
+
expect(subject).to eq([['1','foo','2014-04-15 13:49:14'],['2','var','2014-04-15 14:21:05']])
|
724
|
+
end
|
725
|
+
end
|
726
|
+
|
727
|
+
context 'when data includes carriage return' do
|
728
|
+
let(:target_line) { %q|INSERT INTO `test_table` VALUES (1,'abcd\refgh','2014-04-15 13:49:14');| }
|
729
|
+
it 'should escape return carriage' do
|
730
|
+
expect(subject).to eq([['1',"abcd\refgh",'2014-04-15 13:49:14']])
|
731
|
+
end
|
732
|
+
end
|
733
|
+
|
734
|
+
context 'when data includes new line' do
|
735
|
+
let(:target_line) { %q|INSERT INTO `test_table` VALUES (1,'abcd\nefgh','2014-04-15 13:49:14');| }
|
736
|
+
it 'should escape new line' do
|
737
|
+
expect(subject).to eq([['1',"abcd\nefgh",'2014-04-15 13:49:14']])
|
738
|
+
end
|
739
|
+
end
|
740
|
+
|
741
|
+
context 'when data includes escaped single quotation' do
|
742
|
+
let(:target_line) { %q|INSERT INTO `test_table` VALUES (1,'abcd\',\'efgh','2014-04-15 13:49:14');| }
|
743
|
+
it 'should escape single quotation' do
|
744
|
+
expect(subject).to eq([['1',"abcd','efgh",'2014-04-15 13:49:14']])
|
745
|
+
end
|
746
|
+
end
|
747
|
+
|
748
|
+
context 'when data includes back slash' do
|
749
|
+
let(:target_line) { %q|INSERT INTO `test_table` VALUES (1,'abcd\\efgh','2014-04-15 13:49:14');| }
|
750
|
+
it 'should escape back slash' do
|
751
|
+
expect(subject).to eq([['1',"abcd\\efgh",'2014-04-15 13:49:14']])
|
752
|
+
end
|
753
|
+
end
|
754
|
+
|
755
|
+
context 'when data includes back slash with double quote' do
|
756
|
+
# \"\'\' value on insert query shoulde be "''
|
757
|
+
let(:target_line) { %q|INSERT INTO `tast_table` VALUES (1,'\"\'\'','2014-04-15 13:49:14');| }
|
758
|
+
it 'should escape back slash' do
|
759
|
+
expect(subject).to eq([['1',%q|"''| ,'2014-04-15 13:49:14']])
|
760
|
+
end
|
761
|
+
end
|
762
|
+
|
763
|
+
context 'when data includes back slash with double quote another case' do
|
764
|
+
# \"\"\"\"\"''\"'' value on insert query shoulde be """""''"''
|
765
|
+
let(:target_line) { %q|INSERT INTO `test_table` VALUES (1,'\"\"\"\"\"\'\'\"\'\'','2014-04-15 13:49:14');| }
|
766
|
+
it 'should escape back slash' do
|
767
|
+
expect(subject).to eq([['1',%q|"""""''"''|,'2014-04-15 13:49:14']])
|
768
|
+
end
|
769
|
+
end
|
770
|
+
|
771
|
+
context 'when data includes mixed escaped characters' do
|
772
|
+
let(:target_line) { %q|INSERT INTO `test_table` VALUES (1,'ab\rcd\\e\nf\',\'gh','2014-04-15 13:49:14');| }
|
773
|
+
it 'should escape all' do
|
774
|
+
expect(subject).to eq([['1',"ab\rcd\\e\nf','gh",'2014-04-15 13:49:14']])
|
775
|
+
end
|
776
|
+
end
|
777
|
+
|
778
|
+
context 'when data includes mixed escaped characters in a row' do
|
779
|
+
let(:target_line) { "INSERT INTO `test_table` VALUES (1,'ab\\\\ncd\\\\\\nefgh','2014-04-15 13:49:14');" }
|
780
|
+
it 'should escape all' do
|
781
|
+
expect(subject).to eq([['1',"ab\\ncd\\\nefgh",'2014-04-15 13:49:14']])
|
782
|
+
end
|
783
|
+
end
|
784
|
+
|
785
|
+
context 'when data end with back slash' do
|
786
|
+
let(:target_line) { "INSERT INTO `test_table` VALUES (1,'D:\\\\download\\\\','2014-04-15 13:49:14');" }
|
787
|
+
it 'should escape back slash' do
|
788
|
+
expect(subject).to eq([['1',"D:\\download\\",'2014-04-15 13:49:14']])
|
789
|
+
end
|
790
|
+
end
|
791
|
+
|
792
|
+
context 'when comma is the first character of a string' do
|
793
|
+
let(:target_line) { "INSERT INTO `test_table` VALUES (1,',9','2014-04-15 13:49:14');" }
|
794
|
+
it 'should parse the string correctly' do
|
795
|
+
expect(subject).to eq([['1',',9','2014-04-15 13:49:14']])
|
796
|
+
end
|
797
|
+
end
|
798
|
+
|
799
|
+
context 'when comma is the last character of a string' do
|
800
|
+
let(:target_line) { "INSERT INTO `test_table` VALUES (1,'9,','2014-04-15 13:49:14');" }
|
801
|
+
it 'should parse the string correctly' do
|
802
|
+
expect(subject).to eq([['1','9,','2014-04-15 13:49:14']])
|
803
|
+
end
|
804
|
+
end
|
805
|
+
|
806
|
+
context 'when a string consists of a comma' do
|
807
|
+
let(:target_line) { "INSERT INTO `test_table` VALUES (1,',','2014-04-15 13:49:14');" }
|
808
|
+
it 'should parse the string correctly' do
|
809
|
+
expect(subject).to eq([['1',',','2014-04-15 13:49:14']])
|
810
|
+
end
|
811
|
+
end
|
812
|
+
|
813
|
+
context 'when two commas are given as a string' do
|
814
|
+
let(:target_line) { "INSERT INTO `test_table` VALUES (1,',,','2014-04-15 13:49:14');" }
|
815
|
+
it 'should parse the string correctly' do
|
816
|
+
expect(subject).to eq([['1',',,','2014-04-15 13:49:14']])
|
817
|
+
end
|
818
|
+
end
|
819
|
+
|
820
|
+
context 'when an empty string value is given' do
|
821
|
+
let(:target_line) { "INSERT INTO `test_table` VALUES (1,'','2014-04-15 13:49:14');" }
|
822
|
+
it 'should parse the string correctly' do
|
823
|
+
expect(subject).to eq([['1','','2014-04-15 13:49:14']])
|
824
|
+
end
|
825
|
+
end
|
826
|
+
|
827
|
+
context 'when a value consists of a comma followed by closing bracket' do
|
828
|
+
let(:target_line) { "INSERT INTO `test_table` VALUES (1,'),ksd','2014-04-15 13:49:14');" }
|
829
|
+
it 'should parse the string correctly' do
|
830
|
+
expect(subject).to eq([['1','),ksd','2014-04-15 13:49:14']])
|
831
|
+
end
|
832
|
+
end
|
833
|
+
|
834
|
+
context 'when there is a white space before the closing bracket' do
|
835
|
+
let(:target_line) { "INSERT INTO `test_table` VALUES (1,'aa','2014-04-15 13:49:14' );" }
|
836
|
+
it 'should fail to parse. This is intentional for performance reason' do
|
837
|
+
expect{subject}.to raise_error
|
838
|
+
end
|
839
|
+
end
|
840
|
+
|
841
|
+
context 'when an integer that has leading zeros is given' do
|
842
|
+
let(:target_line) {"INSERT INTO `test_table` VALUES (1,00013);"}
|
843
|
+
it 'should remove the leading zeros' do
|
844
|
+
expect(subject).to eq([['1', '13']])
|
845
|
+
end
|
846
|
+
end
|
847
|
+
|
848
|
+
context 'when a decimal that has leading zeros is given' do
|
849
|
+
let(:target_line) {"INSERT INTO `test_table` VALUES (1,00013.40);"}
|
850
|
+
it 'should remove the leading zeros' do
|
851
|
+
expect(subject).to eq([['1', '13.40']])
|
852
|
+
end
|
853
|
+
end
|
854
|
+
|
855
|
+
context 'when a timestamp that has leading zeros is given' do
|
856
|
+
let(:target_line) {"INSERT INTO `test_table` VALUES (1,'0004-04-15 13:49:14');"}
|
857
|
+
it 'should not remove the leading zeros' do
|
858
|
+
expect(subject).to eq([['1', '0004-04-15 13:49:14']])
|
859
|
+
end
|
860
|
+
end
|
861
|
+
|
862
|
+
context 'when a string that has leading zeros is given' do
|
863
|
+
let(:target_line) {"INSERT INTO `test_table` VALUES (1,'00000aa');"}
|
864
|
+
it 'should not remove the leading zeros' do
|
865
|
+
expect(subject).to eq([['1', '00000aa']])
|
866
|
+
end
|
867
|
+
end
|
868
|
+
|
869
|
+
context 'when a string that has leading zeros, numbers and a comma in between is given' do
|
870
|
+
let(:target_line) {"INSERT INTO `test_table` VALUES (1,'0003,00033');"}
|
871
|
+
it 'should not remove the leading zeros' do
|
872
|
+
expect(subject).to eq([['1', '0003,00033']])
|
873
|
+
end
|
874
|
+
end
|
875
|
+
|
876
|
+
context 'when a string that has leading zeros, has only numbers is given' do
|
877
|
+
let(:target_line) {"INSERT INTO `test_table` VALUES (1,'00033');"}
|
878
|
+
it 'should not remove the leading zeros' do
|
879
|
+
expect(subject).to eq([['1', '00033']])
|
880
|
+
end
|
881
|
+
end
|
882
|
+
|
883
|
+
context 'when 0 is given' do
|
884
|
+
let(:target_line) {"INSERT INTO `test_table` VALUES (1,0);"}
|
885
|
+
it 'should not remove the leading zeros' do
|
886
|
+
expect(subject).to eq([['1', '0']])
|
887
|
+
end
|
888
|
+
end
|
889
|
+
|
890
|
+
context 'when 0.00 is given' do
|
891
|
+
let(:target_line) {"INSERT INTO `test_table` VALUES (1,0.00);"}
|
892
|
+
it 'should not remove the leading zeros' do
|
893
|
+
expect(subject).to eq([['1', '0.00']])
|
894
|
+
end
|
895
|
+
end
|
896
|
+
end
|
897
|
+
end
|
898
|
+
end
|
899
|
+
end
|
900
|
+
end
|