pg_ha_migrations 1.3.0 → 1.6.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: 642f8332dd491d8b489d7044c8989bcfc8a7e34c63ad380c42ed3e78f45c5c8f
4
- data.tar.gz: 0bec0715601bce8ddd77d83c03c399b2dff21039301a72b67899d5041012c231
3
+ metadata.gz: 6962b68069791fd1de1da3dd1f8a3bb34438faf217023fdcc05d792eb345a666
4
+ data.tar.gz: 450e7710be2bac4d25e77dabfb7eff9048f9321ebcf90a428a6125edab848f69
5
5
  SHA512:
6
- metadata.gz: b40ccad5d82c2f2167515eca20da2152482c7e9cd11b84dbf721b0840c7c40462a8818d4bb8dbea840196171185dd3ddc1fb28e205f96a7845cc9c3df69d1a67
7
- data.tar.gz: 2a07b86116d69ee0a51ec8b9d7fb4bcc6ba876e363df79902e7bed3f7ab123bd9f4c4c21b15ba538c0d8850b6f82c353f0c02fa7ad2ac11f8f84ee1f29787bd6
6
+ metadata.gz: 7467eee266a3c9f49faa84cc771ae35d601b25cc38cfe097b5d696e01dfd317ed1a6b5e67e8a053f80cc262bedf2f3b204dbb4b98732d09688485371613025f1
7
+ data.tar.gz: 63d05306b246ff151d0849967bd71a5457f94d1ac968cc155fcd58e5b359c868a4888167e43fd5775e7bec3bfc8dab9eb71341b80971b4ffd03ed0a189f8ff83
@@ -9,12 +9,64 @@ jobs:
9
9
  - 10
10
10
  - 11
11
11
  - 12
12
+ ruby:
13
+ - 2.7
12
14
  gemfile:
13
15
  - rails_5.0
14
16
  - rails_5.1
15
17
  - rails_5.2
16
18
  - rails_6.0
17
19
  - rails_6.1
20
+ - rails_7.0
21
+ include:
22
+ - gemfile: rails_6.1
23
+ ruby: 3.0
24
+ pg: 9.6
25
+ - gemfile: rails_6.1
26
+ ruby: 3.0
27
+ pg: 10
28
+ - gemfile: rails_6.1
29
+ ruby: 3.0
30
+ pg: 11
31
+ - gemfile: rails_6.1
32
+ ruby: 3.0
33
+ pg: 12
34
+ - gemfile: rails_6.1
35
+ ruby: 3.1
36
+ pg: 9.6
37
+ - gemfile: rails_6.1
38
+ ruby: 3.1
39
+ pg: 10
40
+ - gemfile: rails_6.1
41
+ ruby: 3.1
42
+ pg: 11
43
+ - gemfile: rails_6.1
44
+ ruby: 3.1
45
+ pg: 12
46
+ - gemfile: rails_7.0
47
+ ruby: 3.0
48
+ pg: 9.6
49
+ - gemfile: rails_7.0
50
+ ruby: 3.0
51
+ pg: 10
52
+ - gemfile: rails_7.0
53
+ ruby: 3.0
54
+ pg: 11
55
+ - gemfile: rails_7.0
56
+ ruby: 3.0
57
+ pg: 12
58
+ - gemfile: rails_7.0
59
+ ruby: 3.1
60
+ pg: 9.6
61
+ - gemfile: rails_7.0
62
+ ruby: 3.1
63
+ pg: 10
64
+ - gemfile: rails_7.0
65
+ ruby: 3.1
66
+ pg: 11
67
+ - gemfile: rails_7.0
68
+ ruby: 3.1
69
+ pg: 12
18
70
  name: PostgreSQL ${{ matrix.pg }}
19
71
  runs-on: ubuntu-latest
20
72
  env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
@@ -38,5 +90,6 @@ jobs:
38
90
  - name: Setup Ruby using .ruby-version file
39
91
  uses: ruby/setup-ruby@v1
40
92
  with:
93
+ ruby-version: ${{ matrix.ruby }}
41
94
  bundler-cache: true # runs 'bundle install' and caches installed gems automatically
42
95
  - run: bundle exec rake spec
data/Appraisals CHANGED
@@ -18,3 +18,6 @@ appraise "rails-6.1" do
18
18
  gem "rails", "6.1.0"
19
19
  end
20
20
 
21
+ appraise "rails-7.0" do
22
+ gem "rails", "7.0.0"
23
+ end
data/README.md CHANGED
@@ -148,6 +148,8 @@ safe_change_column_default :table, :column, -> { "NOW()" }
148
148
  safe_change_column_default :table, :column, -> { "'NOW()'" }
149
149
  ```
150
150
 
151
+ Note: On Postgres 11+ adding a column with a constant default value does not rewrite or scan the table (under a lock or otherwise). In that case a migration adding a column with a default should do so in a single operation rather than the two-step `safe_add_column` followed by `safe_change_column_default`. We enforce this best practice with the error `PgHaMigrations::BestPracticeError`, but if your prefer otherwise (or are running in a mixed Postgres version environment), you may opt out by setting `config.prefer_single_step_column_addition_with_default = true` [in your configuration initializer](#configuration).
152
+
151
153
  #### safe\_make\_column\_nullable
152
154
 
153
155
  Safely make the column nullable.
@@ -272,6 +274,8 @@ end
272
274
 
273
275
  - `disable_default_migration_methods`: If true, the default implementations of DDL changes in `ActiveRecord::Migration` and the PostgreSQL adapter will be overridden by implementations that raise a `PgHaMigrations::UnsafeMigrationError`. Default: `true`
274
276
  - `check_for_dependent_objects`: If true, some `unsafe_*` migration methods will raise a `PgHaMigrations::UnsafeMigrationError` if any dependent objects exist. Default: `false`
277
+ - `prefer_single_step_column_addition_with_default`: If `true`, raise an error when adding a column and separately setting a constant default value for that column in the same migration. Default: `false`
278
+ - 'allow_force_create_table`: If false, the `force: true` option to ActiveRecord's `create_table` method is disallowed. Default: `true`
275
279
 
276
280
  ### Rake Tasks
277
281
 
@@ -299,7 +303,11 @@ After checking out the repo, run `bin/setup` to install dependencies and start a
299
303
 
300
304
  Running tests will automatically create a test database in the locally running Postgres server. You can find the connection parameters in `spec/spec_helper.rb`, but setting the environment variables `PGHOST`, `PGPORT`, `PGUSER`, and `PGPASSWORD` will override the defaults.
301
305
 
302
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
306
+ To install this gem onto your local machine, run `bundle exec rake install`.
307
+
308
+ To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
309
+
310
+ Note: if while releasing the gem you get the error ``Your rubygems.org credentials aren't set. Run `gem push` to set them.`` you can more simply run `gem signin`.
303
311
 
304
312
  ## Contributing
305
313
 
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "7.0.1"
6
+
7
+ gemspec path: "../"
@@ -1,7 +1,7 @@
1
1
  require "active_record/migration/compatibility"
2
2
 
3
3
  module PgHaMigrations::AllowedVersions
4
- ALLOWED_VERSIONS = [4.2, 5.0, 5.1, 5.2, 6.0, 6.1].map do |v|
4
+ ALLOWED_VERSIONS = [4.2, 5.0, 5.1, 5.2, 6.0, 6.1, 7.0].map do |v|
5
5
  begin
6
6
  ActiveRecord::Migration[v]
7
7
  rescue ArgumentError
@@ -49,10 +49,6 @@ module PgHaMigrations
49
49
  LEFT JOIN pg_class c ON ( -- Database wide
50
50
  l.locktype = 'relation'
51
51
  AND l.relation = c.oid
52
- -- Be explicit about this being for a single database -- it's already implicit in
53
- -- the relations used, and if we don't restrict this we could get incorrect results
54
- -- with oid collisions from pg_namespace and pg_class.
55
- AND l.database = (SELECT d.oid FROM pg_database d WHERE d.datname = current_database())
56
52
  )
57
53
  LEFT JOIN pg_namespace ns ON (c.relnamespace = ns.oid) -- Database wide
58
54
  WHERE psa.#{pid_column} != pg_backend_pid()
@@ -65,6 +61,10 @@ module PgHaMigrations
65
61
  )
66
62
  AND psa.xact_start < clock_timestamp() - ?::interval
67
63
  AND psa.#{query_column} !~ ?
64
+ -- Be explicit about this being for a single database -- it's already implicit in
65
+ -- the relations used, and if we don't restrict this we could get incorrect results
66
+ -- with oid collisions from pg_namespace and pg_class.
67
+ AND psa.datname = current_database()
68
68
  #{postgres_version >= 10_00_00 ? "AND #{ignore_sqlsender_sql}" : ""}
69
69
  GROUP BY psa.datname, psa.#{query_column}, psa.state, psa.xact_start
70
70
  SQL
@@ -1,4 +1,8 @@
1
1
  module PgHaMigrations::SafeStatements
2
+ def safe_added_columns_without_default_value
3
+ @safe_added_columns_without_default_value ||= []
4
+ end
5
+
2
6
  def safe_create_table(table, options={}, &block)
3
7
  if options[:force]
4
8
  raise PgHaMigrations::UnsafeMigrationError.new(":force is NOT SAFE! Explicitly call unsafe_drop_table first if you want to recreate an existing table")
@@ -52,16 +56,26 @@ module PgHaMigrations::SafeStatements
52
56
  raise PgHaMigrations::UnsafeMigrationError.new(":null => false is NOT SAFE if the table has data! If you _really_ want to do this, use unsafe_make_column_not_nullable")
53
57
  end
54
58
 
59
+ unless options.has_key?(:default)
60
+ self.safe_added_columns_without_default_value << [table.to_s, column.to_s]
61
+ end
62
+
55
63
  unsafe_add_column(table, column, type, options)
56
64
  end
57
65
 
58
66
  def unsafe_add_column(table, column, type, options = {})
59
67
  safely_acquire_lock_for_table(table) do
60
- super(table, column, type, options)
68
+ super(table, column, type, **options)
61
69
  end
62
70
  end
63
71
 
64
72
  def safe_change_column_default(table_name, column_name, default_value)
73
+ if PgHaMigrations.config.prefer_single_step_column_addition_with_default &&
74
+ ActiveRecord::Base.connection.postgresql_version >= 11_00_00 &&
75
+ self.safe_added_columns_without_default_value.include?([table_name.to_s, column_name.to_s])
76
+ raise PgHaMigrations::BestPracticeError, "On Postgres 11+ it's safe to set a constant default value when adding a new column; please set the default value as part of the column addition"
77
+ end
78
+
65
79
  column = connection.send(:column_for, table_name, column_name)
66
80
 
67
81
  # In 5.2 we have an edge whereby passing in a string literal with an expression
@@ -131,7 +145,7 @@ module PgHaMigrations::SafeStatements
131
145
  end
132
146
 
133
147
  def safe_add_concurrent_index(table, columns, options={})
134
- unsafe_add_index(table, columns, options.merge(:algorithm => :concurrently))
148
+ unsafe_add_index(table, columns, **options.merge(:algorithm => :concurrently))
135
149
  end
136
150
 
137
151
  def safe_remove_concurrent_index(table, options={})
@@ -143,7 +157,7 @@ module PgHaMigrations::SafeStatements
143
157
  end
144
158
  index_size = select_value("SELECT pg_size_pretty(pg_relation_size('#{options[:name]}'))")
145
159
  say "Preparing to drop index #{options[:name]} which is #{index_size} on disk..."
146
- unsafe_remove_index(table, options.merge(:algorithm => :concurrently))
160
+ unsafe_remove_index(table, **options.merge(:algorithm => :concurrently))
147
161
  end
148
162
 
149
163
  def safe_set_maintenance_work_mem_gb(gigabytes)
@@ -19,6 +19,7 @@ module PgHaMigrations::UnsafeStatements
19
19
 
20
20
  execute_ancestor_statement(method_name, *args, &block)
21
21
  end
22
+ ruby2_keywords method_name
22
23
  end
23
24
 
24
25
  def self.delegate_unsafe_method_to_migration_base_class(method_name)
@@ -29,6 +30,7 @@ module PgHaMigrations::UnsafeStatements
29
30
 
30
31
  execute_ancestor_statement(method_name, *args, &block)
31
32
  end
33
+ ruby2_keywords "unsafe_#{method_name}"
32
34
  end
33
35
 
34
36
  delegate_unsafe_method_to_migration_base_class :add_column
@@ -65,7 +67,7 @@ module PgHaMigrations::UnsafeStatements
65
67
  raise PgHaMigrations::UnsafeMigrationError.new(":force is NOT SAFE! Explicitly call unsafe_drop_table first if you want to recreate an existing table")
66
68
  end
67
69
 
68
- execute_ancestor_statement(:create_table, table, options, &block)
70
+ execute_ancestor_statement(:create_table, table, **options, &block)
69
71
  end
70
72
 
71
73
  def unsafe_add_index(table, column_names, options = {})
@@ -74,11 +76,10 @@ module PgHaMigrations::UnsafeStatements
74
76
  raise PgHaMigrations::InvalidMigrationError, "ActiveRecord drops the :opclass option when supplying a string containing an expression or list of columns; instead either supply an array of columns or include the opclass in the string for each column"
75
77
  end
76
78
 
77
- execute_ancestor_statement(:add_index, table, column_names, options)
79
+ execute_ancestor_statement(:add_index, table, column_names, **options)
78
80
  end
79
81
 
80
-
81
- def execute_ancestor_statement(method_name, *args, &block)
82
+ ruby2_keywords def execute_ancestor_statement(method_name, *args, &block)
82
83
  # Dispatching here is a bit complicated: we need to execute the method
83
84
  # belonging to the first member of the inheritance chain (besides
84
85
  # UnsafeStatements). If don't find the method in the inheritance chain,
@@ -1,3 +1,3 @@
1
1
  module PgHaMigrations
2
- VERSION = "1.3.0"
2
+ VERSION = "1.6.0"
3
3
  end
@@ -3,19 +3,22 @@ require "rails"
3
3
  require "active_record"
4
4
  require "active_record/migration"
5
5
  require "relation_to_struct"
6
+ require "ruby2_keywords"
6
7
 
7
8
  module PgHaMigrations
8
9
  Config = Struct.new(
9
10
  :disable_default_migration_methods,
10
11
  :check_for_dependent_objects,
11
- :allow_force_create_table
12
+ :allow_force_create_table,
13
+ :prefer_single_step_column_addition_with_default
12
14
  )
13
15
 
14
16
  def self.config
15
17
  @config ||= Config.new(
16
18
  true,
17
19
  false,
18
- true
20
+ true,
21
+ false
19
22
  )
20
23
  end
21
24
 
@@ -29,18 +32,24 @@ module PgHaMigrations
29
32
  # Safe versus unsafe in this context specifically means the following:
30
33
  # - Safe operations will not block for long periods of time.
31
34
  # - Unsafe operations _may_ block for long periods of time.
32
- UnsafeMigrationError = Class.new(Exception)
35
+ UnsafeMigrationError = Class.new(StandardError)
33
36
 
34
37
  # Invalid migrations are operations which we expect to not function
35
38
  # as expected or get the schema into an inconsistent state
36
- InvalidMigrationError = Class.new(Exception)
39
+ InvalidMigrationError = Class.new(StandardError)
40
+
41
+ # Operations violating a best practice, but not actually unsafe will
42
+ # raise this error. For example, adding a column without a default and
43
+ # then setting its default in a second action in a single migration
44
+ # isn't our documented best practice and will raise this error.
45
+ BestPracticeError = Class.new(Exception)
37
46
 
38
47
  # Unsupported migrations use ActiveRecord::Migration features that
39
48
  # we don't support, and therefore will likely have unexpected behavior.
40
- UnsupportedMigrationError = Class.new(Exception)
49
+ UnsupportedMigrationError = Class.new(StandardError)
41
50
 
42
51
  # This gem only supports the PostgreSQL adapter at this time.
43
- UnsupportedAdapter = Class.new(Exception)
52
+ UnsupportedAdapter = Class.new(StandardError)
44
53
  end
45
54
 
46
55
  require "pg_ha_migrations/blocking_database_transactions"
@@ -29,14 +29,15 @@ Gem::Specification.new do |spec|
29
29
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
30
30
  spec.require_paths = ["lib"]
31
31
 
32
- spec.add_development_dependency "rake", "~> 10.0"
32
+ spec.add_development_dependency "rake", ">= 12.3.3"
33
33
  spec.add_development_dependency "rspec", "~> 3.0"
34
34
  spec.add_development_dependency "pg"
35
- spec.add_development_dependency "db-query-matchers", "~> 0.10.0"
35
+ spec.add_development_dependency "db-query-matchers", "~> 0.11.0"
36
36
  spec.add_development_dependency "pry"
37
37
  spec.add_development_dependency "pry-byebug"
38
38
  spec.add_development_dependency "appraisal", "~> 2.2.0"
39
39
 
40
- spec.add_dependency "rails", ">= 5.0", "< 6.2"
40
+ spec.add_dependency "rails", ">= 5.0", "< 7.1"
41
41
  spec.add_dependency "relation_to_struct", ">= 1.5.1"
42
+ spec.add_dependency "ruby2_keywords"
42
43
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg_ha_migrations
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - celeen
@@ -14,22 +14,22 @@ authors:
14
14
  autorequire:
15
15
  bindir: exe
16
16
  cert_chain: []
17
- date: 2021-09-14 00:00:00.000000000 Z
17
+ date: 2022-07-15 00:00:00.000000000 Z
18
18
  dependencies:
19
19
  - !ruby/object:Gem::Dependency
20
20
  name: rake
21
21
  requirement: !ruby/object:Gem::Requirement
22
22
  requirements:
23
- - - "~>"
23
+ - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: '10.0'
25
+ version: 12.3.3
26
26
  type: :development
27
27
  prerelease: false
28
28
  version_requirements: !ruby/object:Gem::Requirement
29
29
  requirements:
30
- - - "~>"
30
+ - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: '10.0'
32
+ version: 12.3.3
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: rspec
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -64,14 +64,14 @@ dependencies:
64
64
  requirements:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: 0.10.0
67
+ version: 0.11.0
68
68
  type: :development
69
69
  prerelease: false
70
70
  version_requirements: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - "~>"
73
73
  - !ruby/object:Gem::Version
74
- version: 0.10.0
74
+ version: 0.11.0
75
75
  - !ruby/object:Gem::Dependency
76
76
  name: pry
77
77
  requirement: !ruby/object:Gem::Requirement
@@ -123,7 +123,7 @@ dependencies:
123
123
  version: '5.0'
124
124
  - - "<"
125
125
  - !ruby/object:Gem::Version
126
- version: '6.2'
126
+ version: '7.1'
127
127
  type: :runtime
128
128
  prerelease: false
129
129
  version_requirements: !ruby/object:Gem::Requirement
@@ -133,7 +133,7 @@ dependencies:
133
133
  version: '5.0'
134
134
  - - "<"
135
135
  - !ruby/object:Gem::Version
136
- version: '6.2'
136
+ version: '7.1'
137
137
  - !ruby/object:Gem::Dependency
138
138
  name: relation_to_struct
139
139
  requirement: !ruby/object:Gem::Requirement
@@ -148,6 +148,20 @@ dependencies:
148
148
  - - ">="
149
149
  - !ruby/object:Gem::Version
150
150
  version: 1.5.1
151
+ - !ruby/object:Gem::Dependency
152
+ name: ruby2_keywords
153
+ requirement: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ type: :runtime
159
+ prerelease: false
160
+ version_requirements: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
151
165
  description: Enforces DDL/migration safety in Ruby on Rails project with an emphasis
152
166
  on explicitly choosing trade-offs and avoiding unnecessary magic.
153
167
  email:
@@ -161,7 +175,6 @@ files:
161
175
  - ".pryrc"
162
176
  - ".rspec"
163
177
  - ".ruby-version"
164
- - ".travis.yml"
165
178
  - Appraisals
166
179
  - CODE_OF_CONDUCT.md
167
180
  - Gemfile
@@ -176,6 +189,7 @@ files:
176
189
  - gemfiles/rails_5.2.gemfile
177
190
  - gemfiles/rails_6.0.gemfile
178
191
  - gemfiles/rails_6.1.gemfile
192
+ - gemfiles/rails_7.0.gemfile
179
193
  - lib/pg_ha_migrations.rb
180
194
  - lib/pg_ha_migrations/allowed_versions.rb
181
195
  - lib/pg_ha_migrations/blocking_database_transactions.rb
data/.travis.yml DELETED
@@ -1,27 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- rvm:
4
- - 2.5
5
- env:
6
- jobs:
7
- - PGVERSION: "9.6"
8
- - PGVERSION: "10"
9
- - PGVERSION: "11"
10
- - PGVERSION: "12"
11
- services:
12
- - postgresql
13
- before_install:
14
- - "for CLUSTER_VERSION in $(pg_lsclusters -h | cut -d' ' -f1); do sudo pg_dropcluster $CLUSTER_VERSION main --stop || true; done"
15
- - sudo apt-get update
16
- - sudo apt-get -y install postgresql-$PGVERSION postgresql-client-$PGVERSION postgresql-server-dev-$PGVERSION postgresql-client-common postgresql-common
17
- - sudo pg_dropcluster $PGVERSION main --stop || true
18
- - sudo pg_createcluster $PGVERSION main -D /var/ramfs/postgresql/11/main -- --auth=trust
19
- - sudo pg_ctlcluster start $PGVERSION main
20
- - gem uninstall -v '>= 2' -i $(rvm gemdir)@global -ax bundler || true
21
- - gem install bundler -v 1.15.4
22
- gemfile:
23
- - gemfiles/rails_5.0.gemfile
24
- - gemfiles/rails_5.1.gemfile
25
- - gemfiles/rails_5.2.gemfile
26
- - gemfiles/rails_6.0.gemfile
27
- script: "bundle exec rake spec"