pg_ha_migrations 1.2.0 → 1.2.5

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: 36546429641ce670fa116d251f17993538b3ab60b5014c1917103e45b1fa0f45
4
- data.tar.gz: 969078be676bc767aa8f325328da8aeda95b6120e620c5d8556bda639c94a3f4
3
+ metadata.gz: aa56359f3f0d8d56cc060ffbda09e40a234bbd80581de50d637caf8fc5ffb94b
4
+ data.tar.gz: e53c77e2a38349e16e8dd977747b0bb1691ed7a4643a7ae5dda26d05ef3cf71e
5
5
  SHA512:
6
- metadata.gz: d2ee13eed929c6278cf745b6b4e4ecff210858e930f97be67d0db2ff4bd2efc65288cc2c80090343cb6cc3e2097c8e1fcf62955fddf442eced723660793706df
7
- data.tar.gz: d1f0853d0d47774e889835716f6619097f3e56016e8997a7115e31c34e87dfc80b0871eee128161f4583247265afbfd9aeae752312dad4bb11a5dd5bf0946184
6
+ metadata.gz: ffca13e9164a6d1a82b03fcb521707aef486d78385db1e48f5c43d7702399316d1616a166e89e917f28c18f7aba06b3eec89316c34218ecab851599132b688c6
7
+ data.tar.gz: 657e725471a0caad6e100e0cb009f242b2af4d2eaf4385d65c02623e0366d57ff4d24a6dcc71ba1457e34f80f972d9c0a0704bf238f0442fa902df119440146c
@@ -0,0 +1,41 @@
1
+ name: CI
2
+ on: [push, pull_request]
3
+ jobs:
4
+ test:
5
+ strategy:
6
+ matrix:
7
+ pg:
8
+ - 9.6
9
+ - 10
10
+ - 11
11
+ - 12
12
+ gemfile:
13
+ - rails_5.0
14
+ - rails_5.1
15
+ - rails_5.2
16
+ - rails_6.0
17
+ name: PostgreSQL ${{ matrix.pg }}
18
+ runs-on: ubuntu-latest
19
+ env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
20
+ BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile
21
+ ImageOS: ubuntu20
22
+ services:
23
+ postgresql:
24
+ image: postgres:${{ matrix.pg }}
25
+ env:
26
+ POSTGRES_PASSWORD: postgres
27
+ # Set health checks to wait until postgres has started
28
+ options: >-
29
+ --health-cmd pg_isready
30
+ --health-interval 10s
31
+ --health-timeout 5s
32
+ --health-retries 5
33
+ ports:
34
+ - 5432:5432
35
+ steps:
36
+ - uses: actions/checkout@v2
37
+ - name: Setup Ruby using .ruby-version file
38
+ uses: ruby/setup-ruby@v1
39
+ with:
40
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
41
+ - run: bundle exec rake spec
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- ruby-2.4.3
1
+ ruby-2.7
data/.travis.yml CHANGED
@@ -1,16 +1,27 @@
1
1
  sudo: false
2
2
  language: ruby
3
3
  rvm:
4
- - 2.4
4
+ - 2.5
5
+ env:
6
+ jobs:
7
+ - PGVERSION: "9.6"
8
+ - PGVERSION: "10"
9
+ - PGVERSION: "11"
10
+ - PGVERSION: "12"
5
11
  services:
6
12
  - postgresql
7
- addons:
8
- postgresql: "9.6"
9
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
10
20
  - gem uninstall -v '>= 2' -i $(rvm gemdir)@global -ax bundler || true
11
21
  - gem install bundler -v 1.15.4
12
22
  gemfile:
13
23
  - gemfiles/rails_5.0.gemfile
14
24
  - gemfiles/rails_5.1.gemfile
15
25
  - gemfiles/rails_5.2.gemfile
26
+ - gemfiles/rails_6.0.gemfile
16
27
  script: "bundle exec rake spec"
data/Appraisals CHANGED
@@ -10,3 +10,7 @@ appraise "rails-5.2" do
10
10
  gem "rails", "5.2.3"
11
11
  end
12
12
 
13
+ appraise "rails-6.0" do
14
+ gem "rails", "6.0.0"
15
+ end
16
+
data/README.md CHANGED
@@ -55,7 +55,7 @@ There are two major classes of concerns we try to handle in the API:
55
55
 
56
56
  We rename migration methods with prefixes denoting their safety level:
57
57
 
58
- - `safe_*`: These methods check for both application and database safety concerns prefer concurrent operations where available, set low lock timeouts where appropriate, and decompose operations into multiple safe steps.
58
+ - `safe_*`: These methods check for both application and database safety concerns, prefer concurrent operations where available, set low lock timeouts where appropriate, and decompose operations into multiple safe steps.
59
59
  - `unsafe_*`: These methods are generally a direct dispatch to the native ActiveRecord migration method.
60
60
 
61
61
  Calling the original migration methods without a prefix will raise an error.
@@ -106,6 +106,18 @@ Safely add a new enum value.
106
106
  safe_add_enum_value :enum, "value"
107
107
  ```
108
108
 
109
+ #### unsafe\_rename\_enum\_value
110
+
111
+ Unsafely change the value of an enum type entry.
112
+
113
+ ```ruby
114
+ unsafe_rename_enum_value(:enum, "old_value", "new_value")
115
+ ```
116
+
117
+ Note:
118
+
119
+ Changing an enum value does not issue any long-running scans or acquire locks on usages of the enum type. Therefore multiple queries within a transaction concurrent with the change may see both the old and new values. To highlight these potential pitfalls no `safe_rename_enum_value` equivalent exists. Before modifying an enum type entry you should verify that no concurrently executing queries will attempt to write the old value and that read queries understand the new value.
120
+
109
121
  #### safe\_add\_column
110
122
 
111
123
  Safely add a column.
@@ -283,7 +295,7 @@ Rake::Task["pg_ha_migrations:check_blocking_database_transactions"].enhance ["db
283
295
 
284
296
  ## Development
285
297
 
286
- After checking out the repo, run `bin/setup` to install dependencies and start a postgres docker container. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
298
+ After checking out the repo, run `bin/setup` to install dependencies and start a postgres docker container. Then, run `bundle exec rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. This project uses Appraisal to test against multiple versions of ActiveRecord; you can run the tests against all supported version with `bundle exec appraisal rspec`.
287
299
 
288
300
  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.
289
301
 
data/bin/setup CHANGED
@@ -4,6 +4,7 @@ IFS=$'\n\t'
4
4
  set -vx
5
5
 
6
6
  bundle install
7
+ bundle exec appraisal install
7
8
 
8
9
  # Do any other automated setup that you need to do here
9
10
 
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "6.0.0"
6
+
7
+ gemspec path: "../"
@@ -57,8 +57,8 @@ module PgHaMigrations::AutoIncluder
57
57
  def inherited(klass)
58
58
  super(klass) if defined?(super)
59
59
 
60
- klass.prepend(PgHaMigrations::SafeStatements)
61
60
  klass.prepend(PgHaMigrations::UnsafeStatements)
61
+ klass.prepend(PgHaMigrations::SafeStatements)
62
62
  end
63
63
  end
64
64
 
@@ -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].map do |v|
4
+ ALLOWED_VERSIONS = [4.2, 5.0, 5.1, 5.2, 6.0].map do |v|
5
5
  begin
6
6
  ActiveRecord::Migration[v]
7
7
  rescue ArgumentError
@@ -25,13 +25,19 @@ module PgHaMigrations
25
25
  end
26
26
 
27
27
  def self.find_blocking_transactions(minimum_transaction_age = "0 seconds")
28
- pid_column, query_column = if ActiveRecord::Base.connection.select_value("SHOW server_version") =~ /9\.1/
28
+ postgres_version = ActiveRecord::Base.connection.postgresql_version
29
+ pid_column, query_column = if postgres_version < 9_02_00
29
30
  ["procpid", "current_query"]
30
31
  else
31
32
  ["pid", "query"]
32
33
  end
33
34
 
34
- raw_query = <<-SQL
35
+ # In some versions of Postgres, walsenders show up here with a non-null xact_start.
36
+ # That's been patched, so hard to test, but we should exclude them anyway.
37
+ # https://www.postgresql.org/message-id/flat/20191209234409.exe7osmyalwkt5j4%40development
38
+ ignore_sqlsender_sql = "psa.backend_type != 'walsender'"
39
+
40
+ raw_query = <<~SQL
35
41
  SELECT
36
42
  psa.datname as database, -- Will only ever be one database
37
43
  psa.#{query_column} as current_query,
@@ -59,6 +65,7 @@ module PgHaMigrations
59
65
  )
60
66
  AND psa.xact_start < clock_timestamp() - ?::interval
61
67
  AND psa.#{query_column} !~ ?
68
+ #{postgres_version >= 10_00_00 ? "AND #{ignore_sqlsender_sql}" : ""}
62
69
  GROUP BY psa.datname, psa.#{query_column}, psa.state, psa.xact_start
63
70
  SQL
64
71
 
@@ -24,7 +24,7 @@ module PgHaMigrations
24
24
  end
25
25
 
26
26
  if blocking_transactions.any?(&:concurrent_index_creation?)
27
- report << <<-eos.strip_heredoc.lines.map { |line| "\t#{line}" }.join
27
+ report << <<~eos.lines.map { |line| "\t#{line}" }.join
28
28
  Warning: concurrent indexes are currently being built. If you have any other
29
29
  migrations in this deploy that will attempt to create additional
30
30
  concurrent indexes on the same physical database (even if the table
@@ -25,9 +25,21 @@ module PgHaMigrations::SafeStatements
25
25
  unsafe_execute("ALTER TYPE #{PG::Connection.quote_ident(name.to_s)} ADD VALUE '#{PG::Connection.escape_string(value)}'")
26
26
  end
27
27
 
28
+ def unsafe_rename_enum_value(name, old_value, new_value)
29
+ if ActiveRecord::Base.connection.postgresql_version < 10_00_00
30
+ raise PgHaMigrations::InvalidMigrationError, "Renaming an enum value is not supported on Postgres databases before version 10"
31
+ end
32
+
33
+ unsafe_execute("ALTER TYPE #{PG::Connection.quote_ident(name.to_s)} RENAME VALUE '#{PG::Connection.escape_string(old_value)}' TO '#{PG::Connection.escape_string(new_value)}'")
34
+ end
35
+
28
36
  def safe_add_column(table, column, type, options = {})
29
- if options.has_key? :default
30
- raise PgHaMigrations::UnsafeMigrationError.new(":default is NOT SAFE! Use safe_change_column_default afterwards then backfill the data to prevent locking the table")
37
+ if options.has_key?(:default)
38
+ if ActiveRecord::Base.connection.postgresql_version < 11_00_00
39
+ raise PgHaMigrations::UnsafeMigrationError.new(":default is NOT SAFE! Use safe_change_column_default afterwards then backfill the data to prevent locking the table")
40
+ elsif options[:default].is_a?(Proc) || (options[:default].is_a?(String) && !([:string, :text, :binary].include?(type.to_sym) || _type_is_enum(type)))
41
+ 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
+ end
31
43
  end
32
44
  if options[:null] == false
33
45
  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")
@@ -66,6 +78,8 @@ module PgHaMigrations::SafeStatements
66
78
  # - If the column is text-like or binary, then we can allow anything in the default
67
79
  # value since a Ruby string there will always coerce directly to the equivalent
68
80
  # text/binary value rather than being interpreted as a DDL-time expression.
81
+ # - Custom enum types are a special case: they also are treated like strings by
82
+ # Rails, so we want to allow those as-is.
69
83
  # - Otherwise, disallow any Ruby string values and instead require the Ruby object
70
84
  # type that maps to the column type.
71
85
  #
@@ -78,7 +92,7 @@ module PgHaMigrations::SafeStatements
78
92
  (
79
93
  connection.quote_default_expression(default_value, column) == "NULL" ||
80
94
  (
81
- ![:string, :text, :binary].include?(column.sql_type_metadata.type) &&
95
+ ![:string, :text, :binary, :enum].include?(column.sql_type_metadata.type) &&
82
96
  default_value.is_a?(String)
83
97
  )
84
98
  )
@@ -199,6 +213,10 @@ module PgHaMigrations::SafeStatements
199
213
  raise PgHaMigrations::UnsupportedAdapter, "This gem only works with the #{expected_adapter} adapter, found #{actual_adapter} instead" unless actual_adapter == expected_adapter
200
214
  end
201
215
 
216
+ def _type_is_enum(type)
217
+ ActiveRecord::Base.connection.select_values("SELECT typname FROM pg_type JOIN pg_enum ON pg_type.oid = pg_enum.enumtypid").include?(type.to_s)
218
+ end
219
+
202
220
  def migrate(direction)
203
221
  if respond_to?(:change)
204
222
  raise PgHaMigrations::UnsupportedMigrationError, "Tracking changes for automated rollback is not supported; use explicit #up instead."
@@ -39,7 +39,6 @@ module PgHaMigrations::UnsafeStatements
39
39
  delegate_unsafe_method_to_migration_base_class :change_column
40
40
  delegate_unsafe_method_to_migration_base_class :change_column_default
41
41
  delegate_unsafe_method_to_migration_base_class :remove_column
42
- delegate_unsafe_method_to_migration_base_class :add_index
43
42
  delegate_unsafe_method_to_migration_base_class :execute
44
43
  delegate_unsafe_method_to_migration_base_class :remove_index
45
44
  delegate_unsafe_method_to_migration_base_class :add_foreign_key
@@ -69,6 +68,16 @@ module PgHaMigrations::UnsafeStatements
69
68
  execute_ancestor_statement(:create_table, table, options, &block)
70
69
  end
71
70
 
71
+ def unsafe_add_index(table, column_names, options = {})
72
+ if ((ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR >= 2) || ActiveRecord::VERSION::MAJOR > 5) &&
73
+ column_names.is_a?(String) && /\W/.match?(column_names) && options.key?(:opclass)
74
+ 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
+ end
76
+
77
+ execute_ancestor_statement(:add_index, table, column_names, options)
78
+ end
79
+
80
+
72
81
  def execute_ancestor_statement(method_name, *args, &block)
73
82
  # Dispatching here is a bit complicated: we need to execute the method
74
83
  # belonging to the first member of the inheritance chain (besides
@@ -1,3 +1,3 @@
1
1
  module PgHaMigrations
2
- VERSION = "1.2.0"
2
+ VERSION = "1.2.5"
3
3
  end
@@ -37,6 +37,6 @@ Gem::Specification.new do |spec|
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", "< 5.3"
41
- spec.add_dependency "relation_to_struct"
40
+ spec.add_dependency "rails", ">= 5.0", "< 6.1"
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.0
4
+ version: 1.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - celeen
@@ -14,7 +14,7 @@ authors:
14
14
  autorequire:
15
15
  bindir: exe
16
16
  cert_chain: []
17
- date: 2020-01-31 00:00:00.000000000 Z
17
+ date: 2021-03-05 00:00:00.000000000 Z
18
18
  dependencies:
19
19
  - !ruby/object:Gem::Dependency
20
20
  name: rake
@@ -123,7 +123,7 @@ dependencies:
123
123
  version: '5.0'
124
124
  - - "<"
125
125
  - !ruby/object:Gem::Version
126
- version: '5.3'
126
+ version: '6.1'
127
127
  type: :runtime
128
128
  prerelease: false
129
129
  version_requirements: !ruby/object:Gem::Requirement
@@ -133,21 +133,21 @@ dependencies:
133
133
  version: '5.0'
134
134
  - - "<"
135
135
  - !ruby/object:Gem::Version
136
- version: '5.3'
136
+ version: '6.1'
137
137
  - !ruby/object:Gem::Dependency
138
138
  name: relation_to_struct
139
139
  requirement: !ruby/object:Gem::Requirement
140
140
  requirements:
141
141
  - - ">="
142
142
  - !ruby/object:Gem::Version
143
- version: '0'
143
+ version: 1.5.1
144
144
  type: :runtime
145
145
  prerelease: false
146
146
  version_requirements: !ruby/object:Gem::Requirement
147
147
  requirements:
148
148
  - - ">="
149
149
  - !ruby/object:Gem::Version
150
- version: '0'
150
+ version: 1.5.1
151
151
  description: Enforces DDL/migration safety in Ruby on Rails project with an emphasis
152
152
  on explicitly choosing trade-offs and avoiding unnecessary magic.
153
153
  email:
@@ -156,6 +156,7 @@ executables: []
156
156
  extensions: []
157
157
  extra_rdoc_files: []
158
158
  files:
159
+ - ".github/workflows/ci.yml"
159
160
  - ".gitignore"
160
161
  - ".pryrc"
161
162
  - ".rspec"
@@ -173,6 +174,7 @@ files:
173
174
  - gemfiles/rails_5.0.gemfile
174
175
  - gemfiles/rails_5.1.gemfile
175
176
  - gemfiles/rails_5.2.gemfile
177
+ - gemfiles/rails_6.0.gemfile
176
178
  - lib/pg_ha_migrations.rb
177
179
  - lib/pg_ha_migrations/allowed_versions.rb
178
180
  - lib/pg_ha_migrations/blocking_database_transactions.rb
@@ -205,7 +207,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
205
207
  - !ruby/object:Gem::Version
206
208
  version: '0'
207
209
  requirements: []
208
- rubygems_version: 3.0.6
210
+ rubygems_version: 3.1.4
209
211
  signing_key:
210
212
  specification_version: 4
211
213
  summary: Enforces DDL/migration safety in Ruby on Rails project with an emphasis on