flydata 0.6.4 → 0.6.5

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.
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