Skip to content

Commit

Permalink
Added check for add_exclusion_constraint - closes #191
Browse files Browse the repository at this point in the history
  • Loading branch information
ankane committed Oct 30, 2022
1 parent 0182d27 commit 4ff6be6
Show file tree
Hide file tree
Showing 8 changed files with 68 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## 1.4.0 (unreleased)

- Added check for `add_exclusion_constraint`
- Added support for `RACK_ENV`
- Fixed error when `Rails` defined without `Rails.env`
- Fixed error with `change_column_null` when table does not exist
Expand Down
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ Postgres-specific checks:
- [adding an index non-concurrently](#adding-an-index-non-concurrently)
- [adding a reference](#adding-a-reference)
- [adding a foreign key](#adding-a-foreign-key)
- [adding an exclusion constraint](#adding-an-exclusion-constraint) [unreleased]
- [adding a json column](#adding-a-json-column)
- [setting NOT NULL on an existing column](#setting-not-null-on-an-existing-column)

Expand Down Expand Up @@ -488,6 +489,24 @@ class ValidateForeignKeyOnUsers < ActiveRecord::Migration[7.0]
end
```

### Adding an exclusion constraint

#### Bad

In Postgres, adding an exclusion constraint blocks reads and writes while every row is checked.

```ruby
class AddExclusionContraint < ActiveRecord::Migration[7.1]
def change
add_exclusion_constraint :users, "number WITH =", using: :gist
end
end
```

#### Good

[Let us know](https://github.com/ankane/strong_migrations/issues/new) if you have a safe way to do this (exclusion constraints cannot be marked `NOT VALID`).

### Adding a json column

#### Bad
Expand Down
2 changes: 2 additions & 0 deletions lib/strong_migrations/checker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ def perform(method, *args)
check_add_check_constraint(*args)
when :add_column
check_add_column(*args)
when :add_exclusion_constraint
check_add_exclusion_constraint(*args)
when :add_foreign_key
check_add_foreign_key(*args)
when :add_index
Expand Down
8 changes: 8 additions & 0 deletions lib/strong_migrations/checks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ def check_add_column(*args)
end
end

def check_add_exclusion_constraint(*args)
table = args[0]

unless new_table?(table)
raise_error :add_exclusion_constraint
end
end

# unlike add_index, we don't make an exception here for new tables
#
# with add_index, it's fine to lock a new table even after inserting data
Expand Down
5 changes: 4 additions & 1 deletion lib/strong_migrations/error_messages.rb
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,10 @@ def change

validate_check_constraint:
"Validating a check constraint while writes are blocked is dangerous.
Use disable_ddl_transaction! or a separate migration."
Use disable_ddl_transaction! or a separate migration.",

add_exclusion_constraint:
"Adding an exclusion constraint blocks reads and writes while every row is checked."
}
self.enabled_checks = (error_messages.keys - [:remove_index]).map { |k| [k, {}] }.to_h
end
16 changes: 16 additions & 0 deletions test/add_exclusion_contraint_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
require_relative "test_helper"

class AddExclusionConstraintTest < Minitest::Test
def setup
skip unless ActiveRecord::VERSION::STRING.to_f >= 7.1
super
end

def test_basic
assert_unsafe AddExclusionConstraint
end

def test_new_table
assert_safe AddExclusionConstraintNewTable
end
end
15 changes: 15 additions & 0 deletions test/migrations/add_exclusion_constraint.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class AddExclusionConstraint < TestMigration
def change
add_exclusion_constraint :users, "credit_score WITH =", using: :gist
end
end

class AddExclusionConstraintNewTable < TestMigration
def change
create_table :new_users do |t|
t.decimal :credit_score, precision: 10, scale: 5
end

add_exclusion_constraint :new_users, "credit_score WITH =", using: :gist
end
end
3 changes: 3 additions & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ def schema_migration
ActiveRecord::Schema.define do
enable_extension "citext" if $adapter == "postgresql"

# for exclusion constraints
enable_extension "btree_gist" if $adapter == "postgresql"

# for gen_random_uuid() in Postgres < 13
enable_extension "pgcrypto" if $adapter == "postgresql"

Expand Down

0 comments on commit 4ff6be6

Please sign in to comment.