pg_ha_migrations 1.2.5 → 1.5.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: aa56359f3f0d8d56cc060ffbda09e40a234bbd80581de50d637caf8fc5ffb94b
4
- data.tar.gz: e53c77e2a38349e16e8dd977747b0bb1691ed7a4643a7ae5dda26d05ef3cf71e
3
+ metadata.gz: a5eb93231a1de0f12819682bfe42606bf0cc49813a50b91670107f01eafe8f18
4
+ data.tar.gz: adcea5f591af66bb790118c562a0cab80dca009421529437d2586e6c0b957f01
5
5
  SHA512:
6
- metadata.gz: ffca13e9164a6d1a82b03fcb521707aef486d78385db1e48f5c43d7702399316d1616a166e89e917f28c18f7aba06b3eec89316c34218ecab851599132b688c6
7
- data.tar.gz: 657e725471a0caad6e100e0cb009f242b2af4d2eaf4385d65c02623e0366d57ff4d24a6dcc71ba1457e34f80f972d9c0a0704bf238f0442fa902df119440146c
6
+ metadata.gz: c7c827c5385b13a438a4a2cf1eaa1b7535e54e6d3293f67616849647706ca869a1362db4e102e66802f0930d450a09e3943d365a6b9d7481871c286c3fba1a36
7
+ data.tar.gz: 334f6c562b0b1288690ff6ec1033beb6676446f4c3a5d708d65de93884e85a65a863d985a37bc02a0c30046d34fbd289f9ac5cca50ada30a9e6d4fcdc0d977ac
@@ -14,6 +14,8 @@ jobs:
14
14
  - rails_5.1
15
15
  - rails_5.2
16
16
  - rails_6.0
17
+ - rails_6.1
18
+ - rails_7.0
17
19
  name: PostgreSQL ${{ matrix.pg }}
18
20
  runs-on: ubuntu-latest
19
21
  env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
data/Appraisals CHANGED
@@ -14,3 +14,10 @@ appraise "rails-6.0" do
14
14
  gem "rails", "6.0.0"
15
15
  end
16
16
 
17
+ appraise "rails-6.1" do
18
+ gem "rails", "6.1.0"
19
+ end
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
 
data/bin/setup CHANGED
@@ -2,6 +2,9 @@
2
2
  set -euo pipefail
3
3
  IFS=$'\n\t'
4
4
  set -vx
5
+ export PGPASSWORD="${PGPASSWORD:-postgres}"
6
+ PGVERSION="${PGVERSION:-13}"
7
+
5
8
 
6
9
  bundle install
7
10
  bundle exec appraisal install
@@ -9,4 +12,4 @@ bundle exec appraisal install
9
12
  # Do any other automated setup that you need to do here
10
13
 
11
14
  # Launch a blank postgres image for testing
12
- docker run -d -p 127.0.0.1:5432:5432 postgres:10
15
+ docker run -d -p 127.0.0.1:5432:5432 -e POSTGRES_PASSWORD="${PGPASSWORD}" postgres:${PGVERSION}
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "6.1.0"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "7.0.0"
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].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")
@@ -34,17 +38,28 @@ module PgHaMigrations::SafeStatements
34
38
  end
35
39
 
36
40
  def safe_add_column(table, column, type, options = {})
41
+ # Note: we don't believe we need to consider the odd case where
42
+ # `:default => nil` or `:default => -> { null }` (or similar) is
43
+ # passed because:
44
+ # - It's OK to exclude that case with an "unnecessary" `raise`
45
+ # below as it doesn't make semantic sense anyway.
46
+ # - If `:null => false` is also passed we are assuming Postgres's
47
+ # seq scan of the table (to verify the NOT NULL constraint) will
48
+ # short-circuit (though we have not confirmed that).
37
49
  if options.has_key?(:default)
38
50
  if ActiveRecord::Base.connection.postgresql_version < 11_00_00
39
51
  raise PgHaMigrations::UnsafeMigrationError.new(":default is NOT SAFE! Use safe_change_column_default afterwards then backfill the data to prevent locking the table")
40
52
  elsif options[:default].is_a?(Proc) || (options[:default].is_a?(String) && !([:string, :text, :binary].include?(type.to_sym) || _type_is_enum(type)))
41
53
  raise PgHaMigrations::UnsafeMigrationError.new(":default is not safe if the default value is volatile. Use safe_change_column_default afterwards then backfill the data to prevent locking the table")
42
54
  end
43
- end
44
- if options[:null] == false
55
+ elsif options[:null] == false
45
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")
46
57
  end
47
58
 
59
+ unless options.has_key?(:default)
60
+ self.safe_added_columns_without_default_value << [table.to_s, column.to_s]
61
+ end
62
+
48
63
  unsafe_add_column(table, column, type, options)
49
64
  end
50
65
 
@@ -55,6 +70,12 @@ module PgHaMigrations::SafeStatements
55
70
  end
56
71
 
57
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
+
58
79
  column = connection.send(:column_for, table_name, column_name)
59
80
 
60
81
  # In 5.2 we have an edge whereby passing in a string literal with an expression
@@ -1,3 +1,3 @@
1
1
  module PgHaMigrations
2
- VERSION = "1.2.5"
2
+ VERSION = "1.5.0"
3
3
  end
@@ -8,14 +8,16 @@ module PgHaMigrations
8
8
  Config = Struct.new(
9
9
  :disable_default_migration_methods,
10
10
  :check_for_dependent_objects,
11
- :allow_force_create_table
11
+ :allow_force_create_table,
12
+ :prefer_single_step_column_addition_with_default
12
13
  )
13
14
 
14
15
  def self.config
15
16
  @config ||= Config.new(
16
17
  true,
17
18
  false,
18
- true
19
+ true,
20
+ false
19
21
  )
20
22
  end
21
23
 
@@ -29,18 +31,24 @@ module PgHaMigrations
29
31
  # Safe versus unsafe in this context specifically means the following:
30
32
  # - Safe operations will not block for long periods of time.
31
33
  # - Unsafe operations _may_ block for long periods of time.
32
- UnsafeMigrationError = Class.new(Exception)
34
+ UnsafeMigrationError = Class.new(StandardError)
33
35
 
34
36
  # Invalid migrations are operations which we expect to not function
35
37
  # as expected or get the schema into an inconsistent state
36
- InvalidMigrationError = Class.new(Exception)
38
+ InvalidMigrationError = Class.new(StandardError)
39
+
40
+ # Operations violating a best practice, but not actually unsafe will
41
+ # raise this error. For example, adding a column without a default and
42
+ # then setting its default in a second action in a single migration
43
+ # isn't our documented best practice and will raise this error.
44
+ BestPracticeError = Class.new(Exception)
37
45
 
38
46
  # Unsupported migrations use ActiveRecord::Migration features that
39
47
  # we don't support, and therefore will likely have unexpected behavior.
40
- UnsupportedMigrationError = Class.new(Exception)
48
+ UnsupportedMigrationError = Class.new(StandardError)
41
49
 
42
50
  # This gem only supports the PostgreSQL adapter at this time.
43
- UnsupportedAdapter = Class.new(Exception)
51
+ UnsupportedAdapter = Class.new(StandardError)
44
52
  end
45
53
 
46
54
  require "pg_ha_migrations/blocking_database_transactions"
@@ -29,14 +29,14 @@ 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.9.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.1"
40
+ spec.add_dependency "rails", ">= 5.0", "< 7.1"
41
41
  spec.add_dependency "relation_to_struct", ">= 1.5.1"
42
42
  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.2.5
4
+ version: 1.5.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-03-05 00:00:00.000000000 Z
17
+ date: 2022-05-09 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.9.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.9.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.1'
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.1'
136
+ version: '7.1'
137
137
  - !ruby/object:Gem::Dependency
138
138
  name: relation_to_struct
139
139
  requirement: !ruby/object:Gem::Requirement
@@ -161,7 +161,6 @@ files:
161
161
  - ".pryrc"
162
162
  - ".rspec"
163
163
  - ".ruby-version"
164
- - ".travis.yml"
165
164
  - Appraisals
166
165
  - CODE_OF_CONDUCT.md
167
166
  - Gemfile
@@ -175,6 +174,8 @@ files:
175
174
  - gemfiles/rails_5.1.gemfile
176
175
  - gemfiles/rails_5.2.gemfile
177
176
  - gemfiles/rails_6.0.gemfile
177
+ - gemfiles/rails_6.1.gemfile
178
+ - gemfiles/rails_7.0.gemfile
178
179
  - lib/pg_ha_migrations.rb
179
180
  - lib/pg_ha_migrations/allowed_versions.rb
180
181
  - 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"