lib/strong_migrations/checker.rb in strong_migrations-0.7.2 vs lib/strong_migrations/checker.rb in strong_migrations-0.7.3
- old
+ new
@@ -1,9 +1,11 @@
module StrongMigrations
class Checker
- attr_accessor :direction
+ include SafeMethods
+ attr_accessor :direction, :transaction_disabled
+
def initialize(migration)
@migration = migration
@new_tables = []
@safe = false
@timeouts_set = false
@@ -22,11 +24,11 @@
def perform(method, *args)
set_timeouts
check_lock_timeout
- unless safe?
+ if !safe? || safe_by_default_method?(method)
case method
when :remove_column, :remove_columns, :remove_timestamps, :remove_reference, :remove_belongs_to
columns =
case method
when :remove_timestamps
@@ -63,20 +65,22 @@
if columns.is_a?(Array) && columns.size > 3 && !options[:unique]
raise_error :add_index_columns, header: "Best practice"
end
if postgresql? && options[:algorithm] != :concurrently && !new_table?(table)
+ return safe_add_index(table, columns, options) if StrongMigrations.safe_by_default
raise_error :add_index, command: command_str("add_index", [table, columns, options.merge(algorithm: :concurrently)])
end
when :remove_index
table, options = args
unless options.is_a?(Hash)
options = {column: options}
end
options ||= {}
if postgresql? && options[:algorithm] != :concurrently && !new_table?(table)
+ return safe_remove_index(table, options) if StrongMigrations.safe_by_default
raise_error :remove_index, command: command_str("remove_index", [table, options.merge(algorithm: :concurrently)])
end
when :add_column
table, column, type, options = args
options ||= {}
@@ -182,18 +186,18 @@
index_value = options.fetch(:index, true)
concurrently_set = index_value.is_a?(Hash) && index_value[:algorithm] == :concurrently
bad_index = index_value && !concurrently_set
if bad_index || options[:foreign_key]
- columns = options[:polymorphic] ? [:"#{reference}_type", :"#{reference}_id"] : :"#{reference}_id"
-
if index_value.is_a?(Hash)
options[:index] = options[:index].merge(algorithm: :concurrently)
else
options = options.merge(index: {algorithm: :concurrently})
end
+ return safe_add_reference(table, reference, options) if StrongMigrations.safe_by_default
+
if options.delete(:foreign_key)
headline = "Adding a foreign key blocks writes on both tables."
append = "
Then add the foreign key in separate migrations."
@@ -221,18 +225,26 @@
unless safe
# match https://github.com/nullobject/rein
constraint_name = "#{table}_#{column}_null"
- validate_constraint_code = String.new(constraint_str("ALTER TABLE %s VALIDATE CONSTRAINT %s", [table, constraint_name]))
+ add_code = constraint_str("ALTER TABLE %s ADD CONSTRAINT %s CHECK (%s IS NOT NULL) NOT VALID", [table, constraint_name, column])
+ validate_code = constraint_str("ALTER TABLE %s VALIDATE CONSTRAINT %s", [table, constraint_name])
+ remove_code = constraint_str("ALTER TABLE %s DROP CONSTRAINT %s", [table, constraint_name])
+
+ validate_constraint_code = String.new(safety_assured_str(validate_code))
if postgresql_version >= Gem::Version.new("12")
- validate_constraint_code << "\n #{command_str(:change_column_null, [table, column, null])}"
- validate_constraint_code << "\n #{constraint_str("ALTER TABLE %s DROP CONSTRAINT %s", [table, constraint_name])}"
+ change_args = [table, column, null]
+
+ validate_constraint_code << "\n #{command_str(:change_column_null, change_args)}"
+ validate_constraint_code << "\n #{safety_assured_str(remove_code)}"
end
+ return safe_change_column_null(add_code, validate_code, change_args, remove_code) if StrongMigrations.safe_by_default
+
raise_error :change_column_null_postgresql,
- add_constraint_code: constraint_str("ALTER TABLE %s ADD CONSTRAINT %s CHECK (%s IS NOT NULL) NOT VALID", [table, constraint_name, column]),
+ add_constraint_code: safety_assured_str(add_code),
validate_constraint_code: validate_constraint_code
end
elsif mysql? || mariadb?
raise_error :change_column_null_mysql
elsif !default.nil?
@@ -253,14 +265,21 @@
primary_key = options[:primary_key] || "id"
column = options[:column] || "#{to_table.to_s.singularize}_id"
hashed_identifier = Digest::SHA256.hexdigest("#{from_table}_#{column}_fk").first(10)
fk_name = options[:name] || "fk_rails_#{hashed_identifier}"
+ add_code = constraint_str("ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s) NOT VALID", [from_table, fk_name, column, to_table, primary_key])
+ validate_code = constraint_str("ALTER TABLE %s VALIDATE CONSTRAINT %s", [from_table, fk_name])
+
+ return safe_add_foreign_key_code(from_table, to_table, add_code, validate_code) if StrongMigrations.safe_by_default
+
raise_error :add_foreign_key,
- add_foreign_key_code: constraint_str("ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s) NOT VALID", [from_table, fk_name, column, to_table, primary_key]),
- validate_foreign_key_code: constraint_str("ALTER TABLE %s VALIDATE CONSTRAINT %s", [from_table, fk_name])
+ add_foreign_key_code: safety_assured_str(add_code),
+ validate_foreign_key_code: safety_assured_str(validate_code)
else
+ return safe_add_foreign_key(from_table, to_table, options) if StrongMigrations.safe_by_default
+
raise_error :add_foreign_key,
add_foreign_key_code: command_str("add_foreign_key", [from_table, to_table, options.merge(validate: false)]),
validate_foreign_key_code: command_str("validate_foreign_key", [from_table, to_table])
end
end
@@ -473,10 +492,13 @@
@migration.stop!(message, header: header || "Dangerous operation detected")
end
def constraint_str(statement, identifiers)
# not all identifiers are tables, but this method of quoting should be fine
- code = statement % identifiers.map { |v| connection.quote_table_name(v) }
+ statement % identifiers.map { |v| connection.quote_table_name(v) }
+ end
+
+ def safety_assured_str(code)
"safety_assured do\n execute '#{code}' \n end"
end
def command_str(command, args)
str_args = args[0..-2].map { |a| a.inspect }