flydata 0.6.14 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/flydata-core/Gemfile +1 -0
- data/flydata-core/Gemfile.lock +5 -0
- data/flydata-core/lib/flydata-core/errors.rb +4 -2
- data/flydata-core/lib/flydata-core/mysql/binlog_pos.rb +4 -0
- data/flydata-core/lib/flydata-core/postgresql/compatibility_checker.rb +119 -0
- data/flydata-core/lib/flydata-core/postgresql/config.rb +58 -0
- data/flydata-core/lib/flydata-core/postgresql/pg_client.rb +170 -0
- data/flydata-core/lib/flydata-core/postgresql/snapshot.rb +49 -0
- data/flydata-core/lib/flydata-core/postgresql/source_pos.rb +71 -10
- data/flydata-core/lib/flydata-core/table_def/mysql_table_def.rb +1 -1
- data/flydata-core/lib/flydata-core/table_def/postgresql_table_def.rb +76 -17
- data/flydata-core/lib/flydata-core/table_def/redshift_table_def.rb +59 -10
- data/flydata-core/spec/mysql/binlog_pos_spec.rb +10 -2
- data/flydata-core/spec/postgresql/compatibility_checker_spec.rb +148 -0
- data/flydata-core/spec/postgresql/config_spec.rb +85 -0
- data/flydata-core/spec/postgresql/pg_client_spec.rb +195 -0
- data/flydata-core/spec/postgresql/snapshot_spec.rb +55 -0
- data/flydata-core/spec/postgresql/source_pos_spec.rb +70 -8
- data/flydata-core/spec/table_def/postgresql_table_def_spec.rb +80 -19
- data/flydata-core/spec/table_def/redshift_table_def_spec.rb +211 -14
- data/flydata.gemspec +0 -0
- data/lib/flydata.rb +1 -0
- data/lib/flydata/command/sender.rb +10 -7
- data/lib/flydata/command/sync.rb +4 -1
- data/lib/flydata/fluent-plugins/flydata_plugin_ext/base.rb +1 -0
- data/lib/flydata/fluent-plugins/flydata_plugin_ext/fluent_log_ext.rb +73 -0
- data/lib/flydata/fluent-plugins/flydata_plugin_ext/flydata_sync.rb +35 -10
- data/lib/flydata/fluent-plugins/flydata_plugin_ext/flydata_sync_diff_based.rb +29 -0
- data/lib/flydata/fluent-plugins/flydata_plugin_ext/flydata_sync_query_based.rb +26 -0
- data/lib/flydata/fluent-plugins/flydata_plugin_ext/preference.rb +29 -13
- data/lib/flydata/fluent-plugins/in_mysql_binlog_flydata.rb +10 -18
- data/lib/flydata/fluent-plugins/in_postgresql_query_based_flydata.rb +64 -0
- data/lib/flydata/helpers.rb +1 -3
- data/lib/flydata/plugin_support/context.rb +14 -2
- data/lib/flydata/plugin_support/source_position_file.rb +35 -0
- data/lib/flydata/plugin_support/sync_record_emittable.rb +2 -1
- data/lib/flydata/query_based_sync/client.rb +101 -0
- data/lib/flydata/query_based_sync/record_size_estimator.rb +39 -0
- data/lib/flydata/query_based_sync/resource_requester.rb +70 -0
- data/lib/flydata/query_based_sync/response.rb +122 -0
- data/lib/flydata/query_based_sync/response_handler.rb +30 -0
- data/lib/flydata/source/sync_generate_table_ddl.rb +1 -1
- data/lib/flydata/source_mysql/plugin_support/binlog_record_dispatcher.rb +2 -2
- data/lib/flydata/source_mysql/plugin_support/binlog_record_handler.rb +3 -9
- data/lib/flydata/source_mysql/plugin_support/context.rb +26 -2
- data/lib/flydata/source_mysql/plugin_support/source_position_file.rb +14 -0
- data/lib/flydata/source_mysql/table_ddl.rb +3 -3
- data/lib/flydata/source_mysql/{plugin_support/table_meta.rb → table_meta.rb} +3 -10
- data/lib/flydata/source_postgresql/generate_source_dump.rb +44 -63
- data/lib/flydata/source_postgresql/parse_dump_and_send.rb +2 -0
- data/lib/flydata/source_postgresql/plugin_support/context.rb +13 -0
- data/lib/flydata/source_postgresql/plugin_support/source_position_file.rb +14 -0
- data/lib/flydata/source_postgresql/query_based_sync/client.rb +16 -0
- data/lib/flydata/source_postgresql/query_based_sync/diff_query_generator.rb +135 -0
- data/lib/flydata/source_postgresql/query_based_sync/resource_requester.rb +86 -0
- data/lib/flydata/source_postgresql/query_based_sync/response.rb +12 -0
- data/lib/flydata/source_postgresql/query_based_sync/response_handler.rb +12 -0
- data/lib/flydata/source_postgresql/sync_generate_table_ddl.rb +25 -79
- data/lib/flydata/source_postgresql/table_meta.rb +168 -0
- data/lib/flydata/sync_file_manager.rb +5 -5
- data/lib/flydata/table_meta.rb +19 -0
- data/spec/flydata/fluent-plugins/flydata_plugin_ext/flydata_sync_context.rb +85 -0
- data/spec/flydata/fluent-plugins/flydata_plugin_ext/flydata_sync_diff_based_shared_examples.rb +36 -0
- data/spec/flydata/fluent-plugins/flydata_plugin_ext/flydata_sync_query_based_shared_examples.rb +37 -0
- data/spec/flydata/fluent-plugins/flydata_plugin_ext/flydata_sync_shared_examples.rb +67 -0
- data/spec/flydata/fluent-plugins/in_mysql_binlog_flydata_spec.rb +119 -96
- data/spec/flydata/fluent-plugins/in_postgresql_query_based_flydata_spec.rb +82 -0
- data/spec/flydata/fluent-plugins/sync_source_plugin_context.rb +29 -0
- data/spec/flydata/plugin_support/context_spec.rb +37 -3
- data/spec/flydata/query_based_sync/client_spec.rb +79 -0
- data/spec/flydata/query_based_sync/query_based_sync_context.rb +116 -0
- data/spec/flydata/query_based_sync/record_size_estimator_spec.rb +54 -0
- data/spec/flydata/query_based_sync/resource_requester_spec.rb +58 -0
- data/spec/flydata/query_based_sync/response_handler_spec.rb +36 -0
- data/spec/flydata/query_based_sync/response_spec.rb +157 -0
- data/spec/flydata/source_mysql/plugin_support/context_spec.rb +7 -1
- data/spec/flydata/source_mysql/plugin_support/dml_record_handler_spec.rb +2 -15
- data/spec/flydata/source_mysql/plugin_support/drop_database_query_handler_spec.rb +1 -1
- data/spec/flydata/source_mysql/plugin_support/shared_query_handler_context.rb +12 -11
- data/spec/flydata/source_mysql/plugin_support/source_position_file_spec.rb +53 -0
- data/spec/flydata/source_mysql/plugin_support/truncate_query_handler_spec.rb +1 -1
- data/spec/flydata/source_mysql/table_ddl_spec.rb +5 -5
- data/spec/flydata/source_mysql/{plugin_support/table_meta_spec.rb → table_meta_spec.rb} +6 -7
- data/spec/flydata/source_postgresql/generate_source_dump_spec.rb +165 -77
- data/spec/flydata/source_postgresql/query_based_sync/diff_query_generator_spec.rb +213 -0
- data/spec/flydata/source_postgresql/query_based_sync/query_based_sync_postgresql_context.rb +76 -0
- data/spec/flydata/source_postgresql/query_based_sync/resource_requester_spec.rb +70 -0
- data/spec/flydata/source_postgresql/table_meta_spec.rb +77 -0
- metadata +49 -6
- data/lib/flydata/source_mysql/plugin_support/binlog_position_file.rb +0 -23
- data/lib/flydata/source_postgresql/pg_client.rb +0 -43
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'flydata-core/postgresql/config'
|
3
|
+
|
4
|
+
module FlydataCore
|
5
|
+
module Postgresql
|
6
|
+
describe Config do
|
7
|
+
let(:base_conf) do
|
8
|
+
{
|
9
|
+
'host' => 'localhost',
|
10
|
+
'port' => 1234,
|
11
|
+
'username' => 'testuser',
|
12
|
+
'password' => 'password',
|
13
|
+
'database' => 'testdb',
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:conf) { base_conf }
|
18
|
+
|
19
|
+
describe '.build_db_opts' do
|
20
|
+
subject { described_class.build_db_opts(conf) }
|
21
|
+
|
22
|
+
context 'with basic conf' do
|
23
|
+
it { is_expected.to eq(
|
24
|
+
host: 'localhost',
|
25
|
+
port: 1234,
|
26
|
+
username: 'testuser',
|
27
|
+
password: 'password',
|
28
|
+
database: 'testdb',
|
29
|
+
user: 'testuser',
|
30
|
+
dbname: 'testdb',
|
31
|
+
) }
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'with conf having dbname' do
|
35
|
+
let(:conf) { base_conf.merge('dbname' => 'testdb2') }
|
36
|
+
it { is_expected.to eq(
|
37
|
+
host: 'localhost',
|
38
|
+
port: 1234,
|
39
|
+
username: 'testuser',
|
40
|
+
password: 'password',
|
41
|
+
database: 'testdb',
|
42
|
+
user: 'testuser',
|
43
|
+
dbname: 'testdb2',
|
44
|
+
) }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '.opts_for_pg' do
|
49
|
+
subject { described_class.opts_for_pg(conf) }
|
50
|
+
|
51
|
+
context 'with basic conf' do
|
52
|
+
it { is_expected.to eq(
|
53
|
+
host: 'localhost',
|
54
|
+
port: 1234,
|
55
|
+
password: 'password',
|
56
|
+
user: 'testuser',
|
57
|
+
dbname: 'testdb',
|
58
|
+
) }
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'with conf having dbname' do
|
62
|
+
let(:conf) { base_conf.merge('dbname' => 'testdb2') }
|
63
|
+
it { is_expected.to eq(
|
64
|
+
host: 'localhost',
|
65
|
+
port: 1234,
|
66
|
+
password: 'password',
|
67
|
+
user: 'testuser',
|
68
|
+
dbname: 'testdb2',
|
69
|
+
) }
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'with conf having schema' do
|
73
|
+
let(:conf) { base_conf.merge('schema' => 'dev_schema') }
|
74
|
+
it { is_expected.to eq(
|
75
|
+
host: 'localhost',
|
76
|
+
port: 1234,
|
77
|
+
password: 'password',
|
78
|
+
user: 'testuser',
|
79
|
+
dbname: 'testdb',
|
80
|
+
) }
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'flydata-core/postgresql/pg_client'
|
3
|
+
|
4
|
+
module FlydataCore
|
5
|
+
module Postgresql
|
6
|
+
describe PGClient do
|
7
|
+
let(:dbconf) do
|
8
|
+
{ host: 'localhost', port: 5555, username: 'testuser',
|
9
|
+
password: 'testpassword', dbname: 'testdb' }
|
10
|
+
end
|
11
|
+
let(:subject_object) { described_class.new(dbconf) }
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
let(:pg_conn) { double('pg_conn') }
|
16
|
+
let(:socket) { double('socket') }
|
17
|
+
|
18
|
+
before do
|
19
|
+
allow(PG::Connection).to receive(:connect_start).and_return(pg_conn)
|
20
|
+
allow(pg_conn).to receive(:connect_poll).and_return(PG::PGRES_POLLING_OK)
|
21
|
+
allow(pg_conn).to receive(:status).and_return(PG::CONNECTION_OK)
|
22
|
+
allow(pg_conn).to receive(:socket_io).and_return(socket)
|
23
|
+
allow(IO).to receive(:select).with(nil, [socket], nil, PGClient::PG_CONNECT_TIMEOUT).and_return(true)
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#establish_connection' do
|
27
|
+
subject { subject_object.establish_connection }
|
28
|
+
|
29
|
+
context 'without errors' do
|
30
|
+
it { is_expected.to eq(pg_conn) }
|
31
|
+
it 'sets @conn' do
|
32
|
+
subject
|
33
|
+
expect(subject_object.instance_variable_get(:@conn)).to eq(pg_conn)
|
34
|
+
end
|
35
|
+
it 'reuses @conn until closes' do
|
36
|
+
subject_object.establish_connection
|
37
|
+
subject
|
38
|
+
expect(subject_object.instance_variable_get(:@conn)).to eq(pg_conn)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'when getting an async writing timeout error' do
|
43
|
+
before do
|
44
|
+
expect(IO).to receive(:select).with(nil, [socket], nil, PGClient::PG_CONNECT_TIMEOUT).and_return(nil)
|
45
|
+
end
|
46
|
+
it { expect{subject}.to raise_error(PG::Error, /Asynchronous.+\(WRITING\)/) }
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'when getting an async reading timeout error' do
|
50
|
+
before do
|
51
|
+
expect(pg_conn).to receive(:connect_poll).and_return(PG::PGRES_POLLING_READING)
|
52
|
+
expect(IO).to receive(:select).with(nil, [socket], nil, PGClient::PG_CONNECT_TIMEOUT).and_return(true)
|
53
|
+
expect(IO).to receive(:select).with([socket], nil, nil, PGClient::PG_CONNECT_TIMEOUT).and_return(false)
|
54
|
+
end
|
55
|
+
it { expect{subject}.to raise_error(PG::Error, /Asynchronous.+\(READING\)/) }
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'when getting socket error' do
|
59
|
+
before do
|
60
|
+
expect(IPSocket).to receive(:getaddress).with('localhost').and_raise(SocketError, 'getaddrinfo: nodename nor servname provided, or not known')
|
61
|
+
end
|
62
|
+
it { expect{subject}.to raise_error(PG::Error, /Connection failed: FATAL: unknown host\(localhost\)\./) }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '#query' do
|
67
|
+
subject { subject_object.query(query) }
|
68
|
+
|
69
|
+
context 'when query is a string' do
|
70
|
+
let(:query) { 'test query;' }
|
71
|
+
let(:result) { double('result') }
|
72
|
+
|
73
|
+
it do
|
74
|
+
expect(pg_conn).to receive(:query).with(query, []).
|
75
|
+
and_return(result)
|
76
|
+
expect(subject).to eq(result)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
context 'when query has overriders' do
|
80
|
+
let(:query) { PGQuery.new(query_text, value_overriders:{"value" => -> (v) { "#{v}!" } }) }
|
81
|
+
let(:query_text) { "test_query" }
|
82
|
+
let(:result) { [{"id" => 1, "value" => "hi"},
|
83
|
+
{"id" => 2, "value" => "ho"}] }
|
84
|
+
let(:expected_values) { ["hi!", "ho!"] }
|
85
|
+
it do
|
86
|
+
expect(pg_conn).to receive(:query).with(query_text, []).
|
87
|
+
and_return(result)
|
88
|
+
subject.each_with_index do |row, i|
|
89
|
+
expect(row["value"]).to eq expected_values[i]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'when query has binding_params' do
|
95
|
+
let(:binding_params) { ['1000', '2000'] }
|
96
|
+
let(:query) { PGQuery.new(query_text, binding_params: binding_params) }
|
97
|
+
let(:query_text) { "test_query" }
|
98
|
+
let(:result) { double('result') }
|
99
|
+
it do
|
100
|
+
expect(pg_conn).to receive(:query).with(query_text, binding_params).
|
101
|
+
and_return(result)
|
102
|
+
expect(subject).to eq(result)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe '#close' do
|
108
|
+
subject { subject_object.close }
|
109
|
+
|
110
|
+
context 'when conn exists' do
|
111
|
+
before { subject_object.establish_connection }
|
112
|
+
it do
|
113
|
+
expect(pg_conn).to receive(:finish).once
|
114
|
+
subject
|
115
|
+
expect(subject_object.instance_variable_get(:@conn)).to be_nil
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
context 'when conn does not exist' do
|
120
|
+
it do
|
121
|
+
expect(pg_conn).to receive(:finish).never
|
122
|
+
expect{subject}.not_to raise_error
|
123
|
+
expect(subject_object.instance_variable_get(:@conn)).to be_nil
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
describe PGClient::HashValueOverrider do
|
130
|
+
let(:subject_object) { described_class.new(delegate, overriders) }
|
131
|
+
|
132
|
+
let(:delegate) { { "salute" => saluteval, "id" => idval } }
|
133
|
+
let(:idval) { 1 }
|
134
|
+
let(:saluteval) { "hello" }
|
135
|
+
let(:overriders) { {"salute" => ->(v) { "#{v}!" } } }
|
136
|
+
|
137
|
+
describe '#[]' do
|
138
|
+
subject { subject_object[key] }
|
139
|
+
|
140
|
+
context 'when no overrider for the key' do
|
141
|
+
let(:key) { "id" }
|
142
|
+
it { is_expected.to eq idval }
|
143
|
+
end
|
144
|
+
|
145
|
+
context 'when key has an overrider' do
|
146
|
+
let(:key) { "salute" }
|
147
|
+
it { is_expected.to eq "#{saluteval}!" }
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe '#values' do
|
152
|
+
subject { subject_object.values }
|
153
|
+
|
154
|
+
it { is_expected.to eq [ "#{saluteval}!", idval ] }
|
155
|
+
end
|
156
|
+
|
157
|
+
describe '#first' do
|
158
|
+
subject { subject_object.first }
|
159
|
+
|
160
|
+
it { is_expected.to eq [ "salute", "#{saluteval}!" ] }
|
161
|
+
end
|
162
|
+
|
163
|
+
describe '#kind_of?' do
|
164
|
+
subject { subject_object.kind_of?(klass) }
|
165
|
+
|
166
|
+
context 'when klass is of delegate' do
|
167
|
+
let(:klass) { delegate.class }
|
168
|
+
|
169
|
+
it { is_expected.to be_truthy }
|
170
|
+
end
|
171
|
+
|
172
|
+
context 'when klass is of self' do
|
173
|
+
let(:klass) { described_class }
|
174
|
+
|
175
|
+
it { is_expected.to be_falsey }
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
describe '#has_key?' do
|
180
|
+
subject { subject_object.has_key?(key) }
|
181
|
+
|
182
|
+
context 'when delegate has the key' do
|
183
|
+
let(:key) { "id" }
|
184
|
+
|
185
|
+
it { is_expected.to be_truthy }
|
186
|
+
end
|
187
|
+
context 'when delegate does not have the key' do
|
188
|
+
let(:key) { "random_stuff" }
|
189
|
+
|
190
|
+
it { is_expected.to be_falsey }
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'flydata-core/postgresql/snapshot'
|
3
|
+
|
4
|
+
module FlydataCore
|
5
|
+
module Postgresql
|
6
|
+
describe Snapshot do
|
7
|
+
let(:snapshot_text) { '10:18:11,12,15' }
|
8
|
+
let(:subject_object) { described_class.new(snapshot_text) }
|
9
|
+
|
10
|
+
describe '#initialize' do
|
11
|
+
subject { subject_object }
|
12
|
+
|
13
|
+
context 'when xip_list is empty' do
|
14
|
+
let(:snapshot_text) { '10:18:' }
|
15
|
+
it { expect(subject.xmin).to eq(10) }
|
16
|
+
it { expect(subject.xmax).to eq(18) }
|
17
|
+
it { expect(subject.xip_list).to eq([]) }
|
18
|
+
it { expect(subject.to_s).to eq('10:18:') }
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'when xip_list is not empty' do
|
22
|
+
let(:snapshot_text) { '10:18:11,12,15' }
|
23
|
+
it { expect(subject.xmin).to eq(10) }
|
24
|
+
it { expect(subject.xmax).to eq(18) }
|
25
|
+
it { expect(subject.xip_list).to eq([11, 12, 15]) }
|
26
|
+
it { expect(subject.to_s).to eq('10:18:11,12,15') }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#<=>' do
|
31
|
+
def new_ss(txt)
|
32
|
+
Snapshot.new(txt)
|
33
|
+
end
|
34
|
+
|
35
|
+
it { expect(new_ss("10:10:") == new_ss("10:10:")).to be(true) }
|
36
|
+
it { expect(new_ss("10:10:") == new_ss("11:11:")).to be(false) }
|
37
|
+
it { expect(new_ss("10:10:") == new_ss("9:9:")).to be(false) }
|
38
|
+
it { expect(new_ss("10:10:") == new_ss("10:11:")).to be(false) }
|
39
|
+
it { expect(new_ss("10:18:") == new_ss("10:18:10,11,12")).to be(false) }
|
40
|
+
it { expect(new_ss("10:18:10,11,12") == new_ss("10:18:10,11,12")).to be(true) }
|
41
|
+
it { expect(new_ss("10:18:10,11,12") == new_ss("10:18:11,12")).to be(false) }
|
42
|
+
|
43
|
+
it { expect(new_ss("10:10:") > new_ss("10:10:")).to be(false) }
|
44
|
+
it { expect(new_ss("10:10:") > new_ss("11:11:")).to be(false) }
|
45
|
+
it { expect(new_ss("11:11:") > new_ss("10:10:")).to be(true) }
|
46
|
+
it { expect(new_ss("10:10:") > new_ss("9:9:")).to be(true) }
|
47
|
+
it { expect(new_ss("10:10:") > new_ss("10:11:")).to be(false) }
|
48
|
+
it { expect(new_ss("10:11:") > new_ss("10:10:")).to be(true) }
|
49
|
+
it { expect(new_ss("10:18:") > new_ss("10:18:10,11,12")).to be(true) }
|
50
|
+
it { expect(new_ss("10:18:10,11,12") > new_ss("10:18:10,11,12")).to be(false) }
|
51
|
+
it { expect(new_ss("10:18:10,11,12") > new_ss("10:18:11,12")).to be(false) }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -4,21 +4,42 @@ module FlydataCore
|
|
4
4
|
module Postgresql
|
5
5
|
|
6
6
|
describe SourcePos do
|
7
|
-
let(:subject_object) { described_class.new(
|
7
|
+
let(:subject_object) { described_class.new(snapshot, to_snapshot, pk_values) }
|
8
8
|
|
9
|
-
let(:
|
9
|
+
let(:snapshot) { "1234:1234:" }
|
10
|
+
let(:to_snapshot) { nil }
|
10
11
|
let(:pk_values) { nil }
|
11
12
|
|
13
|
+
describe '#initialize' do
|
14
|
+
context 'with snapshot and pk_values' do
|
15
|
+
it { expect(subject_object.snapshot).to eq(Snapshot.new(snapshot)) }
|
16
|
+
it { expect(subject_object.pk_values).to eq(pk_values) }
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'with source pos object' do
|
20
|
+
let(:snapshot) { '1111:1111:' }
|
21
|
+
let(:to_snapshot) { '2222:2222:' }
|
22
|
+
let(:pk_values) { [{'id' => '1000'}] }
|
23
|
+
let(:obj) { described_class.new(snapshot, to_snapshot, pk_values) }
|
24
|
+
subject { described_class.new(obj) }
|
25
|
+
|
26
|
+
it { expect(subject.snapshot).to eq(Snapshot.new(snapshot)) }
|
27
|
+
it { expect(subject.to_snapshot).to eq(Snapshot.new(to_snapshot)) }
|
28
|
+
it { expect(subject.pk_values).to eq(pk_values) }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
12
32
|
describe '#to_s' do
|
13
33
|
subject { subject_object.to_s }
|
14
34
|
|
15
35
|
context 'when pk_values is nil' do
|
16
36
|
let(:pk_values) { nil }
|
17
|
-
it { is_expected.to eq %Q|#{
|
37
|
+
it { is_expected.to eq %Q|#{snapshot}\t\t| }
|
18
38
|
end
|
19
39
|
context 'when pk_values is not nil' do
|
40
|
+
let(:to_snapshot) { '1234:1234:' }
|
20
41
|
let(:pk_values) { { "user_id" => 1, "address_id" => 3 } }
|
21
|
-
it { is_expected.to eq %Q|#{
|
42
|
+
it { is_expected.to eq %Q|#{snapshot}\t#{to_snapshot}\t{"user_id":1,"address_id":3}| }
|
22
43
|
end
|
23
44
|
end
|
24
45
|
|
@@ -26,17 +47,58 @@ describe SourcePos do
|
|
26
47
|
subject { described_class.load(str) }
|
27
48
|
|
28
49
|
context 'without pk_values' do
|
29
|
-
let(:str) { %Q|#{
|
30
|
-
it { is_expected.to eq described_class.new(
|
50
|
+
let(:str) { %Q|#{snapshot}\t\t| }
|
51
|
+
it { is_expected.to eq described_class.new(snapshot) }
|
31
52
|
end
|
32
53
|
context 'with pk_values' do
|
33
|
-
let(:str) { %Q|#{
|
54
|
+
let(:str) { %Q|#{snapshot}\t\t{"user_id":1,"address_id":3}| }
|
34
55
|
it do
|
35
|
-
is_expected.to eq described_class.new(
|
56
|
+
is_expected.to eq described_class.new(snapshot, nil,
|
36
57
|
"user_id" => 1, "address_id" => 3)
|
37
58
|
end
|
38
59
|
end
|
39
60
|
end
|
61
|
+
|
62
|
+
describe '#<=>' do
|
63
|
+
def src_pos(snapshot, pk_values = nil)
|
64
|
+
described_class.new(snapshot, '2222:2222:', pk_values)
|
65
|
+
end
|
66
|
+
context 'when both have no pk_values' do
|
67
|
+
it { expect(src_pos('1111:1111:') == src_pos('1111:1111:')).to be(true) }
|
68
|
+
it { expect(src_pos('1111:1111:') == src_pos('1112:1112:')).to be(false) }
|
69
|
+
it { expect(src_pos('1111:1111:') < src_pos('1112:1112:')).to be(true) }
|
70
|
+
it { expect(src_pos('1111:1111:') < src_pos('1110:1110:')).to be(false) }
|
71
|
+
it { expect(src_pos('1111:1111:') > src_pos('1110:1110:')).to be(true) }
|
72
|
+
it { expect(src_pos('1111:1111:') > src_pos('1112:1112:')).to be(false) }
|
73
|
+
it { expect(src_pos('1111:1111:') > src_pos('999:999')).to be(true) }
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'when one of them has pk_values' do
|
77
|
+
let(:pkv) { [{'id'=>'10'}] }
|
78
|
+
it { expect(src_pos('1111:1111:',pkv) == src_pos('1111:1111:')).to be(false) }
|
79
|
+
it { expect(src_pos('1111:1111:',pkv) == src_pos('1112:1112:')).to be(false) }
|
80
|
+
it { expect(src_pos('1111:1111:',pkv) < src_pos('1111:1111:')).to be(true) }
|
81
|
+
it { expect(src_pos('1111:1111:',pkv) < src_pos('1112:1112:')).to be(true) }
|
82
|
+
it { expect(src_pos('1111:1111:',pkv) < src_pos('1110:1110:')).to be(false) }
|
83
|
+
it { expect(src_pos('1111:1111:',pkv) > src_pos('1110:1110:')).to be(true) }
|
84
|
+
it { expect(src_pos('1111:1111:',pkv) > src_pos('1112:1112:')).to be(false) }
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'when both have pk_values' do
|
88
|
+
let(:pkv1) { [{'id'=>'10'}] }
|
89
|
+
let(:pkv2) { [{'name'=>'akira'}] }
|
90
|
+
it { expect(src_pos('1111:1111:',pkv1) == src_pos('1111:1111:',pkv1)).to be(true) }
|
91
|
+
it { expect(src_pos('1111:1111:',pkv1) == src_pos('1111:1111:',pkv2)).to be(false) }
|
92
|
+
it { expect(src_pos('1111:1111:',pkv1) == src_pos('1112:1112:',pkv2)).to be(false) }
|
93
|
+
it { expect(src_pos('1111:1111:',pkv1) < src_pos('1111:1111:',pkv2)).to be(true) }
|
94
|
+
it { expect(src_pos('1111:1111:',pkv1) < src_pos('1111:1111:',pkv1)).to be(false) }
|
95
|
+
it { expect(src_pos('1111:1111:',pkv1) < src_pos('1112:1112:',pkv2)).to be(true) }
|
96
|
+
it { expect(src_pos('1111:1111:',pkv1) < src_pos('1110:1110:',pkv2)).to be(false) }
|
97
|
+
it { expect(src_pos('1111:1111:',pkv1) > src_pos('1110:1110:',pkv2)).to be(true) }
|
98
|
+
it { expect(src_pos('1111:1111:',pkv1) > src_pos('1112:1112:',pkv2)).to be(false) }
|
99
|
+
it { expect(src_pos('1111:1111:',pkv1) > src_pos('1111:1111:',pkv2)).to be(false) }
|
100
|
+
end
|
101
|
+
end
|
40
102
|
end
|
41
103
|
|
42
104
|
end
|
@@ -27,7 +27,7 @@ describe PostgresqlTableDef do
|
|
27
27
|
information_schema_columns["data_type"] = data_type
|
28
28
|
end
|
29
29
|
|
30
|
-
let(:type_hash) {
|
30
|
+
let(:type_hash) { PostgresqlTableDef::TYPE_MAP_P2F[data_type] }
|
31
31
|
let(:numeric_precision) { nil }
|
32
32
|
let(:numeric_scale) { nil }
|
33
33
|
let(:width_values) do
|
@@ -49,40 +49,101 @@ describe PostgresqlTableDef do
|
|
49
49
|
information_schema_columns["numeric_scale"] = numeric_scale
|
50
50
|
end
|
51
51
|
|
52
|
-
let(:type_hash) { PostgresqlTableDef::TYPE_MAP_P2F[data_type] }
|
53
|
-
|
54
52
|
context "with precision nil" do
|
55
53
|
let(:numeric_precision) { nil }
|
56
54
|
context "with scale nil" do
|
57
55
|
let(:numeric_scale) { nil }
|
58
56
|
it { is_expected.to eq "numeric" }
|
59
57
|
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
58
|
end
|
70
|
-
context "with precision
|
71
|
-
let(:numeric_precision) {
|
72
|
-
context "with scale
|
73
|
-
let(:numeric_scale) {
|
74
|
-
it { is_expected.to eq "numeric(
|
59
|
+
context "with precision 1000" do
|
60
|
+
let(:numeric_precision) { 1000 }
|
61
|
+
context "with scale 0 (default value given by PostgreSQL)" do
|
62
|
+
let(:numeric_scale) { 0 }
|
63
|
+
it { is_expected.to eq "numeric(1000,0)" }
|
75
64
|
end
|
76
65
|
context "with scale 2" do
|
77
66
|
let(:numeric_scale) { 2 }
|
78
|
-
it { is_expected.to eq "numeric(
|
67
|
+
it { is_expected.to eq "numeric(1000,2)" }
|
79
68
|
end
|
80
69
|
end
|
81
70
|
end
|
82
71
|
# TODO Add data type specific specs here
|
72
|
+
context "with data type money" do
|
73
|
+
let(:data_type) { "money" }
|
74
|
+
|
75
|
+
before do
|
76
|
+
# Note that PostgreSQL's information_schema table does not have these
|
77
|
+
# values for the money type. The user of this method has to populate
|
78
|
+
# these values manually.
|
79
|
+
information_schema_columns["numeric_precision"] = numeric_precision
|
80
|
+
information_schema_columns["numeric_scale"] = numeric_scale
|
81
|
+
end
|
82
|
+
|
83
|
+
let(:numeric_precision) { 19 }
|
84
|
+
|
85
|
+
context 'when the money scale is 0' do
|
86
|
+
let(:numeric_scale) { 0 }
|
87
|
+
|
88
|
+
it { is_expected.to eq "money(19,0)" }
|
89
|
+
end
|
90
|
+
context 'when the moeny scale is 2' do
|
91
|
+
let(:numeric_scale) { 2 }
|
92
|
+
|
93
|
+
it { is_expected.to eq "money(19,2)" }
|
94
|
+
end
|
95
|
+
end
|
96
|
+
context "with time data type" do
|
97
|
+
let(:data_type) { "time" }
|
98
|
+
|
99
|
+
it { is_expected.to eq "time" }
|
100
|
+
end
|
101
|
+
context "with time without time zone data type" do
|
102
|
+
let(:data_type) { "time without time zone" }
|
103
|
+
|
104
|
+
it { is_expected.to eq "time" }
|
105
|
+
end
|
106
|
+
context "with time with time zone data type" do
|
107
|
+
let(:data_type) { "time with time zone" }
|
108
|
+
|
109
|
+
it { is_expected.to eq "timetz" }
|
110
|
+
end
|
111
|
+
context "with timestamp data type" do
|
112
|
+
let(:data_type) { "timestamp" }
|
113
|
+
|
114
|
+
it { is_expected.to eq "datetime" }
|
115
|
+
end
|
116
|
+
context "with timestamp without time zone data type" do
|
117
|
+
let(:data_type) { "timestamp without time zone" }
|
118
|
+
|
119
|
+
it { is_expected.to eq "datetime" }
|
120
|
+
end
|
121
|
+
context "with timestamp with time zone data type" do
|
122
|
+
let(:data_type) { "timestamp with time zone" }
|
123
|
+
|
124
|
+
it { is_expected.to eq "datetimetz" }
|
125
|
+
end
|
126
|
+
context "with bit data type" do
|
127
|
+
let(:data_type) { "bit" }
|
128
|
+
|
129
|
+
it { is_expected.to eq "bit" }
|
130
|
+
end
|
131
|
+
context "with bit varying data type" do
|
132
|
+
let(:data_type) { "bit varying" }
|
133
|
+
|
134
|
+
it { is_expected.to eq "varbit" }
|
135
|
+
end
|
136
|
+
context "with varbit data type" do
|
137
|
+
let(:data_type) { "varbit" }
|
138
|
+
|
139
|
+
it { is_expected.to eq "varbit" }
|
140
|
+
end
|
83
141
|
context "with unknown data type" do
|
84
142
|
let(:data_type) { "duck" }
|
85
|
-
|
143
|
+
|
144
|
+
let(:type_hash) { PostgresqlTableDef::TYPE_MAP_P2F[:default] }
|
145
|
+
|
146
|
+
it { is_expected.to eq "_unsupported" }
|
86
147
|
end
|
87
148
|
context "with no data type" do
|
88
149
|
let(:data_type) { nil }
|