flydata 0.6.4 → 0.6.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -0
  3. data/Gemfile.lock +2 -0
  4. data/VERSION +1 -1
  5. data/flydata-core/lib/flydata-core/table_def/base.rb +31 -0
  6. data/flydata-core/lib/flydata-core/table_def/mysql_table_def.rb +9 -30
  7. data/flydata-core/lib/flydata-core/table_def/postgresql_table_def.rb +111 -0
  8. data/flydata-core/lib/flydata-core/table_def/redshift_table_def.rb +4 -1
  9. data/flydata-core/spec/table_def/postgresql_table_def_spec.rb +348 -0
  10. data/flydata-core/spec/table_def/redshift_table_def_spec.rb +25 -0
  11. data/flydata.gemspec +0 -0
  12. data/lib/flydata.rb +0 -7
  13. data/lib/flydata/command/base.rb +3 -2
  14. data/lib/flydata/fluent-plugins/flydata_plugin_ext/base.rb +5 -0
  15. data/lib/flydata/fluent-plugins/flydata_plugin_ext/flush_support.rb +52 -0
  16. data/lib/flydata/fluent-plugins/flydata_plugin_ext/flydata_sync.rb +55 -0
  17. data/lib/flydata/fluent-plugins/{idle_event_detector.rb → flydata_plugin_ext/idle_event_detector.rb} +0 -0
  18. data/lib/flydata/fluent-plugins/{preference.rb → flydata_plugin_ext/preference.rb} +2 -14
  19. data/lib/flydata/fluent-plugins/flydata_plugin_ext/transaction_support.rb +58 -0
  20. data/lib/flydata/fluent-plugins/in_mysql_binlog_flydata.rb +55 -135
  21. data/lib/flydata/fluent-plugins/mysql/dml_record_handler.rb +9 -4
  22. data/lib/flydata/helper/server.rb +7 -0
  23. data/lib/flydata/preference/data_entry_preference.rb +5 -13
  24. data/lib/flydata/source.rb +1 -1
  25. data/lib/flydata/source/data_entry.rb +29 -0
  26. data/lib/flydata/source/sync.rb +19 -0
  27. data/lib/flydata/source/sync_generate_table_ddl.rb +47 -7
  28. data/lib/flydata/source_mysql/data_entry.rb +22 -0
  29. data/lib/flydata/source_mysql/parser/dump_parser.rb +1 -1
  30. data/lib/flydata/source_mysql/parser/mysql_alter_table.treetop +8 -3
  31. data/lib/flydata/source_mysql/sync.rb +1 -8
  32. data/lib/flydata/source_mysql/sync_generate_table_ddl.rb +11 -16
  33. data/lib/flydata/source_postgresql/data_entry.rb +21 -0
  34. data/lib/flydata/source_postgresql/sync.rb +29 -0
  35. data/lib/flydata/source_postgresql/sync_generate_table_ddl.rb +126 -0
  36. data/spec/flydata/fluent-plugins/{idle_event_detector_spec.rb → flydata_plugin_ext/idle_event_detector_spec.rb} +1 -1
  37. data/spec/flydata/fluent-plugins/in_mysql_binlog_flydata_spec.rb +10 -0
  38. data/spec/flydata/source_mysql/parser/alter_table_parser_spec.rb +119 -0
  39. data/spec/flydata/source_mysql/parser/dump_parser_spec.rb +32 -1
  40. data/spec/flydata/source_mysql/sync_generate_table_ddl_spec.rb +4 -4
  41. metadata +31 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 12c8fd94c861658f352698075555d8a868d3ca6d
4
- data.tar.gz: ff5061d23bb1cee12430d471834131034bc925b2
3
+ metadata.gz: 614092c4a27b51744d8f8fdbd521e95205455eac
4
+ data.tar.gz: 3e6d10c8acd5331700c312c1982a6a9acdba3c66
5
5
  SHA512:
6
- metadata.gz: 0c956f29080dbc9a517a08b2b865aceef102f26a509061cb2152ee519d0f2d99794348546001d09c48f9b7ae2ed8015b12a593520780ffe1517614a9fc1dedde
7
- data.tar.gz: 731ecc427a94b7aab2afb638c8dbbb532b6055f080ed938574d26a19b187478d1fecbb75dc1c09a066b816983dfb0611711a27b8123b14370efda8128f535da0
6
+ metadata.gz: 0370a93f87eedacf8b56210c15363be7f040980b73eb938cf077953a16d0da942d522881601e21ded8a89a1376c480263483a61d4b0932cd41b70739c457f964
7
+ data.tar.gz: d870e1abfe9491ad135b1b8ee4e45a293b8a312db2823b7a1cc73fd7d337d19a47e68935c2b6fa606a1f0b2a95af87e7c0ee350170192638b109e75acf567647
data/Gemfile CHANGED
@@ -9,6 +9,7 @@ gem "highline", '~> 1.6', '>= 1.6.19'
9
9
  gem "fluentd", "0.10.46"
10
10
  gem "fluent-plugin-mysql-binlog", '~> 0.0', '>= 0.0.2'
11
11
  gem "mysql2", '~> 0.3', '>= 0.3.17'
12
+ gem "pg", '~> 0.18.4'
12
13
  gem "slop", '~> 3.4', '>= 3.4.6'
13
14
  gem "treetop", '~> 1.5', '>= 1.5.3'
14
15
  gem "sys-filesystem", '~> 1.1', '>= 1.1.3'
data/Gemfile.lock CHANGED
@@ -79,6 +79,7 @@ GEM
79
79
  multi_json (~> 1.3)
80
80
  multi_xml (~> 0.5)
81
81
  rack (~> 1.2)
82
+ pg (0.18.4)
82
83
  polyglot (0.3.5)
83
84
  protected_attributes (1.0.8)
84
85
  activemodel (>= 4.0.1, < 5.0)
@@ -139,6 +140,7 @@ DEPENDENCIES
139
140
  json (~> 1.8, >= 1.8.0)
140
141
  kodama (~> 0.1, >= 0.1.8)
141
142
  mysql2 (~> 0.3, >= 0.3.17)
143
+ pg (~> 0.18.4)
142
144
  protected_attributes (~> 1.0, >= 1.0.8)
143
145
  pry
144
146
  rake-compiler (~> 0.9, >= 0.9.5)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.4
1
+ 0.6.5
@@ -0,0 +1,31 @@
1
+ module FlydataCore
2
+ module TableDef
3
+
4
+ class Base
5
+ def initialize(table_def, table_name, columns, column_def, default_charset,
6
+ default_source_charset, comment)
7
+ @table_def = table_def
8
+ @table_name = table_name
9
+ @columns = columns
10
+ @column_def = column_def
11
+ @default_charset = default_charset
12
+ @default_source_charset = default_source_charset
13
+ @comment = comment
14
+ end
15
+
16
+ attr_reader :columns, :column_def, :table_name, :default_source_charset
17
+
18
+ def to_flydata_tabledef
19
+ tabledef = { table_name: @table_name,
20
+ columns: @columns,
21
+ }
22
+ tabledef[:default_charset] = @default_charset if @default_charset
23
+ tabledef[:comment] = @comment if @comment
24
+ tabledef[:src_ddl] = @table_def
25
+
26
+ tabledef
27
+ end
28
+ end
29
+
30
+ end
31
+ end
@@ -1,14 +1,16 @@
1
+ require 'flydata-core/table_def/base'
2
+
1
3
  module FlydataCore
2
4
  module TableDef
3
5
 
4
- class MysqlTableDef
6
+ class MysqlTableDef < Base
5
7
  # Check and set the varchar(char) size which is converted from
6
8
  # length to byte size.
7
9
  # On Mysql the record size of varchar(char) is a length of characters.
8
10
  # ex) varchar(6) on mysql -> varchar(18) on flydata
9
11
  PROC_override_varchar = ->(type, mysql_type, flydata_type) do
10
12
  return type unless %w(char varchar).include?(mysql_type)
11
- if type =~ /\((\d+)\)/
13
+ if type =~ /\((\s*\d+\s*)\)/
12
14
  # expect 3 byte UTF-8 character
13
15
  "#{flydata_type}(#{$1.to_i * 3})"
14
16
  else
@@ -18,7 +20,7 @@ class MysqlTableDef
18
20
 
19
21
  PROC_override_varbinary = ->(type, mysql_type, flydata_type) do
20
22
  return type unless %w(binary varbinary).include?(mysql_type)
21
- if type =~ /\((\d+)\)/
23
+ if type =~ /\((\s*\d+\s*)\)/ ###DEBUG/\((\d+)\)/
22
24
  # expect 2 bytes for each original byte + 2 bytes for the prefix
23
25
  # ex) 4E5DFF => "0x4e5dff"
24
26
  "#{flydata_type}(#{$1.to_i * 2 + 2})"
@@ -87,24 +89,13 @@ class MysqlTableDef
87
89
  params ? self.new(*params) : nil
88
90
  end
89
91
 
90
- def initialize(table_def, table_name, columns, column_def, default_charset,
91
- default_charset_mysql, comment)
92
- @table_def = table_def
93
- @table_name = table_name
94
- @columns = columns
95
- @column_def = column_def
96
- @default_charset = default_charset
97
- @default_charset_mysql = default_charset_mysql
98
- @comment = comment
99
- end
100
-
101
92
  def self._create(io, options)
102
93
  table_def = ''
103
94
  table_name = nil
104
95
  columns = []
105
96
  column_def = {}
106
97
  default_charset = nil
107
- default_charset_mysql = nil
98
+ default_source_charset = nil
108
99
  comment = nil
109
100
 
110
101
  position = :before_create_table
@@ -135,8 +126,8 @@ class MysqlTableDef
135
126
  #) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='test table';
136
127
  elsif stripped_line.start_with?(')')
137
128
  if line =~ /DEFAULT CHARSET\s*=\s*([a-z0-9]+)/
138
- default_charset_mysql = $1
139
- default_charset = flydata_charset(default_charset_mysql)
129
+ default_source_charset = $1
130
+ default_charset = flydata_charset(default_source_charset)
140
131
  end
141
132
  comment = $1 if /COMMENT='((?:\\'|[^'])*)'/.match(line)
142
133
  position = :after_create_table
@@ -157,21 +148,9 @@ class MysqlTableDef
157
148
  break
158
149
  end
159
150
  end
160
- position == :after_create_table ? [table_def, table_name, columns, column_def, default_charset, default_charset_mysql, comment] : nil
151
+ position == :after_create_table ? [table_def, table_name, columns, column_def, default_charset, default_source_charset, comment] : nil
161
152
  end
162
- attr_reader :columns, :column_def, :table_name, :default_charset_mysql
163
153
 
164
- def to_flydata_tabledef
165
- tabledef = { table_name: @table_name,
166
- columns: @columns,
167
- }
168
- tabledef[:default_charset] = @default_charset if @default_charset
169
- tabledef[:comment] = @comment if @comment
170
- tabledef[:src_ddl] = @table_def
171
-
172
- tabledef
173
- end
174
-
175
154
  # Replaces a MySQL string with an empty string ('')
176
155
  def self.strip_string(line)
177
156
  line.gsub(/'(\\'|''|[^'])*'/, "''")
@@ -0,0 +1,111 @@
1
+ require 'flydata-core/table_def/base'
2
+
3
+ module FlydataCore
4
+ module TableDef
5
+
6
+ class PostgresqlTableDef < Base
7
+
8
+ VALUE_CONVERTERS = {}
9
+
10
+ TYPE_MAP_P2F = {
11
+ 'bigint' => {type: 'int8'},
12
+ 'character varying' => {type: 'varchar',
13
+ width_attrs:["character_octet_length"]}, # TODO check def_width
14
+ 'integer' => {type: 'int4'},
15
+ 'timestamp with time zone' => {type: 'datetime'}, # TODO may need override?
16
+ 'timestamp without time zone' => {type: 'datetime'}, # TODO may need override?
17
+ 'bytea' => {type: 'varbinary'}, # TODO need value conversion
18
+ 'numeric' => {type: 'numeric',
19
+ width_attrs:["numeric_precision", "numeric_scale"],
20
+ def_width:[nil, 0],
21
+ },
22
+ # TODO add more types
23
+ }
24
+
25
+ def self.convert_to_flydata_type(information_schema_columns)
26
+ pg_type = information_schema_columns["data_type"]
27
+ unless TYPE_MAP_P2F.has_key?(pg_type)
28
+ raise "Unknown PostgreSQL type or internal error. type:#{pg_type}"
29
+ end
30
+ type_hash = TYPE_MAP_P2F[pg_type]
31
+ flydata_type = type_hash[:type]
32
+ ret_type = flydata_type
33
+
34
+ width_values = get_width_values(information_schema_columns, type_hash)
35
+ if width_values
36
+ ret_type += "(#{width_values.join(",")})"
37
+ end
38
+
39
+ if type_hash[:override]
40
+ ret_type = type_hash[:override].call(ret_type, pg_type, flydata_type)
41
+ end
42
+ ret_type
43
+ end
44
+
45
+ def self.create(information_schema_columns, options)
46
+ params = _create(information_schema_columns, options)
47
+ params ? self.new(*params) : nil
48
+ end
49
+
50
+ private
51
+
52
+ def self.get_width_values(information_schema_columns, type_hash)
53
+ values = []
54
+ if type_hash.has_key?(:width_attrs)
55
+ values = type_hash[:width_attrs].collect{|attr| information_schema_columns[attr] }
56
+ end
57
+
58
+ if type_hash.has_key?(:def_width)
59
+ if values.nil? || values.size != type_hash[:def_width].size
60
+ raise "The number of the default values must match the number of width attributes def_width:#{type_hash[:def_width].inspect} width_attrs:#{type_hash[:width_attrs].inspect}"
61
+ end
62
+ values = values.each_with_index.collect {|v, i| v ? v : type_hash[:def_width][i]}
63
+ end
64
+ values.pop until values.empty? || values.last # remove trailing nil
65
+ if values.any?{|v| v.nil?}
66
+ raise "nil value is not allowed"
67
+ end
68
+ values.empty? ? nil : values
69
+ end
70
+
71
+ def self._create(information_schema_columns, options)
72
+ table_def = information_schema_columns.inspect
73
+ table_name = nil
74
+ columns = []
75
+ column_def = {}
76
+ # SourcePostgresql uses UTF8 client encoding no matter what the server
77
+ # encoding is.
78
+ default_charset = 'UTF_8'
79
+ default_charset_postgresql = 'UTF8'
80
+ comment = nil
81
+
82
+ information_schema_columns.each do |iscol|
83
+ column = parse_one_column_def(iscol)
84
+ if table_name
85
+ unless table_name == column[:table]
86
+ raise "Table name must match through all columns. Got `#{table_name}` and `#{column[:table]}`"
87
+ end
88
+ else
89
+ table_name = column[:table]
90
+ end
91
+ columns << column
92
+ coldef = iscol.inspect
93
+ column_def[column[:column]] = coldef
94
+ end
95
+ [table_def, table_name, columns, column_def, default_charset, default_charset_postgresql, comment]
96
+ end
97
+
98
+ def self.parse_one_column_def(information_schema_column)
99
+ column = {}
100
+ column[:table] = information_schema_column["table_name"]
101
+ column[:column] = information_schema_column["column_name"]
102
+ column[:type] = convert_to_flydata_type(information_schema_column)
103
+ column[:not_null] = true if information_schema_column["is_nullable"] == "NO"
104
+ column[:primary_key] = true if information_schema_column["is_primary"]
105
+ column[:default] = information_schema_column["column_default"] # TODO nill handling
106
+ column
107
+ end
108
+ end
109
+
110
+ end
111
+ end
@@ -229,7 +229,7 @@ EOS
229
229
 
230
230
  if (column.has_key?(:default))
231
231
  val = replace_default_value(type, type_info[:type], column[:default])
232
- line += " DEFAULT #{val}"
232
+ line += " DEFAULT #{val}" if val
233
233
  elsif not_null && opt[:for] == :alter_table
234
234
  # Redshift doesn't allow adding a not null column without default value
235
235
  # Add a defalt value
@@ -275,6 +275,9 @@ EOS
275
275
  default_value.oct
276
276
  elsif /^'.*'$/.match(default_value)
277
277
  default_value
278
+ elsif /\s*nextval\(/.match(default_value)
279
+ # Redshift does not support nextval() function. Set no default.
280
+ nil
278
281
  else
279
282
  "'#{default_value}'"
280
283
  end
@@ -0,0 +1,348 @@
1
+ require 'spec_helper'
2
+ require 'flydata-core/table_def/postgresql_table_def'
3
+
4
+ module FlydataCore
5
+ module TableDef
6
+
7
+ describe PostgresqlTableDef do
8
+ let(:subject_object) {
9
+ described_class.new(table_def, table_name, columns, column_def,
10
+ default_charset, default_charset_postgresql, comment)
11
+ }
12
+
13
+ let(:table_def) { double('table_def') }
14
+ let(:table_name) { double('table_name') }
15
+ let(:columns) { double('columns') }
16
+ let(:column_def) { double('column_def') }
17
+ let(:default_charset) { double('default_charset') }
18
+ let(:default_charset_postgresql) { double('default_charset_postgresql') }
19
+ let(:comment) { double('comment') }
20
+
21
+ let(:information_schema_columns) { {} }
22
+
23
+ describe '.convert_to_flydata_type' do
24
+ subject { described_class.convert_to_flydata_type(information_schema_columns) }
25
+
26
+ before do
27
+ information_schema_columns["data_type"] = data_type
28
+ end
29
+
30
+ let(:type_hash) { double('type_hash') }
31
+ let(:numeric_precision) { nil }
32
+ let(:numeric_scale) { nil }
33
+ let(:width_values) do
34
+ a = [ numeric_precision, numeric_scale ]
35
+ a.pop until a.empty? || a.last
36
+ a.empty? ? nil : a
37
+ end
38
+ before do
39
+ allow(described_class).to receive(:get_width_values).
40
+ with(information_schema_columns, type_hash).
41
+ and_return(width_values)
42
+ end
43
+
44
+ context "with data type numeric" do
45
+ let(:data_type) { "numeric" }
46
+
47
+ before do
48
+ information_schema_columns["numeric_precision"] = numeric_precision
49
+ information_schema_columns["numeric_scale"] = numeric_scale
50
+ end
51
+
52
+ let(:type_hash) { PostgresqlTableDef::TYPE_MAP_P2F[data_type] }
53
+
54
+ context "with precision nil" do
55
+ let(:numeric_precision) { nil }
56
+ context "with scale nil" do
57
+ let(:numeric_scale) { nil }
58
+ it { is_expected.to eq "numeric" }
59
+ end
60
+ context "with scale 2" do
61
+ let(:numeric_scale) { 2 }
62
+ it do
63
+ expect(described_class).to receive(:get_width_values).
64
+ with(information_schema_columns, type_hash).and_raise("test")
65
+
66
+ expect{subject}.to raise_error("test")
67
+ end
68
+ end
69
+ end
70
+ context "with precision 23" do
71
+ let(:numeric_precision) { 23 }
72
+ context "with scale nil" do
73
+ let(:numeric_scale) { nil }
74
+ it { is_expected.to eq "numeric(23)" }
75
+ end
76
+ context "with scale 2" do
77
+ let(:numeric_scale) { 2 }
78
+ it { is_expected.to eq "numeric(23,2)" }
79
+ end
80
+ end
81
+ end
82
+ # TODO Add data type specific specs here
83
+ context "with unknown data type" do
84
+ let(:data_type) { "duck" }
85
+ it { expect{subject}.to raise_error /Unknown PostgreSQL type/ }
86
+ end
87
+ context "with no data type" do
88
+ let(:data_type) { nil }
89
+ it { expect{subject}.to raise_error /Unknown PostgreSQL type/ }
90
+ end
91
+ context "with test data type" do
92
+ let(:data_type) { test_data_type }
93
+
94
+ before do
95
+ allow(PostgresqlTableDef::TYPE_MAP_P2F).to receive(:has_key?).
96
+ with(test_data_type).and_return true
97
+ allow(PostgresqlTableDef::TYPE_MAP_P2F).to receive(:[]).
98
+ with(test_data_type).and_return test_type_hash
99
+ end
100
+ let(:test_data_type) { double('test_data_type') }
101
+
102
+ let(:type_hash) { test_type_hash }
103
+ let(:test_type_hash) { {type: test_fd_type} }
104
+ let(:test_fd_type) { "testfdtype" }
105
+
106
+ let(:override_proc) { double('override_proc') }
107
+ let(:overriden_type) { double('overriden_type') }
108
+
109
+ context "no width values" do
110
+ let(:width_values) { nil }
111
+
112
+ it { is_expected.to eq test_fd_type }
113
+ end
114
+ context "single width value" do
115
+ let(:width_values) { [10] }
116
+
117
+ it { is_expected.to eq "#{test_fd_type}(10)" }
118
+ end
119
+ context "two width values" do
120
+ let(:width_values) { [10,2] }
121
+
122
+ it { is_expected.to eq "#{test_fd_type}(10,2)"}
123
+ end
124
+ context "with override proc" do
125
+ before do
126
+ test_type_hash[:override] = override_proc
127
+ end
128
+ it 'calls the override proc of the type and return its result' do
129
+ expect(override_proc).to receive(:call).
130
+ with(test_fd_type, test_data_type, test_fd_type).
131
+ and_return(overriden_type)
132
+
133
+ is_expected.to eq overriden_type
134
+ end
135
+ end
136
+ end
137
+ end
138
+
139
+ describe '.get_width_values(information_schema_columns, type_hash)' do
140
+ subject { described_class.send(:get_width_values,
141
+ information_schema_columns, type_hash) }
142
+
143
+ let(:type_hash) { {} }
144
+
145
+ context 'with no width attr' do
146
+ before do
147
+ type_hash.delete(:width_attrs)
148
+ end
149
+ context 'without default' do
150
+ before do
151
+ type_hash.delete(:def_width)
152
+ end
153
+
154
+ it { is_expected.to be_nil }
155
+ end
156
+ context 'with default' do
157
+ before do
158
+ type_hash[:def_width] = [10]
159
+ end
160
+
161
+ it { expect{ subject }.to raise_error }
162
+ end
163
+ end
164
+ context 'with a single width attr' do
165
+ before do
166
+ type_hash[:width_attrs] = ["char_octet_length"]
167
+ end
168
+ context 'without default' do
169
+ before do
170
+ type_hash.delete(:def_width)
171
+ end
172
+ context 'when attr value exists' do
173
+ before do
174
+ information_schema_columns["char_octet_length"] = 1024
175
+ end
176
+
177
+ it { is_expected.to eq [ 1024 ] }
178
+ end
179
+ context 'when attr value does not exist' do
180
+ before do
181
+ information_schema_columns.delete("char_octet_length")
182
+ end
183
+
184
+ it { is_expected.to be_nil }
185
+ end
186
+ end
187
+ context 'with default' do
188
+ before do
189
+ type_hash[:def_width] = [10]
190
+ end
191
+ context 'when attr value exists' do
192
+ before do
193
+ information_schema_columns["char_octet_length"] = 1024
194
+ end
195
+
196
+ it { is_expected.to eq [1024] }
197
+ end
198
+ context 'when attr value does not exist' do
199
+ before do
200
+ information_schema_columns.delete("char_octet_length")
201
+ end
202
+
203
+ it { is_expected.to eq [10] }
204
+ end
205
+ end
206
+ end
207
+ context 'with two width attrs' do
208
+ before do
209
+ type_hash[:width_attrs] = ["precision", "scale"]
210
+ end
211
+ context 'without default' do
212
+ before do
213
+ type_hash.delete(:def_width)
214
+ end
215
+ context 'when the 1st attr value exists' do
216
+ before do
217
+ information_schema_columns["precision"] = 23
218
+ end
219
+ context 'when the 2nd attr value exists' do
220
+ before do
221
+ information_schema_columns["scale"] = 2
222
+ end
223
+
224
+ it { is_expected.to eq [23, 2] }
225
+ end
226
+ context 'when the 2nd attr value does not exist' do
227
+ before do
228
+ information_schema_columns.delete("scale")
229
+ end
230
+
231
+ it 'omits trailing nils' do
232
+ is_expected.to eq [23]
233
+ end
234
+ end
235
+ end
236
+ context 'when the 1st attr value does not exist' do
237
+ before do
238
+ information_schema_columns.delete("precision")
239
+ end
240
+ context 'when the 2nd attr value exists' do
241
+ before do
242
+ information_schema_columns["scale"] = 2
243
+ end
244
+
245
+ it { expect{subject}.to raise_error }
246
+ end
247
+ context 'when the 2nd attr value does not exist' do
248
+ before do
249
+ information_schema_columns.delete("scale")
250
+ end
251
+
252
+ it { is_expected.to be_nil }
253
+ end
254
+ end
255
+ end
256
+ context 'with default' do
257
+ before do
258
+ type_hash[:def_width] = [10, 0]
259
+ end
260
+ context 'when the 1st attr value exists' do
261
+ before do
262
+ information_schema_columns["precision"] = 23
263
+ end
264
+ context 'when the 2nd attr value exists' do
265
+ before do
266
+ information_schema_columns["scale"] = 2
267
+ end
268
+
269
+ it { is_expected.to eq [23, 2] }
270
+ end
271
+ context 'when the 2nd attr value does not exist' do
272
+ before do
273
+ information_schema_columns.delete("scale")
274
+ end
275
+
276
+ it { is_expected.to eq [23, 0] }
277
+ end
278
+ end
279
+ context 'when the 1st attr value does not exist' do
280
+ before do
281
+ information_schema_columns.delete("precision")
282
+ end
283
+ context 'when the 2nd attr value exists' do
284
+ before do
285
+ information_schema_columns["scale"] = 2
286
+ end
287
+
288
+ it { is_expected.to eq [10, 2] }
289
+ end
290
+ context 'when the 2nd attr value does not exist' do
291
+ before do
292
+ information_schema_columns.delete("scale")
293
+ end
294
+
295
+ it { is_expected.to eq [10, 0] }
296
+ end
297
+ end
298
+ end
299
+ context 'when the number of default values does not match
300
+ the attr number' do
301
+ before do
302
+ type_hash[:def_width] = [10]
303
+ end
304
+ context 'when the 1st attr value exists' do
305
+ before do
306
+ information_schema_columns["precision"] = 23
307
+ end
308
+ context 'when the 2nd attr value exists' do
309
+ before do
310
+ information_schema_columns["scale"] = 2
311
+ end
312
+
313
+ it { expect{subject}.to raise_error }
314
+ end
315
+ context 'when the 2nd attr value does not exist' do
316
+ before do
317
+ information_schema_columns.delete("scale")
318
+ end
319
+
320
+ it { expect{subject}.to raise_error }
321
+ end
322
+ end
323
+ context 'when the 1st attr value does not exist' do
324
+ before do
325
+ information_schema_columns.delete("precision")
326
+ end
327
+ context 'when the 2nd attr value exists' do
328
+ before do
329
+ information_schema_columns["scale"] = 2
330
+ end
331
+
332
+ it { expect{subject}.to raise_error }
333
+ end
334
+ context 'when the 2nd attr value does not exist' do
335
+ before do
336
+ information_schema_columns.delete("scale")
337
+ end
338
+
339
+ it { expect{subject}.to raise_error }
340
+ end
341
+ end
342
+ end
343
+ end
344
+ end
345
+ end
346
+
347
+ end
348
+ end