table_copy 0.0.6 → 0.0.7
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/lib/table_copy/copier.rb +6 -3
- data/lib/table_copy/pg/destination.rb +8 -2
- data/lib/table_copy/version.rb +1 -1
- data/spec/db.rb +110 -0
- data/spec/lib/table_copy/copier_spec.rb +196 -2
- data/spec/lib/table_copy/pg/destination_spec.rb +51 -77
- data/spec/spec_helper.rb +7 -1
- metadata +4 -3
- data/config/database.yml +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e1b6fbde1c068309a2bb36fb4aeba74a6f56efcc
|
4
|
+
data.tar.gz: bfafe52a0a0bf3792293cb9c8a58bae71a288b2a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f875ce996caa3a33633af3a04368d2ce8d3a0ac0787cee6c1c13ba22ccf908869bde6dfb39f4b2def4ca20ba60ec15865e5ecd7e3fbcaabc83cbbb1cfdac6b9e
|
7
|
+
data.tar.gz: 5e193bcf30ec23fa9a53f11785e1fdcb990ad78c31fe51bd0faac515bfa378a2f394f0d18107248c4a73938de1e71954e311595f6c041219ebccefb775192071
|
data/lib/table_copy/copier.rb
CHANGED
@@ -23,17 +23,20 @@ module TableCopy
|
|
23
23
|
|
24
24
|
def droppy
|
25
25
|
logger.info { "Droppy #{destination_table.table_name}" }
|
26
|
+
views = destination_table.query_views
|
27
|
+
|
26
28
|
destination_table.transaction do
|
27
|
-
views = destination_table.query_views
|
28
29
|
destination_table.drop(cascade: true)
|
29
30
|
create_table
|
30
|
-
destination_table.create_views(views)
|
31
|
-
logger.info { "Created #{views.count} views for #{destination_table.table_name}" }
|
32
31
|
moved_count = destination_table.copy_data_from(source_table)
|
33
32
|
logger.info { "#{moved_count} rows moved to #{destination_table.table_name}" }
|
34
33
|
destination_table.create_indexes
|
35
34
|
logger.info { "Completed #{source_table.indexes.count} indexes on #{destination_table.table_name}." }
|
36
35
|
end
|
36
|
+
|
37
|
+
destination_table.create_views(views).each do |view_name, view_status|
|
38
|
+
logger.info { "#{view_status ? 'Created' : 'Failed to create'} view #{view_name} for #{destination_table.table_name}" }
|
39
|
+
end
|
37
40
|
end
|
38
41
|
|
39
42
|
def find_deletes
|
@@ -109,8 +109,14 @@ module TableCopy
|
|
109
109
|
|
110
110
|
def create_views(views)
|
111
111
|
with_conn do |conn|
|
112
|
-
views.
|
113
|
-
|
112
|
+
views.inject({}) do |result, view|
|
113
|
+
begin
|
114
|
+
conn.exec("create or replace view #{view['viewname']} as (#{view['definition'].gsub(/;\z/, '')})")
|
115
|
+
result[view['viewname']] = true
|
116
|
+
rescue ::PG::UndefinedTable, ::PG::UndefinedColumn => e
|
117
|
+
result[view['viewname']] = false
|
118
|
+
end
|
119
|
+
result
|
114
120
|
end
|
115
121
|
end
|
116
122
|
end
|
data/lib/table_copy/version.rb
CHANGED
data/spec/db.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
class DB
|
2
|
+
attr_reader :table_name, :view_name, :conn
|
3
|
+
|
4
|
+
def initialize(table_name: 'table_name', view_name: 'view_name', conn: $pg_conn)
|
5
|
+
@table_name = table_name
|
6
|
+
@view_name = view_name
|
7
|
+
@conn = conn
|
8
|
+
end
|
9
|
+
|
10
|
+
def with_conn
|
11
|
+
yield conn
|
12
|
+
end
|
13
|
+
|
14
|
+
def table_exists?(name=table_name)
|
15
|
+
conn.exec("select count(*) from pg_tables where tablename='#{name}'").first['count'] == '1'
|
16
|
+
end
|
17
|
+
|
18
|
+
def view_exists?(name=view_name)
|
19
|
+
conn.exec("select count(*) from pg_views where viewname='#{name}'").first['count'] == '1'
|
20
|
+
end
|
21
|
+
|
22
|
+
def insert_data(name=table_name)
|
23
|
+
conn.exec("insert into #{name} values(#{next_val}, 'foo', '{bar, baz}')")
|
24
|
+
end
|
25
|
+
|
26
|
+
def row_count(name=table_name)
|
27
|
+
conn.exec("select count(*) from #{name}").first['count'].to_i
|
28
|
+
end
|
29
|
+
|
30
|
+
def create_table(name=table_name)
|
31
|
+
conn.exec("create table #{name} (column1 integer, column2 varchar(123), column3 varchar(256)[])")
|
32
|
+
end
|
33
|
+
|
34
|
+
def create_view(name=view_name, t_name: table_name)
|
35
|
+
conn.exec("create view #{name} as (select * from #{t_name})")
|
36
|
+
end
|
37
|
+
|
38
|
+
def drop_table(name=table_name)
|
39
|
+
conn.exec("drop table if exists #{name} cascade")
|
40
|
+
end
|
41
|
+
|
42
|
+
def indexes(name=table_name)
|
43
|
+
conn.exec(indexes_sql(name))
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_field(name, t_name: table_name)
|
47
|
+
conn.exec("alter table #{t_name} add #{name} #{data_types.sample}")
|
48
|
+
end
|
49
|
+
|
50
|
+
def drop_field(name, t_name: table_name)
|
51
|
+
conn.exec("alter table #{t_name} drop column #{name}")
|
52
|
+
end
|
53
|
+
|
54
|
+
def has_field?(name, t_name: table_name)
|
55
|
+
conn.exec("select #{name} from #{t_name}")
|
56
|
+
true
|
57
|
+
rescue PG::UndefinedColumn
|
58
|
+
false
|
59
|
+
end
|
60
|
+
|
61
|
+
def delete_row(name=table_name, pk=nil)
|
62
|
+
conn.exec("delete from #{name} where column1=#{pk || min_pk(name)}")
|
63
|
+
end
|
64
|
+
|
65
|
+
def exec(sql)
|
66
|
+
conn.exec(sql)
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def min_pk(name)
|
72
|
+
conn.exec("select min(column1) from #{name}").first['min']
|
73
|
+
end
|
74
|
+
|
75
|
+
def next_val
|
76
|
+
@next_val ||= 0
|
77
|
+
@next_val += 1
|
78
|
+
end
|
79
|
+
|
80
|
+
def data_types
|
81
|
+
[
|
82
|
+
'integer',
|
83
|
+
'varchar(123)',
|
84
|
+
'varchar(256)[]'
|
85
|
+
]
|
86
|
+
end
|
87
|
+
|
88
|
+
def indexes_sql(name)
|
89
|
+
<<-SQL
|
90
|
+
select
|
91
|
+
i.relname as index_name,
|
92
|
+
a.attname as column_name
|
93
|
+
from
|
94
|
+
pg_class t,
|
95
|
+
pg_class i,
|
96
|
+
pg_index ix,
|
97
|
+
pg_attribute a
|
98
|
+
where
|
99
|
+
t.oid = ix.indrelid
|
100
|
+
and i.oid = ix.indexrelid
|
101
|
+
and a.attrelid = t.oid
|
102
|
+
and a.attnum = ANY(ix.indkey)
|
103
|
+
and t.relkind = 'r'
|
104
|
+
and t.relname = '#{name}'
|
105
|
+
order by
|
106
|
+
t.relname,
|
107
|
+
i.relname;
|
108
|
+
SQL
|
109
|
+
end
|
110
|
+
end
|
@@ -92,7 +92,7 @@ describe TableCopy::Copier do
|
|
92
92
|
expect(destination).to receive(:drop).with(cascade: true)
|
93
93
|
expect(source).to receive(:fields_ddl).and_return(fields_ddl)
|
94
94
|
expect(destination).to receive(:create).with(fields_ddl)
|
95
|
-
expect(destination).to receive(:create_views).with('views')
|
95
|
+
expect(destination).to receive(:create_views).with('views').and_return([])
|
96
96
|
expect(destination).to receive(:copy_data_from).with(source)
|
97
97
|
expect(destination).to receive(:create_indexes)
|
98
98
|
copier.droppy
|
@@ -122,7 +122,201 @@ describe TableCopy::Copier do
|
|
122
122
|
end
|
123
123
|
end
|
124
124
|
end
|
125
|
-
|
126
125
|
end
|
127
126
|
|
127
|
+
describe 'integration tests', speed: 'slow' do
|
128
|
+
let(:db1) { DB.new(conn: $pg_conn, table_name: table_name1) }
|
129
|
+
let(:db2) { DB.new(conn: $pg_conn2, table_name: table_name2) }
|
130
|
+
|
131
|
+
let(:table_name1) { 'table_name1' }
|
132
|
+
let(:table_name2) { 'table_name2' }
|
133
|
+
|
134
|
+
let(:source) { TableCopy::PG::Source.new(
|
135
|
+
table_name: table_name1,
|
136
|
+
conn_method: db1.method(:with_conn)
|
137
|
+
) }
|
138
|
+
|
139
|
+
let(:sequence_field) { 'column1' }
|
140
|
+
|
141
|
+
let(:dest) { TableCopy::PG::Destination.new(
|
142
|
+
table_name: table_name2,
|
143
|
+
conn_method: db2.method(:with_conn),
|
144
|
+
primary_key: 'column1',
|
145
|
+
sequence_field: sequence_field,
|
146
|
+
indexes: source.indexes,
|
147
|
+
fields: source.fields
|
148
|
+
)}
|
149
|
+
|
150
|
+
let(:copier) { TableCopy::Copier.new(source, dest) }
|
151
|
+
|
152
|
+
before do
|
153
|
+
db1.create_table
|
154
|
+
end
|
155
|
+
|
156
|
+
after do
|
157
|
+
db1.drop_table
|
158
|
+
end
|
159
|
+
|
160
|
+
describe '#update' do
|
161
|
+
context 'no destination table' do
|
162
|
+
after { db2.drop_table }
|
163
|
+
|
164
|
+
it 'creates the table' do
|
165
|
+
expect {
|
166
|
+
copier.update
|
167
|
+
}.to change {
|
168
|
+
db2.table_exists?
|
169
|
+
}.from(false).to(true)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
context 'destination table exists' do
|
174
|
+
before do
|
175
|
+
db2.create_table
|
176
|
+
end
|
177
|
+
|
178
|
+
after do
|
179
|
+
db2.drop_table
|
180
|
+
end
|
181
|
+
|
182
|
+
before do
|
183
|
+
db1.insert_data
|
184
|
+
db1.insert_data
|
185
|
+
db2.insert_data
|
186
|
+
end
|
187
|
+
|
188
|
+
context 'a max sequence is available' do
|
189
|
+
it 'updates the table with new data' do
|
190
|
+
expect {
|
191
|
+
copier.update
|
192
|
+
}.to change {
|
193
|
+
db2.row_count
|
194
|
+
}.from(1).to(2)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
context 'no max sequence is available' do
|
199
|
+
let(:sequence_field) { nil }
|
200
|
+
|
201
|
+
it 'updates the table with new data' do
|
202
|
+
expect(destination.max_sequence).to eq nil
|
203
|
+
|
204
|
+
expect {
|
205
|
+
copier.update
|
206
|
+
}.to change {
|
207
|
+
db2.row_count
|
208
|
+
}.from(1).to(2)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
context 'a field is added' do
|
213
|
+
let(:new_field) { 'new_field' }
|
214
|
+
|
215
|
+
before do
|
216
|
+
db1.add_field(new_field)
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'adds the field to the destination table' do
|
220
|
+
expect {
|
221
|
+
copier.update
|
222
|
+
}.to change {
|
223
|
+
db2.has_field?(new_field)
|
224
|
+
}.from(false).to(true)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
context 'within a transaction in the destination' do
|
231
|
+
before do
|
232
|
+
db2.create_table
|
233
|
+
end
|
234
|
+
|
235
|
+
after do
|
236
|
+
db2.drop_table
|
237
|
+
end
|
238
|
+
|
239
|
+
describe '#droppy' do
|
240
|
+
let(:new_field) { 'new_field' }
|
241
|
+
|
242
|
+
before do
|
243
|
+
db1.add_field(new_field)
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'drops and rebuilds the destination table' do
|
247
|
+
expect {
|
248
|
+
copier.droppy
|
249
|
+
}.to change {
|
250
|
+
db2.has_field?(new_field)
|
251
|
+
}.from(false).to(true)
|
252
|
+
end
|
253
|
+
|
254
|
+
context 'with pre-existing views' do
|
255
|
+
before do
|
256
|
+
db2.create_view
|
257
|
+
end
|
258
|
+
|
259
|
+
it 'rebuilds views' do
|
260
|
+
expect {
|
261
|
+
copier.droppy
|
262
|
+
}.not_to change {
|
263
|
+
db2.view_exists?
|
264
|
+
}.from(true)
|
265
|
+
end
|
266
|
+
|
267
|
+
context 'a view becomes invalid' do
|
268
|
+
before do
|
269
|
+
db1.drop_field('column2')
|
270
|
+
db2.exec("create view view_name2 as (select column1 from #{db2.table_name})")
|
271
|
+
end
|
272
|
+
|
273
|
+
it 'rebuilds valid views' do
|
274
|
+
expect {
|
275
|
+
copier.droppy
|
276
|
+
}.to change {
|
277
|
+
db2.view_exists?
|
278
|
+
}.from(true).to(false)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
context 'destination has rows absent from source' do
|
285
|
+
before { 3.times { db2.insert_data } }
|
286
|
+
|
287
|
+
describe '#find_deletes' do
|
288
|
+
it 'finds and removes deleted rows' do
|
289
|
+
expect {
|
290
|
+
copier.find_deletes
|
291
|
+
}.to change {
|
292
|
+
db2.row_count
|
293
|
+
}.from(3).to(0)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
describe '#diffy' do
|
298
|
+
before do
|
299
|
+
5.times { db1.insert_data }
|
300
|
+
db1.delete_row
|
301
|
+
end
|
302
|
+
|
303
|
+
it 'copies data from source' do
|
304
|
+
expect {
|
305
|
+
copier.diffy
|
306
|
+
}.to change {
|
307
|
+
db2.row_count
|
308
|
+
}.from(3).to(4) # +2 -1
|
309
|
+
end
|
310
|
+
|
311
|
+
it 'finds and removes deleted rows' do
|
312
|
+
expect {
|
313
|
+
copier.diffy
|
314
|
+
}.to change {
|
315
|
+
db2.row_count
|
316
|
+
}.from(3).to(4) # +2 -1
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
128
322
|
end
|
@@ -2,59 +2,13 @@ require 'table_copy/pg/destination'
|
|
2
2
|
require 'table_copy/pg/index'
|
3
3
|
|
4
4
|
describe TableCopy::PG::Destination do
|
5
|
-
let(:conn) { $pg_conn }
|
6
5
|
let(:table_name) { 'table_name' }
|
7
6
|
let(:view_name) { 'view_name' }
|
8
|
-
let(:
|
9
|
-
<<-SQL
|
10
|
-
select
|
11
|
-
i.relname as index_name,
|
12
|
-
a.attname as column_name
|
13
|
-
from
|
14
|
-
pg_class t,
|
15
|
-
pg_class i,
|
16
|
-
pg_index ix,
|
17
|
-
pg_attribute a
|
18
|
-
where
|
19
|
-
t.oid = ix.indrelid
|
20
|
-
and i.oid = ix.indexrelid
|
21
|
-
and a.attrelid = t.oid
|
22
|
-
and a.attnum = ANY(ix.indkey)
|
23
|
-
and t.relkind = 'r'
|
24
|
-
and t.relname = '#{table_name}'
|
25
|
-
order by
|
26
|
-
t.relname,
|
27
|
-
i.relname;
|
28
|
-
SQL
|
29
|
-
}
|
30
|
-
|
31
|
-
def with_conn
|
32
|
-
yield conn
|
33
|
-
end
|
34
|
-
|
35
|
-
def table_exists?(name=table_name)
|
36
|
-
conn.exec("select count(*) from pg_tables where tablename='#{name}'").first['count'] == '1'
|
37
|
-
end
|
38
|
-
|
39
|
-
def view_exists?(name=view_name)
|
40
|
-
conn.exec("select count(*) from pg_views where viewname='#{name}'").first['count'] == '1'
|
41
|
-
end
|
42
|
-
|
43
|
-
def insert_data(name=table_name)
|
44
|
-
conn.exec("insert into #{name} values(1, 'foo', '{bar, baz}')")
|
45
|
-
end
|
46
|
-
|
47
|
-
def row_count(name=table_name)
|
48
|
-
conn.exec("select count(*) from #{name}").first['count'].to_i
|
49
|
-
end
|
50
|
-
|
51
|
-
def create_table(name=table_name)
|
52
|
-
conn.exec("create table #{name} (column1 integer, column2 varchar(123), column3 varchar(256)[])")
|
53
|
-
end
|
7
|
+
let(:db) { DB.new(table_name: table_name, view_name: view_name) }
|
54
8
|
|
55
9
|
let(:dest) { TableCopy::PG::Destination.new(
|
56
10
|
table_name: table_name,
|
57
|
-
conn_method: method(:with_conn),
|
11
|
+
conn_method: db.method(:with_conn),
|
58
12
|
indexes: [ TableCopy::PG::Index.new(table_name, nil, ['column1']) ],
|
59
13
|
fields: [ 'column1', 'column2', 'column3' ],
|
60
14
|
primary_key: 'column1',
|
@@ -62,7 +16,7 @@ describe TableCopy::PG::Destination do
|
|
62
16
|
)}
|
63
17
|
|
64
18
|
after do
|
65
|
-
|
19
|
+
db.drop_table
|
66
20
|
end
|
67
21
|
|
68
22
|
describe '#to_s' do
|
@@ -73,7 +27,7 @@ describe TableCopy::PG::Destination do
|
|
73
27
|
|
74
28
|
context 'a table exists' do
|
75
29
|
before do
|
76
|
-
create_table
|
30
|
+
db.create_table
|
77
31
|
end
|
78
32
|
|
79
33
|
let(:expected_view) {
|
@@ -87,7 +41,7 @@ describe TableCopy::PG::Destination do
|
|
87
41
|
|
88
42
|
context 'a view exists' do
|
89
43
|
before do
|
90
|
-
|
44
|
+
db.create_view
|
91
45
|
end
|
92
46
|
|
93
47
|
describe '#query_views' do
|
@@ -102,15 +56,35 @@ describe TableCopy::PG::Destination do
|
|
102
56
|
expect {
|
103
57
|
dest.create_views(expected_view)
|
104
58
|
}.to change {
|
105
|
-
view_exists?
|
59
|
+
db.view_exists?
|
106
60
|
}.from(false).to(true)
|
107
61
|
end
|
62
|
+
|
63
|
+
it 'returns a hash of name => success' do
|
64
|
+
expect(dest.create_views(expected_view)).to eq({ view_name => true })
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'a view fails to be created' do
|
68
|
+
let(:failing_view) { {
|
69
|
+
"viewname" => 'another_view_name',
|
70
|
+
"definition" => "SELECT #{table_name}.column_does_no_exist FROM #{table_name};"
|
71
|
+
} }
|
72
|
+
|
73
|
+
let(:expected_result) { {
|
74
|
+
view_name => true,
|
75
|
+
'another_view_name' => false
|
76
|
+
} }
|
77
|
+
|
78
|
+
it 'returns a hash of name => success' do
|
79
|
+
expect(dest.create_views(expected_view << failing_view)).to eq(expected_result)
|
80
|
+
end
|
81
|
+
end
|
108
82
|
end
|
109
83
|
|
110
84
|
describe '#none?' do
|
111
85
|
it 'indicates whether the table has any data' do
|
112
86
|
expect {
|
113
|
-
insert_data
|
87
|
+
db.insert_data
|
114
88
|
}.to change {
|
115
89
|
dest.none?
|
116
90
|
}.from(true).to(false)
|
@@ -122,10 +96,10 @@ describe TableCopy::PG::Destination do
|
|
122
96
|
it 'opens and commits a transaction' do
|
123
97
|
expect {
|
124
98
|
dest.transaction do
|
125
|
-
insert_data
|
99
|
+
db.insert_data
|
126
100
|
end
|
127
101
|
}.to change {
|
128
|
-
|
102
|
+
db.row_count
|
129
103
|
}.by(1)
|
130
104
|
end
|
131
105
|
end
|
@@ -135,12 +109,12 @@ describe TableCopy::PG::Destination do
|
|
135
109
|
expect {
|
136
110
|
begin
|
137
111
|
dest.transaction do
|
138
|
-
insert_data
|
112
|
+
db.insert_data
|
139
113
|
raise
|
140
114
|
end
|
141
115
|
rescue RuntimeError; end
|
142
116
|
}.not_to change {
|
143
|
-
|
117
|
+
db.row_count
|
144
118
|
}
|
145
119
|
end
|
146
120
|
end
|
@@ -152,7 +126,7 @@ describe TableCopy::PG::Destination do
|
|
152
126
|
expect {
|
153
127
|
dest.drop
|
154
128
|
}.to change {
|
155
|
-
table_exists?
|
129
|
+
db.table_exists?
|
156
130
|
}.from(true).to(false)
|
157
131
|
end
|
158
132
|
end
|
@@ -162,7 +136,7 @@ describe TableCopy::PG::Destination do
|
|
162
136
|
expect {
|
163
137
|
dest.create_indexes
|
164
138
|
}.to change {
|
165
|
-
|
139
|
+
db.indexes.count
|
166
140
|
}.from(0).to(1)
|
167
141
|
end
|
168
142
|
end
|
@@ -177,7 +151,7 @@ describe TableCopy::PG::Destination do
|
|
177
151
|
context 'sequence field specified' do
|
178
152
|
let(:dest) { TableCopy::PG::Destination.new(
|
179
153
|
table_name: table_name,
|
180
|
-
conn_method: method(:with_conn),
|
154
|
+
conn_method: db.method(:with_conn),
|
181
155
|
sequence_field: 'column1'
|
182
156
|
)}
|
183
157
|
|
@@ -189,7 +163,7 @@ describe TableCopy::PG::Destination do
|
|
189
163
|
|
190
164
|
context 'with rows' do
|
191
165
|
before do
|
192
|
-
insert_data
|
166
|
+
db.insert_data
|
193
167
|
end
|
194
168
|
|
195
169
|
it 'returns the max value of the sequence field' do
|
@@ -215,25 +189,25 @@ describe TableCopy::PG::Destination do
|
|
215
189
|
expect {
|
216
190
|
dest.copy_data_from(source)
|
217
191
|
}.to change {
|
218
|
-
row_count
|
192
|
+
db.row_count
|
219
193
|
}.from(0).to(1)
|
220
194
|
end
|
221
195
|
end
|
222
196
|
|
223
197
|
context 'temp is true' do
|
224
198
|
before do
|
225
|
-
create_table("temp_#{table_name}")
|
199
|
+
db.create_table("temp_#{table_name}")
|
226
200
|
end
|
227
201
|
|
228
202
|
after do
|
229
|
-
|
203
|
+
db.drop_table("temp_#{table_name}")
|
230
204
|
end
|
231
205
|
|
232
206
|
it 'inserts data into temp table' do
|
233
207
|
expect {
|
234
208
|
dest.copy_data_from(source, temp: true)
|
235
209
|
}.to change {
|
236
|
-
row_count("temp_#{table_name}")
|
210
|
+
db.row_count("temp_#{table_name}")
|
237
211
|
}.from(0).to(1)
|
238
212
|
end
|
239
213
|
end
|
@@ -250,7 +224,7 @@ describe TableCopy::PG::Destination do
|
|
250
224
|
expect {
|
251
225
|
dest.copy_data_from(source, pk_only: true)
|
252
226
|
}.to change {
|
253
|
-
row_count
|
227
|
+
db.row_count
|
254
228
|
}.from(0).to(1)
|
255
229
|
end
|
256
230
|
end
|
@@ -266,7 +240,7 @@ describe TableCopy::PG::Destination do
|
|
266
240
|
expect {
|
267
241
|
dest.copy_data_from(source, update: 'a_value')
|
268
242
|
}.to change {
|
269
|
-
row_count
|
243
|
+
db.row_count
|
270
244
|
}.from(0).to(1)
|
271
245
|
end
|
272
246
|
end
|
@@ -274,43 +248,43 @@ describe TableCopy::PG::Destination do
|
|
274
248
|
|
275
249
|
context 'with temp table' do
|
276
250
|
before do
|
277
|
-
create_table("temp_#{table_name}")
|
251
|
+
db.create_table("temp_#{table_name}")
|
278
252
|
end
|
279
253
|
|
280
254
|
after do
|
281
|
-
|
255
|
+
db.drop_table("temp_#{table_name}")
|
282
256
|
end
|
283
257
|
|
284
258
|
describe '#copy_from_temp' do
|
285
259
|
before do
|
286
|
-
insert_data("temp_#{table_name}")
|
260
|
+
db.insert_data("temp_#{table_name}")
|
287
261
|
end
|
288
262
|
|
289
263
|
it 'upserts from the temp table' do
|
290
264
|
expect {
|
291
265
|
dest.copy_from_temp
|
292
266
|
}.to change {
|
293
|
-
row_count
|
267
|
+
db.row_count
|
294
268
|
}.from(0).to(1)
|
295
269
|
|
296
270
|
expect {
|
297
271
|
dest.copy_from_temp
|
298
272
|
}.not_to change {
|
299
|
-
row_count
|
273
|
+
db.row_count
|
300
274
|
}.from(1)
|
301
275
|
end
|
302
276
|
end
|
303
277
|
|
304
278
|
describe '#delete_not_in_temp' do
|
305
279
|
before do
|
306
|
-
insert_data
|
280
|
+
db.insert_data
|
307
281
|
end
|
308
282
|
|
309
283
|
it 'deletes row that are not in the temp table' do
|
310
284
|
expect {
|
311
285
|
dest.delete_not_in_temp
|
312
286
|
}.to change {
|
313
|
-
row_count
|
287
|
+
db.row_count
|
314
288
|
}.from(1).to(0)
|
315
289
|
end
|
316
290
|
end
|
@@ -322,7 +296,7 @@ describe TableCopy::PG::Destination do
|
|
322
296
|
expect {
|
323
297
|
dest.create('column1 integer')
|
324
298
|
}.to change {
|
325
|
-
table_exists?
|
299
|
+
db.table_exists?
|
326
300
|
}.from(false).to(true)
|
327
301
|
end
|
328
302
|
end
|
@@ -333,10 +307,10 @@ describe TableCopy::PG::Destination do
|
|
333
307
|
expect {
|
334
308
|
dest.create_temp('column1 integer')
|
335
309
|
}.to change {
|
336
|
-
table_exists?("temp_#{table_name}")
|
310
|
+
db.table_exists?("temp_#{table_name}")
|
337
311
|
}.from(false).to(true)
|
338
312
|
end
|
339
|
-
expect(table_exists?("temp_#{table_name}")).to eq false
|
313
|
+
expect(db.table_exists?("temp_#{table_name}")).to eq false
|
340
314
|
end
|
341
315
|
end
|
342
316
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -3,6 +3,7 @@ SimpleCov.start
|
|
3
3
|
|
4
4
|
require 'table_copy'
|
5
5
|
require 'pg'
|
6
|
+
require_relative 'db'
|
6
7
|
# This file was generated by the `rspec --init` command. Conventionally, all
|
7
8
|
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
8
9
|
# The generated `.rspec` file contains `--require spec_helper` which will cause this
|
@@ -24,12 +25,17 @@ RSpec.configure do |config|
|
|
24
25
|
TableCopy.logger.level = Logger::FATAL
|
25
26
|
system('dropdb table_copy_test')
|
26
27
|
system('createdb table_copy_test')
|
27
|
-
|
28
|
+
system('dropdb table_copy_test2')
|
29
|
+
system('createdb table_copy_test2')
|
30
|
+
$pg_conn = PG::Connection.new(dbname: 'table_copy_test')
|
31
|
+
$pg_conn2 = PG::Connection.new(dbname: 'table_copy_test2')
|
28
32
|
end
|
29
33
|
|
30
34
|
config.after(:suite) do
|
31
35
|
$pg_conn.close
|
36
|
+
$pg_conn2.close
|
32
37
|
system('dropdb table_copy_test')
|
38
|
+
system('dropdb table_copy_test2')
|
33
39
|
end
|
34
40
|
# The settings below are suggested to provide a good initial experience
|
35
41
|
# with RSpec, but feel free to customize to your heart's content.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: table_copy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- TLH
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-09-
|
11
|
+
date: 2014-09-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -95,7 +95,6 @@ files:
|
|
95
95
|
- README.md
|
96
96
|
- Rakefile
|
97
97
|
- bin/table_copy
|
98
|
-
- config/database.yml
|
99
98
|
- config/initializers/table_copy.rb.example
|
100
99
|
- lib/table_copy.rb
|
101
100
|
- lib/table_copy/copier.rb
|
@@ -105,6 +104,7 @@ files:
|
|
105
104
|
- lib/table_copy/pg/index.rb
|
106
105
|
- lib/table_copy/pg/source.rb
|
107
106
|
- lib/table_copy/version.rb
|
107
|
+
- spec/db.rb
|
108
108
|
- spec/lib/table_copy/copier_spec.rb
|
109
109
|
- spec/lib/table_copy/pg/destination_spec.rb
|
110
110
|
- spec/lib/table_copy/pg/field_spec.rb
|
@@ -138,6 +138,7 @@ signing_key:
|
|
138
138
|
specification_version: 4
|
139
139
|
summary: Move full tables between databases.
|
140
140
|
test_files:
|
141
|
+
- spec/db.rb
|
141
142
|
- spec/lib/table_copy/copier_spec.rb
|
142
143
|
- spec/lib/table_copy/pg/destination_spec.rb
|
143
144
|
- spec/lib/table_copy/pg/field_spec.rb
|
data/config/database.yml
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
:dbname: table_copy_test
|