pg_easy_replicate 0.3.8 → 0.4.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5e534de6c71967e1534422b3bb2871b97e267c63d130a0d21290913c8b7a94d4
4
- data.tar.gz: 718fd526277f65d4666a5cfc3e847dcb295e8e298f41893398bf1349c33d71a9
3
+ metadata.gz: 15886c32380f5cc412bc1d577795d9650381703050445523966533a8f11f6b30
4
+ data.tar.gz: dbe671357959ec3b75e2b56f09e55ef48d9f316028c9d7cd96909a0bdde65916
5
5
  SHA512:
6
- metadata.gz: 6ae2923e4c7c2727833bd2084a02aeddb04671fd7787bf573c58cde616279398bb6fe2e9ce2100b349fbcefd811942029e00504da19262ff5e0eb9d81f62f0bd
7
- data.tar.gz: 0efdf075efa3b6cdfe070a4153ae49fbb9d21f2c79d2b70ece398ec601147ae68d691899258b2648581fd9c72dfb31b218cc0df2be23bd0b5cf86990bc45adc9
6
+ metadata.gz: 650cd861e5e69039024753fb02d87957faa9bfc0ffe1fc965eea29f5fd5eb5a60c54a4e94793949dcaf5d44a1387adac9585a8ebb43457a179ebe3f4f843e2d7
7
+ data.tar.gz: be238ebb8857e2cd83ea944ab3e0cac15e5fe5e0d85fc84b745dfbdc16548c2e2c9f569ea559b3ca813d8314d6f7d2e0f1558f9310b5bc68a202957f293d4d2c
data/.rubocop.yml CHANGED
@@ -1,4 +1,4 @@
1
- require:
1
+ plugins:
2
2
  - rubocop-rspec
3
3
  - rubocop-rake
4
4
  - rubocop-performance
@@ -211,8 +211,9 @@ RSpec/DescribeClass:
211
211
  RSpec/DescribedClass:
212
212
  Enabled: false
213
213
 
214
- RSpec/FilePath:
215
- Enabled: false
214
+ # RSpec/FilePath is replaced by RSpec/SpecFilePathFormat and RSpec/SpecFilePathSuffix
215
+ # RSpec/FilePath:
216
+ # Enabled: false
216
217
 
217
218
  RSpec/IdenticalEqualityAssertion:
218
219
  Enabled: false
@@ -232,9 +233,6 @@ RSpec/NestedGroups:
232
233
  RSpec/PredicateMatcher:
233
234
  Enabled: false
234
235
 
235
- RSpec/Rails/HttpStatus:
236
- Enabled: false
237
-
238
236
  RSpec/RepeatedExampleGroupDescription:
239
237
  Enabled: false
240
238
 
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.3.6
1
+ 3.4.4
data/Dockerfile CHANGED
@@ -1,4 +1,4 @@
1
- FROM ruby:3.3.6-slim
1
+ FROM ruby:3.4.4-slim
2
2
 
3
3
  ARG VERSION
4
4
 
data/Gemfile.lock CHANGED
@@ -1,101 +1,104 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pg_easy_replicate (0.3.8)
4
+ pg_easy_replicate (0.4.0)
5
5
  ougai (~> 2.0.0)
6
- pg (~> 1.5.3)
7
- pg_query (~> 5.1.0)
8
- sequel (>= 5.69, < 5.87)
9
- thor (>= 1.2.2, < 1.4.0)
6
+ pg (>= 1.5.3, < 1.7.0)
7
+ pg_query (>= 5.1, < 6.2)
8
+ sequel (>= 5.69, < 5.98)
9
+ thor (>= 1.2.2, < 1.5.0)
10
10
 
11
11
  GEM
12
12
  remote: https://rubygems.org/
13
13
  specs:
14
- ast (2.4.2)
15
- bigdecimal (3.1.8)
14
+ ast (2.4.3)
15
+ bigdecimal (3.3.1)
16
16
  coderay (1.1.3)
17
- diff-lcs (1.5.1)
18
- google-protobuf (4.28.2-arm64-darwin)
17
+ diff-lcs (1.6.2)
18
+ google-protobuf (4.33.0-arm64-darwin)
19
19
  bigdecimal
20
20
  rake (>= 13)
21
- google-protobuf (4.28.2-x86_64-linux)
21
+ google-protobuf (4.33.0-x86_64-linux-gnu)
22
22
  bigdecimal
23
23
  rake (>= 13)
24
- haml (6.1.1)
24
+ haml (6.3.0)
25
25
  temple (>= 0.8.2)
26
26
  thor
27
27
  tilt
28
- json (2.8.2)
29
- language_server-protocol (3.17.0.3)
28
+ json (2.15.2)
29
+ language_server-protocol (3.17.0.5)
30
+ lint_roller (1.1.0)
31
+ logger (1.7.0)
30
32
  method_source (1.1.0)
31
- oj (3.14.3)
33
+ oj (3.16.11)
34
+ bigdecimal (>= 3.0)
35
+ ostruct (>= 0.2)
36
+ ostruct (0.6.3)
32
37
  ougai (2.0.0)
33
38
  oj (~> 3.10)
34
- parallel (1.26.3)
35
- parser (3.3.6.0)
39
+ parallel (1.27.0)
40
+ parser (3.3.10.0)
36
41
  ast (~> 2.4.1)
37
42
  racc
38
- pg (1.5.9)
39
- pg_query (5.1.0)
40
- google-protobuf (>= 3.22.3)
43
+ pg (1.6.2-arm64-darwin)
44
+ pg (1.6.2-x86_64-linux)
45
+ pg_query (6.1.0)
46
+ google-protobuf (>= 3.25.3)
41
47
  prettier_print (1.2.1)
42
- pry (0.15.0)
48
+ prism (1.6.0)
49
+ pry (0.15.2)
43
50
  coderay (~> 1.1)
44
51
  method_source (~> 1.0)
45
52
  racc (1.8.1)
46
53
  rainbow (3.1.1)
47
- rake (13.2.1)
48
- rbs (3.1.0)
49
- regexp_parser (2.9.2)
50
- rexml (3.3.9)
51
- rspec (3.13.0)
54
+ rake (13.3.0)
55
+ rbs (3.9.5)
56
+ logger
57
+ regexp_parser (2.11.3)
58
+ rspec (3.13.2)
52
59
  rspec-core (~> 3.13.0)
53
60
  rspec-expectations (~> 3.13.0)
54
61
  rspec-mocks (~> 3.13.0)
55
- rspec-core (3.13.0)
62
+ rspec-core (3.13.6)
56
63
  rspec-support (~> 3.13.0)
57
- rspec-expectations (3.13.0)
64
+ rspec-expectations (3.13.5)
58
65
  diff-lcs (>= 1.2.0, < 2.0)
59
66
  rspec-support (~> 3.13.0)
60
- rspec-mocks (3.13.0)
67
+ rspec-mocks (3.13.6)
61
68
  diff-lcs (>= 1.2.0, < 2.0)
62
69
  rspec-support (~> 3.13.0)
63
- rspec-support (3.13.0)
64
- rubocop (1.64.1)
70
+ rspec-support (3.13.6)
71
+ rubocop (1.81.6)
65
72
  json (~> 2.3)
66
- language_server-protocol (>= 3.17.0)
73
+ language_server-protocol (~> 3.17.0.2)
74
+ lint_roller (~> 1.1.0)
67
75
  parallel (~> 1.10)
68
76
  parser (>= 3.3.0.2)
69
77
  rainbow (>= 2.2.2, < 4.0)
70
- regexp_parser (>= 1.8, < 3.0)
71
- rexml (>= 3.2.5, < 4.0)
72
- rubocop-ast (>= 1.31.1, < 2.0)
78
+ regexp_parser (>= 2.9.3, < 3.0)
79
+ rubocop-ast (>= 1.47.1, < 2.0)
73
80
  ruby-progressbar (~> 1.7)
74
- unicode-display_width (>= 2.4.0, < 3.0)
75
- rubocop-ast (1.36.1)
76
- parser (>= 3.3.1.0)
77
- rubocop-capybara (2.20.0)
78
- rubocop (~> 1.41)
79
- rubocop-factory_bot (2.25.1)
80
- rubocop (~> 1.41)
81
- rubocop-packaging (0.5.2)
82
- rubocop (>= 1.33, < 2.0)
83
- rubocop-performance (1.23.0)
84
- rubocop (>= 1.48.1, < 2.0)
85
- rubocop-ast (>= 1.31.1, < 2.0)
86
- rubocop-rake (0.6.0)
87
- rubocop (~> 1.0)
88
- rubocop-rspec (2.29.1)
89
- rubocop (~> 1.40)
90
- rubocop-capybara (~> 2.17)
91
- rubocop-factory_bot (~> 2.22)
92
- rubocop-rspec_rails (~> 2.28)
93
- rubocop-rspec_rails (2.28.2)
94
- rubocop (~> 1.40)
81
+ unicode-display_width (>= 2.4.0, < 4.0)
82
+ rubocop-ast (1.47.1)
83
+ parser (>= 3.3.7.2)
84
+ prism (~> 1.4)
85
+ rubocop-packaging (0.6.0)
86
+ lint_roller (~> 1.1.0)
87
+ rubocop (>= 1.72.1, < 2.0)
88
+ rubocop-performance (1.26.1)
89
+ lint_roller (~> 1.1)
90
+ rubocop (>= 1.75.0, < 2.0)
91
+ rubocop-ast (>= 1.47.1, < 2.0)
92
+ rubocop-rake (0.7.1)
93
+ lint_roller (~> 1.1)
94
+ rubocop (>= 1.72.1)
95
+ rubocop-rspec (3.7.0)
96
+ lint_roller (~> 1.1)
97
+ rubocop (~> 1.72, >= 1.72.1)
95
98
  ruby-progressbar (1.13.0)
96
- sequel (5.86.0)
99
+ sequel (5.97.0)
97
100
  bigdecimal
98
- syntax_tree (6.2.0)
101
+ syntax_tree (6.3.0)
99
102
  prettier_print (>= 1.2.0)
100
103
  syntax_tree-haml (4.0.3)
101
104
  haml (>= 5.2)
@@ -105,14 +108,18 @@ GEM
105
108
  prettier_print
106
109
  rbs
107
110
  syntax_tree (>= 2.0.1)
108
- temple (0.10.1)
109
- thor (1.3.2)
110
- tilt (2.1.0)
111
- unicode-display_width (2.6.0)
111
+ temple (0.10.4)
112
+ thor (1.4.0)
113
+ tilt (2.6.1)
114
+ unicode-display_width (3.2.0)
115
+ unicode-emoji (~> 4.1)
116
+ unicode-emoji (4.1.0)
112
117
 
113
118
  PLATFORMS
114
119
  arm64-darwin-22
120
+ arm64-darwin-24
115
121
  x86_64-linux
122
+ x86_64-linux-gnu
116
123
 
117
124
  DEPENDENCIES
118
125
  pg_easy_replicate!
data/README.md CHANGED
@@ -159,6 +159,11 @@ For AWS the special user role is `rds_superuser`, and for GCP it is `cloudsqlsup
159
159
 
160
160
  **Note**: The user in the connection url must be part of the special user role being supplied.
161
161
 
162
+ **PostgreSQL 16+ Requirement**: Starting with PostgreSQL 16, if you're using `--special-user-role`, the user in your connection URL must have the `ADMIN OPTION` on that role. You can grant this with:
163
+ ```sql
164
+ GRANT rds_superuser TO your_user WITH ADMIN OPTION;
165
+ ```
166
+
162
167
  #### Config Check
163
168
 
164
169
  ```bash
@@ -374,5 +379,5 @@ Next, you can set up a program that watches the `stats` and waits until `switcho
374
379
  PRs most welcome. You can get started locally by
375
380
 
376
381
  - `docker compose down -v && docker compose up --remove-orphans --build`
377
- - Install ruby `3.3.6` using RVM ([instruction](https://rvm.io/rvm/install#any-other-system))
382
+ - Install ruby `3.4.4` using RVM ([instruction](https://rvm.io/rvm/install#any-other-system))
378
383
  - `bundle exec rspec` for specs
@@ -171,18 +171,22 @@ module PgEasyReplicate
171
171
 
172
172
  def create_event_triggers(conn, group_name)
173
173
  sanitized_group_name = sanitize_identifier(group_name)
174
+
175
+ pg_version = conn.fetch("SHOW server_version_num").first[:server_version_num].to_i
176
+ execute_keyword = pg_version >= 110000 ? "FUNCTION" : "PROCEDURE"
177
+
174
178
  conn.run(<<~SQL)
175
179
  DROP EVENT TRIGGER IF EXISTS pger_ddl_trigger_#{sanitized_group_name};
176
180
  CREATE EVENT TRIGGER pger_ddl_trigger_#{sanitized_group_name} ON ddl_command_end
177
- EXECUTE FUNCTION #{internal_schema_name}.pger_ddl_trigger_#{sanitized_group_name}();
181
+ EXECUTE #{execute_keyword} #{internal_schema_name}.pger_ddl_trigger_#{sanitized_group_name}();
178
182
 
179
183
  DROP EVENT TRIGGER IF EXISTS pger_drop_trigger_#{sanitized_group_name};
180
184
  CREATE EVENT TRIGGER pger_drop_trigger_#{sanitized_group_name} ON sql_drop
181
- EXECUTE FUNCTION #{internal_schema_name}.pger_ddl_trigger_#{sanitized_group_name}();
185
+ EXECUTE #{execute_keyword} #{internal_schema_name}.pger_ddl_trigger_#{sanitized_group_name}();
182
186
 
183
187
  DROP EVENT TRIGGER IF EXISTS pger_table_rewrite_trigger_#{sanitized_group_name};
184
188
  CREATE EVENT TRIGGER pger_table_rewrite_trigger_#{sanitized_group_name} ON table_rewrite
185
- EXECUTE FUNCTION #{internal_schema_name}.pger_ddl_trigger_#{sanitized_group_name}();
189
+ EXECUTE #{execute_keyword} #{internal_schema_name}.pger_ddl_trigger_#{sanitized_group_name}();
186
190
  SQL
187
191
  rescue => e
188
192
  abort_with("Creating event triggers failed: #{e.message}")
@@ -122,7 +122,7 @@ module PgEasyReplicate
122
122
  .with_object(Hash.new(0)) do |state, counts|
123
123
  counts[state[:replication_state]] += 1
124
124
  end
125
- result.keys.uniq.count == 1 &&
125
+ result.keys.uniq.one? &&
126
126
  result.keys.first == REPLICATION_STATE_MAP["r"]
127
127
  end
128
128
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PgEasyReplicate
4
- VERSION = "0.3.8"
4
+ VERSION = "0.4.0"
5
5
  end
@@ -166,8 +166,13 @@ module PgEasyReplicate
166
166
  def cleanup(options)
167
167
  cleanup_steps = [
168
168
  -> do
169
- logger.info("Dropping groups table")
170
- Group.drop
169
+ if options[:everything]
170
+ logger.info("Dropping groups table")
171
+ Group.drop
172
+ else
173
+ logger.info("Deleting group entry for #{options[:group_name]}")
174
+ Group.delete(options[:group_name])
175
+ end
171
176
  end,
172
177
  -> do
173
178
  if options[:restore_connection_on_source_db]
@@ -343,6 +348,18 @@ module PgEasyReplicate
343
348
  raise "Unable to check superuser conditions: #{e.message}"
344
349
  end
345
350
 
351
+ def get_pg_version(conn_string:)
352
+ sql = "SELECT version()"
353
+ result = Query.run(query: sql, connection_url: conn_string, user: db_user(conn_string))
354
+ version_string = result.first[:version]
355
+ # Extract major version number (e.g., "PostgreSQL 16.1..." -> 16)
356
+ version_match = version_string.match(/PostgreSQL (\d+)\./)
357
+ version_match ? version_match[1].to_i : nil
358
+ rescue => e
359
+ logger.warn("Unable to determine PostgreSQL version: #{e.message}")
360
+ nil
361
+ end
362
+
346
363
  def create_user(
347
364
  conn_string:,
348
365
  special_user_role: nil,
@@ -364,12 +381,11 @@ module PgEasyReplicate
364
381
  transaction: false,
365
382
  )
366
383
 
367
- sql =
368
- if special_user_role
369
- "grant #{quote_ident(special_user_role)} to #{quote_ident(internal_user_name)};"
370
- else
371
- "alter user #{quote_ident(internal_user_name)} with superuser;"
372
- end
384
+ sql = if special_user_role
385
+ "grant #{quote_ident(special_user_role)} to #{quote_ident(internal_user_name)};"
386
+ else
387
+ "alter user #{quote_ident(internal_user_name)} with superuser;"
388
+ end
373
389
 
374
390
  Query.run(
375
391
  query: sql,
@@ -378,16 +394,29 @@ module PgEasyReplicate
378
394
  transaction: false,
379
395
  )
380
396
 
381
- return unless grant_permissions_on_schema
382
- Query.run(
383
- query:
384
- "grant all on schema #{quote_ident(internal_schema_name)} to #{quote_ident(internal_user_name)}",
385
- connection_url: conn_string,
386
- user: db_user(conn_string),
387
- transaction: false,
388
- )
397
+ if grant_permissions_on_schema
398
+ Query.run(
399
+ query:
400
+ "grant all on schema #{quote_ident(internal_schema_name)} to #{quote_ident(internal_user_name)}",
401
+ connection_url: conn_string,
402
+ user: db_user(conn_string),
403
+ transaction: false,
404
+ )
405
+ end
389
406
  rescue => e
390
- raise "Unable to create user: #{e.message}"
407
+ raise "Unable to create user: #{e.message}" unless special_user_role && e.message.include?("permission denied to grant role")
408
+
409
+ pg_version = get_pg_version(conn_string: conn_string)
410
+ version_info = if pg_version && pg_version >= 16
411
+ "PostgreSQL #{pg_version} requires"
412
+ else
413
+ "PostgreSQL 16+ requires"
414
+ end
415
+
416
+ raise "Unable to create user: #{e.message}. " \
417
+ "#{version_info} the user '#{db_user(conn_string)}' to have the ADMIN option on role '#{special_user_role}' to grant it to other users. " \
418
+ "Please ensure the user has been granted the role with ADMIN option: " \
419
+ "GRANT #{special_user_role} TO #{db_user(conn_string)} WITH ADMIN OPTION;"
391
420
  end
392
421
 
393
422
  def drop_user(conn_string:, user: internal_user_name)
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg_easy_replicate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.8
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shayon Mukherjee
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-12-08 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: ougai
@@ -28,30 +27,42 @@ dependencies:
28
27
  name: pg
29
28
  requirement: !ruby/object:Gem::Requirement
30
29
  requirements:
31
- - - "~>"
30
+ - - ">="
32
31
  - !ruby/object:Gem::Version
33
32
  version: 1.5.3
33
+ - - "<"
34
+ - !ruby/object:Gem::Version
35
+ version: 1.7.0
34
36
  type: :runtime
35
37
  prerelease: false
36
38
  version_requirements: !ruby/object:Gem::Requirement
37
39
  requirements:
38
- - - "~>"
40
+ - - ">="
39
41
  - !ruby/object:Gem::Version
40
42
  version: 1.5.3
43
+ - - "<"
44
+ - !ruby/object:Gem::Version
45
+ version: 1.7.0
41
46
  - !ruby/object:Gem::Dependency
42
47
  name: pg_query
43
48
  requirement: !ruby/object:Gem::Requirement
44
49
  requirements:
45
- - - "~>"
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '5.1'
53
+ - - "<"
46
54
  - !ruby/object:Gem::Version
47
- version: 5.1.0
55
+ version: '6.2'
48
56
  type: :runtime
49
57
  prerelease: false
50
58
  version_requirements: !ruby/object:Gem::Requirement
51
59
  requirements:
52
- - - "~>"
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '5.1'
63
+ - - "<"
53
64
  - !ruby/object:Gem::Version
54
- version: 5.1.0
65
+ version: '6.2'
55
66
  - !ruby/object:Gem::Dependency
56
67
  name: sequel
57
68
  requirement: !ruby/object:Gem::Requirement
@@ -61,7 +72,7 @@ dependencies:
61
72
  version: '5.69'
62
73
  - - "<"
63
74
  - !ruby/object:Gem::Version
64
- version: '5.87'
75
+ version: '5.98'
65
76
  type: :runtime
66
77
  prerelease: false
67
78
  version_requirements: !ruby/object:Gem::Requirement
@@ -71,7 +82,7 @@ dependencies:
71
82
  version: '5.69'
72
83
  - - "<"
73
84
  - !ruby/object:Gem::Version
74
- version: '5.87'
85
+ version: '5.98'
75
86
  - !ruby/object:Gem::Dependency
76
87
  name: thor
77
88
  requirement: !ruby/object:Gem::Requirement
@@ -81,7 +92,7 @@ dependencies:
81
92
  version: 1.2.2
82
93
  - - "<"
83
94
  - !ruby/object:Gem::Version
84
- version: 1.4.0
95
+ version: 1.5.0
85
96
  type: :runtime
86
97
  prerelease: false
87
98
  version_requirements: !ruby/object:Gem::Requirement
@@ -91,7 +102,7 @@ dependencies:
91
102
  version: 1.2.2
92
103
  - - "<"
93
104
  - !ruby/object:Gem::Version
94
- version: 1.4.0
105
+ version: 1.5.0
95
106
  - !ruby/object:Gem::Dependency
96
107
  name: prettier_print
97
108
  requirement: !ruby/object:Gem::Requirement
@@ -305,8 +316,10 @@ homepage: https://github.com/shayonj/pg_easy_replicate
305
316
  licenses:
306
317
  - MIT
307
318
  metadata:
319
+ homepage_uri: https://github.com/shayonj/pg_easy_replicate
320
+ source_code_uri: https://github.com/shayonj/pg_easy_replicate
321
+ changelog_uri: https://github.com/shayonj/pg_easy_replicate/blob/main/CODE_OF_CONDUCT.md
308
322
  rubygems_mfa_required: 'true'
309
- post_install_message:
310
323
  rdoc_options: []
311
324
  require_paths:
312
325
  - lib
@@ -321,8 +334,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
321
334
  - !ruby/object:Gem::Version
322
335
  version: '0'
323
336
  requirements: []
324
- rubygems_version: 3.5.22
325
- signing_key:
337
+ rubygems_version: 3.6.7
326
338
  specification_version: 4
327
339
  summary: Easily setup logical replication and switchover to new database with minimal
328
340
  downtime