pg_ha_migrations 1.3.0 → 1.6.0

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