pgsync 0.4.3 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of pgsync might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +27 -21
- data/README.md +14 -4
- data/exe/pgsync +3 -1
- data/lib/pgsync.rb +8 -4
- data/lib/pgsync/client.rb +59 -8
- data/lib/pgsync/table_sync.rb +27 -19
- data/lib/pgsync/version.rb +1 -1
- metadata +13 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 855b6fc121c88a53bc7fda6243add1d965c880e4fcb33328d5cfd931cb7bb588
|
4
|
+
data.tar.gz: fc157143ac24e7cfad51d57bc10c2859ea8225d74a6a3376821e7ac45e8dee13
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6fbdbb036ea9ca16138af36fec1a01ae31c9b7963065bf8014ae3a09d564cdc7ecb82ee36f32531435a793ae9e933a49685e348fcaed674f702b2cce1d7b7c74
|
7
|
+
data.tar.gz: f3ff567e7e2b0720696efe209c98cc2b983969aefe28c9ee3ac68c829ccd5a755820f1d77008f1c4e86acd184f56ec08c1762a3460b08c27679e93cb1313718c
|
data/CHANGELOG.md
CHANGED
@@ -1,26 +1,32 @@
|
|
1
|
-
|
1
|
+
## 0.5.0 (2020-03-26)
|
2
|
+
|
3
|
+
- Improved output when syncing
|
4
|
+
- Improved output on interrupt
|
5
|
+
- Added `--no-sequences` option
|
6
|
+
|
7
|
+
## 0.4.3 (2019-10-27)
|
2
8
|
|
3
9
|
- Added `sslmode` to template
|
4
10
|
|
5
|
-
|
11
|
+
## 0.4.2 (2019-10-27)
|
6
12
|
|
7
13
|
- Improved flexibility of commands
|
8
14
|
- Sync all objects when no tables specified
|
9
15
|
|
10
|
-
|
16
|
+
## 0.4.1 (2018-12-15)
|
11
17
|
|
12
18
|
- Made `psql` version check more robust
|
13
19
|
- Fixed issue with non-lowercase primary key
|
14
20
|
- Prefer `--init` over `--setup`
|
15
21
|
- Improved data rules
|
16
22
|
|
17
|
-
|
23
|
+
## 0.4.0 (2018-02-28)
|
18
24
|
|
19
25
|
- Sync all schemas in search path by default
|
20
26
|
- Added support for socket connections
|
21
27
|
- Added support for environment variables
|
22
28
|
|
23
|
-
|
29
|
+
## 0.3.9 (2018-02-27)
|
24
30
|
|
25
31
|
- Better support for schemas
|
26
32
|
- Added `--schemas` option
|
@@ -28,47 +34,47 @@
|
|
28
34
|
- Added `--schema-first` option
|
29
35
|
- Fixed issue with non-lowercase tables and partial syncs
|
30
36
|
|
31
|
-
|
37
|
+
## 0.3.8 (2017-10-01)
|
32
38
|
|
33
39
|
- Added Windows support
|
34
40
|
- Added `random_string` and `random_number` replacement options
|
35
41
|
- Improved performance of `--in-batches` for large tables
|
36
42
|
|
37
|
-
|
43
|
+
## 0.3.7 (2017-08-30)
|
38
44
|
|
39
45
|
- Fixed non-lowercase tables and columns
|
40
46
|
- Fixed `--truncate` option with `--in-batches`
|
41
47
|
|
42
|
-
|
48
|
+
## 0.3.6 (2016-10-02)
|
43
49
|
|
44
50
|
- Fixed `Table does not exist in source` error
|
45
51
|
|
46
|
-
|
52
|
+
## 0.3.5 (2016-07-23)
|
47
53
|
|
48
54
|
- Support schemas other than public
|
49
55
|
|
50
|
-
|
56
|
+
## 0.3.4 (2016-04-29)
|
51
57
|
|
52
58
|
- Added `--in-batches` mode for production transfers with `--batch-size` and `--sleep`
|
53
59
|
|
54
|
-
|
60
|
+
## 0.3.3 (2016-04-25)
|
55
61
|
|
56
62
|
- Added `-d` option as an alias for `--db`
|
57
63
|
- Added support for wildcard tables
|
58
64
|
- Fixed `--schema-only` errors
|
59
65
|
|
60
|
-
|
66
|
+
## 0.3.2 (2016-04-19)
|
61
67
|
|
62
68
|
- Prefer `{1}` for interpolation
|
63
69
|
- Added `--overwrite` option
|
64
70
|
- Deprecated `--where` and `--limit`
|
65
71
|
|
66
|
-
|
72
|
+
## 0.3.1 (2016-04-06)
|
67
73
|
|
68
74
|
- Added `-t` or `--tables`, `-g` or `--groups` options
|
69
75
|
- Deprecated `tables`, `groups`, and `setup` commands
|
70
76
|
|
71
|
-
|
77
|
+
## 0.3.0 (2016-04-06)
|
72
78
|
|
73
79
|
- More powerful groups
|
74
80
|
- Overwrite rows by default when `WHERE` clause (previously truncated)
|
@@ -79,34 +85,34 @@
|
|
79
85
|
- Added `--setup` option
|
80
86
|
- Added `--truncate` option
|
81
87
|
|
82
|
-
|
88
|
+
## 0.2.4 (2016-04-04)
|
83
89
|
|
84
90
|
- Added `--preserve` option
|
85
91
|
- Added `--list` option for groups and tables
|
86
92
|
- Added `--limit` option
|
87
93
|
|
88
|
-
|
94
|
+
## 0.2.3 (2016-03-30)
|
89
95
|
|
90
96
|
- Fixed `no PostgreSQL user name specified in startup packet`
|
91
97
|
|
92
|
-
|
98
|
+
## 0.2.2 (2016-03-28)
|
93
99
|
|
94
100
|
- Added `--debug` option
|
95
101
|
|
96
|
-
|
102
|
+
## 0.2.1 (2016-03-27)
|
97
103
|
|
98
104
|
- Do not require config file
|
99
105
|
|
100
|
-
|
106
|
+
## 0.2.0 (2016-03-26)
|
101
107
|
|
102
108
|
- Fixed idle transaction timeout errors - respects `statement_timeout` as a result
|
103
109
|
- Raise error when command exits with non-zero status
|
104
110
|
|
105
|
-
|
111
|
+
## 0.1.1 (2016-03-23)
|
106
112
|
|
107
113
|
- Better support for multiple databases
|
108
114
|
- Search parent directories for config file
|
109
115
|
|
110
|
-
|
116
|
+
## 0.1.0 (2015-12-07)
|
111
117
|
|
112
118
|
- First release
|
data/README.md
CHANGED
@@ -122,6 +122,8 @@ pgsync product:123
|
|
122
122
|
|
123
123
|
### Schema
|
124
124
|
|
125
|
+
**Note:** pgsync is designed to sync data. You should use a schema migration tool to manage schema changes. The methods in this section are provided for convenience but not recommended.
|
126
|
+
|
125
127
|
Sync schema before the data
|
126
128
|
|
127
129
|
```sh
|
@@ -146,7 +148,7 @@ pgsync does not try to sync Postgres extensions.
|
|
146
148
|
|
147
149
|
## Data Protection
|
148
150
|
|
149
|
-
Always make sure your [connection is secure](https://ankane.org/postgres-sslmode-explained) when connecting to
|
151
|
+
Always make sure your [connection is secure](https://ankane.org/postgres-sslmode-explained) when connecting to a database over a network you don’t fully trust. Your best option is to connect over SSH or a VPN. Another option is to use `sslmode=verify-full`. If you don’t do this, your database credentials can be compromised.
|
150
152
|
|
151
153
|
## Sensitive Information
|
152
154
|
|
@@ -183,7 +185,7 @@ Options for replacement are:
|
|
183
185
|
- `null`
|
184
186
|
- `untouched`
|
185
187
|
|
186
|
-
Rules starting with `unique_` require the table to have a primary key.
|
188
|
+
Rules starting with `unique_` require the table to have a primary key. `unique_phone` requires a numeric primary key.
|
187
189
|
|
188
190
|
## Multiple Databases
|
189
191
|
|
@@ -280,6 +282,14 @@ gem install specific_install
|
|
280
282
|
gem specific_install https://github.com/ankane/pgsync.git
|
281
283
|
```
|
282
284
|
|
285
|
+
## Related Projects
|
286
|
+
|
287
|
+
Also check out:
|
288
|
+
|
289
|
+
- [Dexter](https://github.com/ankane/dexter) - The automatic indexer for Postgres
|
290
|
+
- [PgHero](https://github.com/ankane/pghero) - A performance dashboard for Postgres
|
291
|
+
- [pgslice](https://github.com/ankane/pgslice) - Postgres partitioning as easy as pie
|
292
|
+
|
283
293
|
## Thanks
|
284
294
|
|
285
295
|
Inspired by [heroku-pg-transfer](https://github.com/ddollar/heroku-pg-transfer).
|
@@ -293,7 +303,7 @@ Everyone is encouraged to help improve this project. Here are a few ways you can
|
|
293
303
|
- Write, clarify, or fix documentation
|
294
304
|
- Suggest or add new features
|
295
305
|
|
296
|
-
To
|
306
|
+
To get started with development:
|
297
307
|
|
298
308
|
```sh
|
299
309
|
git clone https://github.com/ankane/pgsync.git
|
@@ -304,5 +314,5 @@ createdb pgsync_test1
|
|
304
314
|
createdb pgsync_test2
|
305
315
|
createdb pgsync_test3
|
306
316
|
|
307
|
-
bundle exec rake
|
317
|
+
bundle exec rake test
|
308
318
|
```
|
data/exe/pgsync
CHANGED
data/lib/pgsync.rb
CHANGED
@@ -1,17 +1,21 @@
|
|
1
|
+
# dependencies
|
2
|
+
require "parallel"
|
3
|
+
require "pg"
|
4
|
+
require "slop"
|
5
|
+
require "tty-spinner"
|
6
|
+
|
7
|
+
# stdlib
|
1
8
|
require "cgi"
|
2
9
|
require "erb"
|
3
10
|
require "fileutils"
|
4
|
-
require "multiprocessing"
|
5
|
-
require "pg"
|
6
|
-
require "parallel"
|
7
11
|
require "set"
|
8
12
|
require "shellwords"
|
9
|
-
require "slop"
|
10
13
|
require "tempfile"
|
11
14
|
require "thread" # windows only
|
12
15
|
require "uri"
|
13
16
|
require "yaml"
|
14
17
|
|
18
|
+
# modules
|
15
19
|
require "pgsync/client"
|
16
20
|
require "pgsync/data_source"
|
17
21
|
require "pgsync/table_list"
|
data/lib/pgsync/client.rb
CHANGED
@@ -5,7 +5,6 @@ module PgSync
|
|
5
5
|
$stderr.sync = true
|
6
6
|
@exit = false
|
7
7
|
@arguments, @options = parse_args(args)
|
8
|
-
@mutex = windows? ? Mutex.new : MultiProcessing::Mutex.new
|
9
8
|
end
|
10
9
|
|
11
10
|
# TODO clean up this mess
|
@@ -92,8 +91,8 @@ module PgSync
|
|
92
91
|
unless opts[:schema_only]
|
93
92
|
confirm_tables_exist(destination, tables, "destination")
|
94
93
|
|
95
|
-
in_parallel(tables) do |table, table_opts|
|
96
|
-
TableSync.new.sync(
|
94
|
+
in_parallel(tables, first_schema: source.search_path.find { |sp| sp != "pg_catalog" }) do |table, table_opts|
|
95
|
+
TableSync.new.sync(config, table, opts.merge(table_opts), source.url, destination.url)
|
97
96
|
end
|
98
97
|
end
|
99
98
|
|
@@ -181,6 +180,7 @@ Options:}
|
|
181
180
|
o.boolean "--schema-only", "schema only", default: false
|
182
181
|
o.boolean "--all-schemas", "all schemas", default: false
|
183
182
|
o.boolean "--no-rules", "do not apply data rules", default: false
|
183
|
+
o.boolean "--no-sequences", "do not sync sequences", default: false
|
184
184
|
o.boolean "--init", "init", default: false
|
185
185
|
o.boolean "--setup", "setup", default: false, help: false
|
186
186
|
o.boolean "--in-batches", "in batches", default: false, help: false
|
@@ -267,14 +267,56 @@ Options:}
|
|
267
267
|
$stderr.puts message
|
268
268
|
end
|
269
269
|
|
270
|
-
def in_parallel(tables, &block)
|
270
|
+
def in_parallel(tables, first_schema:, &block)
|
271
|
+
spinners = TTY::Spinner::Multi.new(format: :dots)
|
272
|
+
item_spinners = {}
|
273
|
+
|
274
|
+
start = lambda do |item, i|
|
275
|
+
table, opts = item
|
276
|
+
message = String.new(":spinner ")
|
277
|
+
message << table.sub("#{first_schema}.", "")
|
278
|
+
# maybe output later
|
279
|
+
# message << " #{opts[:sql]}" if opts[:sql]
|
280
|
+
spinner = spinners.register(message)
|
281
|
+
spinner.auto_spin
|
282
|
+
item_spinners[item] = spinner
|
283
|
+
end
|
284
|
+
|
285
|
+
errors = 0
|
286
|
+
|
287
|
+
finish = lambda do |item, i, result|
|
288
|
+
spinner = item_spinners[item]
|
289
|
+
if result[:status] == "success"
|
290
|
+
spinner.success(display_message(result))
|
291
|
+
else
|
292
|
+
# TODO add option to fail fast
|
293
|
+
spinner.error(display_message(result))
|
294
|
+
errors += 1
|
295
|
+
end
|
296
|
+
|
297
|
+
unless spinner.send(:tty?)
|
298
|
+
status = result[:status] == "success" ? "✔" : "✖"
|
299
|
+
log [status, item.first.sub("#{first_schema}.", ""), display_message(result)].compact.join(" ")
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
options = {start: start, finish: finish}
|
271
304
|
if @options[:debug] || @options[:in_batches]
|
272
|
-
|
305
|
+
options[:in_processes] = 0
|
273
306
|
else
|
274
|
-
options = {}
|
275
307
|
options[:in_threads] = 4 if windows?
|
276
|
-
Parallel.each(tables, options, &block)
|
277
308
|
end
|
309
|
+
|
310
|
+
Parallel.each(tables, **options, &block)
|
311
|
+
|
312
|
+
raise PgSync::Error, "Synced failed for #{errors} table#{errors == 1 ? nil : "s"}" if errors > 0
|
313
|
+
end
|
314
|
+
|
315
|
+
def display_message(result)
|
316
|
+
message = String.new("")
|
317
|
+
message << "- #{result[:time]}s" if result[:time]
|
318
|
+
message << "(#{result[:message].gsub("\n", " ").strip})" if result[:message]
|
319
|
+
message
|
278
320
|
end
|
279
321
|
|
280
322
|
def pretty_list(items)
|
@@ -289,11 +331,20 @@ Options:}
|
|
289
331
|
|
290
332
|
def log_completed(start_time)
|
291
333
|
time = Time.now - start_time
|
292
|
-
|
334
|
+
message = "Completed in #{time.round(1)}s"
|
335
|
+
log self.class.colorize(message, 32) # green
|
293
336
|
end
|
294
337
|
|
295
338
|
def windows?
|
296
339
|
Gem.win_platform?
|
297
340
|
end
|
341
|
+
|
342
|
+
def self.colorize(message, color_code)
|
343
|
+
if $stderr.tty?
|
344
|
+
"\e[#{color_code}m#{message}\e[0m"
|
345
|
+
else
|
346
|
+
message
|
347
|
+
end
|
348
|
+
end
|
298
349
|
end
|
299
350
|
end
|
data/lib/pgsync/table_sync.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module PgSync
|
2
2
|
class TableSync
|
3
|
-
def sync(
|
3
|
+
def sync(config, table, opts, source_url, destination_url)
|
4
4
|
start_time = Time.now
|
5
5
|
source = DataSource.new(source_url, timeout: 0)
|
6
6
|
destination = DataSource.new(destination_url, timeout: 0)
|
@@ -17,30 +17,32 @@ module PgSync
|
|
17
17
|
extra_fields = to_fields - from_fields
|
18
18
|
missing_fields = from_fields - to_fields
|
19
19
|
|
20
|
-
|
21
|
-
|
20
|
+
if opts[:no_sequences]
|
21
|
+
from_sequences = []
|
22
|
+
to_sequences = []
|
23
|
+
else
|
24
|
+
from_sequences = source.sequences(table, shared_fields)
|
25
|
+
to_sequences = destination.sequences(table, shared_fields)
|
26
|
+
end
|
27
|
+
|
22
28
|
shared_sequences = to_sequences & from_sequences
|
23
29
|
extra_sequences = to_sequences - from_sequences
|
24
30
|
missing_sequences = from_sequences - to_sequences
|
25
31
|
|
26
32
|
sql_clause = String.new
|
27
33
|
|
28
|
-
|
34
|
+
if opts[:sql]
|
35
|
+
sql_clause << " #{opts[:sql]}"
|
36
|
+
end
|
29
37
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
36
|
-
log " Extra columns: #{extra_fields.join(", ")}" if extra_fields.any?
|
37
|
-
log " Missing columns: #{missing_fields.join(", ")}" if missing_fields.any?
|
38
|
-
log " Extra sequences: #{extra_sequences.join(", ")}" if extra_sequences.any?
|
39
|
-
log " Missing sequences: #{missing_sequences.join(", ")}" if missing_sequences.any?
|
38
|
+
notes = []
|
39
|
+
notes << "Extra columns: #{extra_fields.join(", ")}" if extra_fields.any?
|
40
|
+
notes << "Missing columns: #{missing_fields.join(", ")}" if missing_fields.any?
|
41
|
+
notes << "Extra sequences: #{extra_sequences.join(", ")}" if extra_sequences.any?
|
42
|
+
notes << "Missing sequences: #{missing_sequences.join(", ")}" if missing_sequences.any?
|
40
43
|
|
41
|
-
|
42
|
-
|
43
|
-
end
|
44
|
+
if shared_fields.empty?
|
45
|
+
return {status: "success", message: "No fields to copy"}
|
44
46
|
end
|
45
47
|
|
46
48
|
if shared_fields.any?
|
@@ -147,13 +149,19 @@ module PgSync
|
|
147
149
|
to_connection.exec("SELECT setval(#{escape(seq)}, #{escape(value)})")
|
148
150
|
end
|
149
151
|
end
|
150
|
-
|
151
|
-
|
152
|
+
|
153
|
+
message = nil
|
154
|
+
if notes.any?
|
155
|
+
message = notes.join(", ")
|
152
156
|
end
|
157
|
+
|
158
|
+
{status: "success", message: message, time: (Time.now - start_time).round(1)}
|
153
159
|
ensure
|
154
160
|
source.close
|
155
161
|
destination.close
|
156
162
|
end
|
163
|
+
rescue PgSync::Error => e
|
164
|
+
{status: "error", message: e.message}
|
157
165
|
end
|
158
166
|
|
159
167
|
private
|
data/lib/pgsync/version.rb
CHANGED
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pgsync
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-03-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: parallel
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
@@ -25,47 +25,47 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: pg
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 0.18.2
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 0.18.2
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: slop
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 4.2.0
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 4.2.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: tty-spinner
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: '0'
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
68
|
+
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: bundler
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -145,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
145
145
|
- !ruby/object:Gem::Version
|
146
146
|
version: '0'
|
147
147
|
requirements: []
|
148
|
-
rubygems_version: 3.
|
148
|
+
rubygems_version: 3.1.2
|
149
149
|
signing_key:
|
150
150
|
specification_version: 4
|
151
151
|
summary: Sync Postgres data between databases
|