table_copy 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|