pgsync 0.5.1 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b145ab9d9f312a9a043e91d5d4b9e7977bb5788e339aa4f88a571a5178d9c92f
4
- data.tar.gz: 8843f622d7b152fff80a2a465fddd55dcd4271f0256cd64b46a3898a38214d65
3
+ metadata.gz: f6c20cf99ebc0961d8699228883f6c2e9c95c9798cfb6f101cb78803df6b1b43
4
+ data.tar.gz: 7c719442c07e6db1d4704199aa2b95d32e1ba528797b2d21b15be85f2de71d79
5
5
  SHA512:
6
- metadata.gz: f210607011225b932975ddd77343c1ff9468ae824a642b3c0b48033853a2fa0d68f4eed4fcfcda5c36a6d7cc1b99e491a6a0b62f147d232c8d517721b08a6590
7
- data.tar.gz: bb38c6291b40ad0188483889aa9696269a384456ed7425be61e624a26b797110dc363a44b9d0774fc4b2ad04122d6e8863d04b7bb1a89f986c0829ada8a2aea4
6
+ metadata.gz: 1e3d735e6e3002e2fcb74de574f63f534472821aca0154c13d9d7ce6e9fa94426239cef875cd33724fec5a6cf2567b5656a80a9d92f89d9678ee6bee2c2ec70c
7
+ data.tar.gz: 3ab5d416f4c2364644c015d106631898a3615cc671179f8b1193cda5b1e894512fb39906ec1c27570ca9fbd186c9eb19af3afb88eba093bb21e8bc471c28f18b
@@ -1,3 +1,43 @@
1
+ ## 0.6.0 (2020-06-07)
2
+
3
+ - Added messages for different column types and non-deferrable constraints
4
+ - Added support for wildcards to `--exclude`
5
+ - Improved `--overwrite` and `--preserve` options for foreign keys
6
+ - Improved output for schema sync
7
+ - Fixed `--overwrite` and `--preserve` options for multicolumn primary keys
8
+ - Fixed output for notices
9
+
10
+ Breaking
11
+
12
+ - Syncs shared tables instead of raising an error when tables missing in destination
13
+ - Raise an error when `--config` or `--db` option provided and config not found
14
+ - Removed deprecated options
15
+ - Dropped support for Postgres < 9.5
16
+
17
+ ## 0.5.5 (2020-05-13)
18
+
19
+ - Added `--jobs` option
20
+ - Added `--defer-constraints` option
21
+ - Added `--disable-user-triggers` option
22
+ - Added `--disable-integrity` option
23
+ - Improved error message for older libpq
24
+
25
+ ## 0.5.4 (2020-05-09)
26
+
27
+ - Fixed output for `--in-batches`
28
+
29
+ ## 0.5.3 (2020-04-03)
30
+
31
+ - Improved Postgres error messages
32
+ - Fixed behavior of wildcard without schema
33
+
34
+ ## 0.5.2 (2020-03-27)
35
+
36
+ - Added `--fail-fast` option
37
+ - Automatically exclude tables when `--init` run inside Rails app
38
+ - Improved error message
39
+ - Fixed typo in error message
40
+
1
41
  ## 0.5.1 (2020-03-26)
2
42
 
3
43
  - Fixed Slop warning with Ruby 2.7
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2015-2019 Andrew Kane
3
+ Copyright (c) 2015-2020 Andrew Kane
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,9 +1,10 @@
1
1
  # pgsync
2
2
 
3
- Sync Postgres data between databases. Designed for:
3
+ Sync data from one Postgres database to another (like `pg_dump`/`pg_restore`). Designed for:
4
4
 
5
- - **speed** - up to 4x faster than traditional tools on a 4-core machine
5
+ - **speed** - tables are transferred in parallel
6
6
  - **security** - built-in methods to prevent sensitive data from ever leaving the server
7
+ - **flexibility** - gracefully handles schema differences, like missing columns and extra columns
7
8
  - **convenience** - sync partial tables, groups of tables, and related records
8
9
 
9
10
  :tangerine: Battle-tested at [Instacart](https://www.instacart.com/opensource)
@@ -20,6 +21,8 @@ gem install pgsync
20
21
 
21
22
  This will give you the `pgsync` command. If installation fails, you may need to install [dependencies](#dependencies).
22
23
 
24
+ ## Setup
25
+
23
26
  In your project directory, run:
24
27
 
25
28
  ```sh
@@ -30,20 +33,26 @@ This creates `.pgsync.yml` for you to customize. We recommend checking this into
30
33
 
31
34
  ## How to Use
32
35
 
36
+ First, make sure your schema is set up in both databases. We recommend using a schema migration tool for this, but pgsync also provides a few [convenience methods](#schema). Once that’s done, you’re ready to sync data.
37
+
33
38
  Sync all tables
34
39
 
35
40
  ```sh
36
41
  pgsync
37
42
  ```
38
43
 
39
- **Note:** pgsync assumes your schema is already set up on your local machine. See the [schema section](#schema) if that’s not the case.
40
-
41
44
  Sync specific tables
42
45
 
43
46
  ```sh
44
47
  pgsync table1,table2
45
48
  ```
46
49
 
50
+ Works with wildcards as well
51
+
52
+ ```sh
53
+ pgsync "table*"
54
+ ```
55
+
47
56
  Sync specific rows (existing rows are overwritten)
48
57
 
49
58
  ```sh
@@ -62,13 +71,15 @@ Or truncate them
62
71
  pgsync products "where store_id = 1" --truncate
63
72
  ```
64
73
 
65
- ### Exclude Tables
74
+ ## Tables
75
+
76
+ Exclude specific tables
66
77
 
67
78
  ```sh
68
- pgsync --exclude users
79
+ pgsync --exclude table1,table2
69
80
  ```
70
81
 
71
- To always exclude, add to `.pgsync.yml`.
82
+ Add to `.pgsync.yml` to exclude by default
72
83
 
73
84
  ```yml
74
85
  exclude:
@@ -76,15 +87,17 @@ exclude:
76
87
  - table2
77
88
  ```
78
89
 
79
- For Rails, you probably want to exclude schema migrations and ActiveRecord metadata.
90
+ Sync tables from all schemas or specific schemas (by default, only the search path is synced)
80
91
 
81
- ```yml
82
- exclude:
83
- - schema_migrations
84
- - ar_internal_metadata
92
+ ```sh
93
+ pgsync --all-schemas
94
+ # or
95
+ pgsync --schemas public,other
96
+ # or
97
+ pgsync public.table1,other.table2
85
98
  ```
86
99
 
87
- ### Groups
100
+ ## Groups
88
101
 
89
102
  Define groups in `.pgsync.yml`:
90
103
 
@@ -101,6 +114,8 @@ And run:
101
114
  pgsync group1
102
115
  ```
103
116
 
117
+ ## Variables
118
+
104
119
  You can also use groups to sync a specific record and associated records in other tables.
105
120
 
106
121
  To get product `123` with its reviews, last 10 coupons, and store, use:
@@ -120,18 +135,14 @@ And run:
120
135
  pgsync product:123
121
136
  ```
122
137
 
123
- ### Schema
138
+ ## Schema
124
139
 
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
-
127
- Sync schema before the data
140
+ Sync schema before the data (this wipes out existing data)
128
141
 
129
142
  ```sh
130
143
  pgsync --schema-first
131
144
  ```
132
145
 
133
- **Note:** This wipes out existing data
134
-
135
146
  Specify tables
136
147
 
137
148
  ```sh
@@ -146,13 +157,9 @@ pgsync --schema-only
146
157
 
147
158
  pgsync does not try to sync Postgres extensions.
148
159
 
149
- ## Data Protection
160
+ ## Sensitive Data
150
161
 
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.
152
-
153
- ## Sensitive Information
154
-
155
- Prevent sensitive information like email addresses from leaving the remote server.
162
+ Prevent sensitive data like email addresses from leaving the remote server.
156
163
 
157
164
  Define rules in `.pgsync.yml`:
158
165
 
@@ -185,31 +192,45 @@ Options for replacement are:
185
192
  - `null`
186
193
  - `untouched`
187
194
 
188
- Rules starting with `unique_` require the table to have a primary key. `unique_phone` requires a numeric primary key.
195
+ Rules starting with `unique_` require the table to have a single column primary key. `unique_phone` requires a numeric primary key.
189
196
 
190
- ## Multiple Databases
197
+ ## Foreign Keys
191
198
 
192
- To use with multiple databases, run:
199
+ Foreign keys can make it difficult to sync data. Three options are:
200
+
201
+ 1. Manually specify the order of tables
202
+ 2. Use deferrable constraints
203
+ 3. Disable foreign key triggers, which can silently break referential integrity
204
+
205
+ When manually specifying the order, use `--jobs 1` so tables are synced one-at-a-time.
193
206
 
194
207
  ```sh
195
- pgsync --init db2
208
+ pgsync table1,table2,table3 --jobs 1
196
209
  ```
197
210
 
198
- This creates `.pgsync-db2.yml` for you to edit. Specify a database in commands with:
211
+ If your tables have [deferrable constraints](https://begriffs.com/posts/2017-08-27-deferrable-sql-constraints.html), use:
199
212
 
200
213
  ```sh
201
- pgsync --db db2
214
+ pgsync --defer-constraints
202
215
  ```
203
216
 
204
- ## Safety
217
+ To disable foreign key triggers and potentially break referential integrity, use:
205
218
 
206
- To keep you from accidentally overwriting production, the destination is limited to `localhost` or `127.0.0.1` by default.
219
+ ```sh
220
+ pgsync --disable-integrity
221
+ ```
207
222
 
208
- To use another host, add `to_safe: true` to your `.pgsync.yml`.
223
+ ## Triggers
224
+
225
+ Disable user triggers with:
209
226
 
210
- ## Large Tables
227
+ ```sh
228
+ pgsync --disable-user-triggers
229
+ ```
211
230
 
212
- For extremely large tables, sync in batches.
231
+ ## Append-Only Tables
232
+
233
+ For extremely large, append-only tables, sync in batches.
213
234
 
214
235
  ```sh
215
236
  pgsync large_table --in-batches
@@ -217,15 +238,31 @@ pgsync large_table --in-batches
217
238
 
218
239
  The script will resume where it left off when run again, making it great for backfills.
219
240
 
220
- ## Foreign Keys
241
+ ## Connection Security
242
+
243
+ 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.
244
+
245
+ ## Safety
246
+
247
+ To keep you from accidentally overwriting production, the destination is limited to `localhost` or `127.0.0.1` by default.
248
+
249
+ To use another host, add `to_safe: true` to your `.pgsync.yml`.
221
250
 
222
- By default, tables are copied in parallel. If you use foreign keys, this can cause violations. You can specify tables to be copied serially with:
251
+ ## Multiple Databases
252
+
253
+ To use with multiple databases, run:
254
+
255
+ ```sh
256
+ pgsync --init db2
257
+ ```
258
+
259
+ This creates `.pgsync-db2.yml` for you to edit. Specify a database in commands with:
223
260
 
224
261
  ```sh
225
- pgsync group1 --debug
262
+ pgsync --db db2
226
263
  ```
227
264
 
228
- ## Reference
265
+ ## Other Commands
229
266
 
230
267
  Help
231
268
 
@@ -239,6 +276,12 @@ Version
239
276
  pgsync --version
240
277
  ```
241
278
 
279
+ List tables
280
+
281
+ ```sh
282
+ pgsync --list
283
+ ```
284
+
242
285
  ## Scripts
243
286
 
244
287
  Use groups when possible to take advantage of parallelism.
@@ -246,7 +289,7 @@ Use groups when possible to take advantage of parallelism.
246
289
  For Ruby scripts, you may need to do:
247
290
 
248
291
  ```rb
249
- Bundler.with_clean_env do
292
+ Bundler.with_unbundled_env do
250
293
  system "pgsync ..."
251
294
  end
252
295
  ```
data/config.yml CHANGED
@@ -13,16 +13,17 @@ from: $(some_command)?sslmode=require
13
13
  to: postgres://localhost:5432/myapp_development
14
14
 
15
15
  # exclude tables
16
- # exclude:
17
- # - schema_migrations
18
- # - ar_internal_metadata
19
-
16
+ %{exclude}
20
17
  # define groups
21
18
  # groups:
22
19
  # group1:
23
20
  # - table1
24
21
  # - table2
25
22
 
23
+ # sync specific schemas
24
+ # schemas:
25
+ # - public
26
+
26
27
  # protect sensitive information
27
28
  data_rules:
28
29
  email: unique_email
data/exe/pgsync CHANGED
@@ -1,15 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- # disable Slop warning with Ruby 2.7
4
- $VERBOSE = nil
5
-
3
+ # handle interrupts
6
4
  trap("SIGINT") { abort }
7
5
 
8
- begin
9
- require "pgsync"
10
- PgSync::Client.new(ARGV).perform
11
- rescue PgSync::Error => e
12
- abort PgSync::Client.colorize(e.message, 31) # red
13
- rescue Interrupt
14
- abort
15
- end
6
+ require "pgsync"
7
+ PgSync::Client.start
@@ -5,21 +5,24 @@ require "slop"
5
5
  require "tty-spinner"
6
6
 
7
7
  # stdlib
8
- require "cgi"
9
- require "erb"
10
- require "fileutils"
11
8
  require "set"
12
9
  require "shellwords"
13
10
  require "tempfile"
14
- require "thread" # windows only
15
11
  require "uri"
16
12
  require "yaml"
13
+ require "open3"
17
14
 
18
15
  # modules
16
+ require "pgsync/utils"
19
17
  require "pgsync/client"
20
18
  require "pgsync/data_source"
21
- require "pgsync/table_list"
19
+ require "pgsync/init"
20
+ require "pgsync/schema_sync"
21
+ require "pgsync/sync"
22
+ require "pgsync/table"
22
23
  require "pgsync/table_sync"
24
+ require "pgsync/task"
25
+ require "pgsync/task_resolver"
23
26
  require "pgsync/version"
24
27
 
25
28
  module PgSync
@@ -1,350 +1,78 @@
1
1
  module PgSync
2
2
  class Client
3
+ include Utils
4
+
3
5
  def initialize(args)
4
- $stdout.sync = true
5
- $stderr.sync = true
6
- @exit = false
7
- @arguments, @options = parse_args(args)
6
+ @args = args
7
+ output.sync = true
8
8
  end
9
9
 
10
- # TODO clean up this mess
11
10
  def perform
12
- return if @exit
13
-
14
- args, opts = @arguments, @options
15
- [:to, :from, :to_safe, :exclude, :schemas].each do |opt|
16
- opts[opt] ||= config[opt.to_s]
17
- end
18
- map_deprecations(args, opts)
19
-
20
- if opts[:init]
21
- setup(db_config_file(args[0]) || config_file || ".pgsync.yml")
22
- else
23
- sync(args, opts)
24
- end
25
-
26
- true
27
- end
28
-
29
- protected
30
-
31
- def sync(args, opts)
32
- start_time = Time.now
33
-
34
- if args.size > 2
35
- raise PgSync::Error, "Usage:\n pgsync [options]"
36
- end
37
-
38
- source = DataSource.new(opts[:from])
39
- raise PgSync::Error, "No source" unless source.exists?
40
-
41
- destination = DataSource.new(opts[:to])
42
- raise PgSync::Error, "No destination" unless destination.exists?
43
-
44
- begin
45
- # start connections
46
- source.host
47
- destination.host
48
-
49
- unless opts[:to_safe] || destination.local?
50
- raise PgSync::Error, "Danger! Add `to_safe: true` to `.pgsync.yml` if the destination is not localhost or 127.0.0.1"
51
- end
52
-
53
- print_description("From", source)
54
- print_description("To", destination)
55
- ensure
56
- source.close
57
- destination.close
58
- end
59
-
60
- tables = nil
61
- begin
62
- tables = TableList.new(args, opts, source, config).tables
63
- ensure
64
- source.close
65
- end
66
-
67
- confirm_tables_exist(source, tables, "source")
68
-
69
- if opts[:list]
70
- confirm_tables_exist(destination, tables, "destination")
71
-
72
- list_items =
73
- if args[0] == "groups"
74
- (config["groups"] || {}).keys
75
- else
76
- tables.keys
77
- end
78
-
79
- pretty_list list_items
11
+ result = Slop::Parser.new(slop_options).parse(@args)
12
+ arguments = result.arguments
13
+ options = result.to_h
14
+
15
+ raise Error, "Specify either --db or --config, not both" if options[:db] && options[:config]
16
+ raise Error, "Cannot use --overwrite with --in-batches" if options[:overwrite] && options[:in_batches]
17
+
18
+ if options[:version]
19
+ log VERSION
20
+ elsif options[:help]
21
+ log slop_options
22
+ elsif options[:init]
23
+ Init.new(arguments, options).perform
80
24
  else
81
- if opts[:schema_first] || opts[:schema_only]
82
- if opts[:preserve]
83
- raise PgSync::Error, "Cannot use --preserve with --schema-first or --schema-only"
84
- end
85
-
86
- log "* Dumping schema"
87
- schema_tables = tables if !opts[:all_schemas] || opts[:tables] || opts[:groups] || args[0] || opts[:exclude]
88
- sync_schema(source, destination, schema_tables)
89
- end
90
-
91
- unless opts[:schema_only]
92
- confirm_tables_exist(destination, tables, "destination")
93
-
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)
96
- end
97
- end
98
-
99
- log_completed(start_time)
25
+ Sync.new(arguments, options).perform
100
26
  end
27
+ rescue => e
28
+ # Error, PG::ConnectionBad, Slop::Error
29
+ raise e if options && options[:debug]
30
+ abort colorize(e.message.strip, :red)
101
31
  end
102
32
 
103
- def confirm_tables_exist(data_source, tables, description)
104
- tables.keys.each do |table|
105
- unless data_source.table_exists?(table)
106
- raise PgSync::Error, "Table does not exist in #{description}: #{table}"
107
- end
108
- end
109
- ensure
110
- data_source.close
33
+ def self.start
34
+ new(ARGV).perform
111
35
  end
112
36
 
113
- def map_deprecations(args, opts)
114
- command = args[0]
115
-
116
- case command
117
- when "setup"
118
- args.shift
119
- opts[:init] = true
120
- deprecated "Use `psync --init` instead"
121
- when "schema"
122
- args.shift
123
- opts[:schema_only] = true
124
- deprecated "Use `psync --schema-only` instead"
125
- when "tables"
126
- args.shift
127
- opts[:tables] = args.shift
128
- deprecated "Use `pgsync #{opts[:tables]}` instead"
129
- when "groups"
130
- args.shift
131
- opts[:groups] = args.shift
132
- deprecated "Use `pgsync #{opts[:groups]}` instead"
133
- end
134
-
135
- if opts[:where]
136
- opts[:sql] ||= String.new
137
- opts[:sql] << " WHERE #{opts[:where]}"
138
- deprecated "Use `\"WHERE #{opts[:where]}\"` instead"
139
- end
140
-
141
- if opts[:limit]
142
- opts[:sql] ||= String.new
143
- opts[:sql] << " LIMIT #{opts[:limit]}"
144
- deprecated "Use `\"LIMIT #{opts[:limit]}\"` instead"
145
- end
146
- end
147
-
148
- def sync_schema(source, destination, tables = nil)
149
- dump_command = source.dump_command(tables)
150
- restore_command = destination.restore_command
151
- unless system("#{dump_command} | #{restore_command}")
152
- raise PgSync::Error, "Schema sync returned non-zero exit code"
153
- end
154
- end
37
+ protected
155
38
 
156
- def parse_args(args)
157
- opts = Slop.parse(args) do |o|
158
- o.banner = %{Usage:
159
- pgsync [options]
39
+ def slop_options
40
+ o = Slop::Options.new
41
+ o.banner = %{Usage:
42
+ pgsync [options]
160
43
 
161
44
  Options:}
162
- o.string "-d", "--db", "database"
163
- o.string "-t", "--tables", "tables to sync"
164
- o.string "-g", "--groups", "groups to sync"
165
- o.string "--schemas", "schemas to sync"
166
- o.string "--from", "source"
167
- o.string "--to", "destination"
168
- o.string "--where", "where", help: false
169
- o.integer "--limit", "limit", help: false
170
- o.string "--exclude", "exclude tables"
171
- o.string "--config", "config file"
172
- # TODO much better name for this option
173
- o.boolean "--to-safe", "accept danger", default: false
174
- o.boolean "--debug", "debug", default: false
175
- o.boolean "--list", "list", default: false
176
- o.boolean "--overwrite", "overwrite existing rows", default: false, help: false
177
- o.boolean "--preserve", "preserve existing rows", default: false
178
- o.boolean "--truncate", "truncate existing rows", default: false
179
- o.boolean "--schema-first", "schema first", default: false
180
- o.boolean "--schema-only", "schema only", default: false
181
- o.boolean "--all-schemas", "all schemas", default: false
182
- o.boolean "--no-rules", "do not apply data rules", default: false
183
- o.boolean "--no-sequences", "do not sync sequences", default: false
184
- o.boolean "--init", "init", default: false
185
- o.boolean "--setup", "setup", default: false, help: false
186
- o.boolean "--in-batches", "in batches", default: false, help: false
187
- o.integer "--batch-size", "batch size", default: 10000, help: false
188
- o.float "--sleep", "sleep", default: 0, help: false
189
- o.on "-v", "--version", "print the version" do
190
- log PgSync::VERSION
191
- @exit = true
192
- end
193
- o.on "-h", "--help", "prints help" do
194
- log o
195
- @exit = true
196
- end
197
- end
198
-
199
- opts_hash = opts.to_hash
200
- opts_hash[:init] = opts_hash[:setup] if opts_hash[:setup]
201
-
202
- [opts.arguments, opts_hash]
203
- rescue Slop::Error => e
204
- raise PgSync::Error, e.message
205
- end
206
-
207
- def config
208
- @config ||= begin
209
- if config_file
210
- begin
211
- YAML.load_file(config_file) || {}
212
- rescue Psych::SyntaxError => e
213
- raise PgSync::Error, e.message
214
- end
215
- else
216
- {}
217
- end
218
- end
219
- end
220
-
221
- def setup(config_file)
222
- if File.exist?(config_file)
223
- raise PgSync::Error, "#{config_file} exists."
224
- else
225
- FileUtils.cp(File.dirname(__FILE__) + "/../../config.yml", config_file)
226
- log "#{config_file} created. Add your database credentials."
227
- end
228
- end
229
-
230
- def db_config_file(db)
231
- return unless db
232
- ".pgsync-#{db}.yml"
233
- end
234
-
235
- def print_description(prefix, source)
236
- location = " on #{source.host}:#{source.port}" if source.host
237
- log "#{prefix}: #{source.dbname}#{location}"
238
- end
239
-
240
- def search_tree(file)
241
- path = Dir.pwd
242
- # prevent infinite loop
243
- 20.times do
244
- absolute_file = File.join(path, file)
245
- if File.exist?(absolute_file)
246
- break absolute_file
247
- end
248
- path = File.dirname(path)
249
- break if path == "/"
250
- end
251
- end
252
-
253
- def config_file
254
- return @config_file if instance_variable_defined?(:@config_file)
255
-
256
- @config_file =
257
- search_tree(
258
- if @options[:db]
259
- db_config_file(@options[:db])
260
- else
261
- @options[:config] || ".pgsync.yml"
262
- end
263
- )
264
- end
265
-
266
- def log(message = nil)
267
- $stderr.puts message
268
- end
269
-
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}
304
- if @options[:debug] || @options[:in_batches]
305
- options[:in_processes] = 0
306
- else
307
- options[:in_threads] = 4 if windows?
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
320
- end
321
-
322
- def pretty_list(items)
323
- items.each do |item|
324
- log item
325
- end
326
- end
327
-
328
- def deprecated(message)
329
- log "[DEPRECATED] #{message}"
330
- end
331
-
332
- def log_completed(start_time)
333
- time = Time.now - start_time
334
- message = "Completed in #{time.round(1)}s"
335
- log self.class.colorize(message, 32) # green
336
- end
337
-
338
- def windows?
339
- Gem.win_platform?
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
45
+ o.string "-d", "--db", "database"
46
+ o.string "-t", "--tables", "tables to sync"
47
+ o.string "-g", "--groups", "groups to sync"
48
+ o.integer "-j", "--jobs", "number of tables to sync at a time"
49
+ o.string "--schemas", "schemas to sync"
50
+ o.string "--from", "source"
51
+ o.string "--to", "destination"
52
+ o.string "--exclude", "exclude tables"
53
+ o.string "--config", "config file"
54
+ o.boolean "--to-safe", "accept danger", default: false
55
+ o.boolean "--debug", "debug", default: false
56
+ o.boolean "--list", "list", default: false
57
+ o.boolean "--overwrite", "overwrite existing rows", default: false, help: false
58
+ o.boolean "--preserve", "preserve existing rows", default: false
59
+ o.boolean "--truncate", "truncate existing rows", default: false
60
+ o.boolean "--schema-first", "schema first", default: false
61
+ o.boolean "--schema-only", "schema only", default: false
62
+ o.boolean "--all-schemas", "all schemas", default: false
63
+ o.boolean "--no-rules", "do not apply data rules", default: false
64
+ o.boolean "--no-sequences", "do not sync sequences", default: false
65
+ o.boolean "--init", "init", default: false
66
+ o.boolean "--in-batches", "in batches", default: false, help: false
67
+ o.integer "--batch-size", "batch size", default: 10000, help: false
68
+ o.float "--sleep", "sleep", default: 0, help: false
69
+ o.boolean "--fail-fast", "stop on the first failed table", default: false
70
+ o.boolean "--defer-constraints", "defer constraints", default: false
71
+ o.boolean "--disable-user-triggers", "disable non-system triggers", default: false
72
+ o.boolean "--disable-integrity", "disable foreign key triggers", default: false
73
+ o.boolean "-v", "--version", "print the version"
74
+ o.boolean "-h", "--help", "prints help"
75
+ o
348
76
  end
349
77
  end
350
78
  end