lib/strong_migrations/checker.rb in strong_migrations-0.6.7 vs lib/strong_migrations/checker.rb in strong_migrations-0.6.8
- old
+ new
@@ -194,16 +194,30 @@
raise_error :execute, header: "Possibly dangerous operation"
when :change_column_null
table, column, null, default = args
if !null
if postgresql?
- # match https://github.com/nullobject/rein
- constraint_name = "#{table}_#{column}_null"
+ safe = false
+ if postgresql_version >= Gem::Version.new("12")
+ # TODO likely need to quote the column in some situations
+ safe = constraints(table).any? { |c| c["def"] == "CHECK ((#{column} IS NOT NULL))" }
+ end
- 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]),
- validate_constraint_code: constraint_str("ALTER TABLE %s VALIDATE CONSTRAINT %s", [table, constraint_name])
+ 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]))
+ 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])}"
+ end
+
+ 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]),
+ validate_constraint_code: validate_constraint_code
+ end
elsif mysql? || mariadb?
raise_error :change_column_null_mysql
elsif !default.nil?
raise_error :change_column_null,
code: backfill_code(table, column, default)
@@ -253,11 +267,10 @@
end
result
end
- # TODO allow string timeouts in 0.7.0
def set_timeouts
if !@timeouts_set
if StrongMigrations.statement_timeout
statement =
if postgresql?
@@ -377,20 +390,20 @@
end
end
# units: https://www.postgresql.org/docs/current/config-setting.html
def timeout_to_sec(timeout)
- suffixes = {
+ units = {
"us" => 0.001,
"ms" => 1,
"s" => 1000,
"min" => 1000 * 60,
"h" => 1000 * 60 * 60,
"d" => 1000 * 60 * 60 * 24
}
timeout_ms = timeout.to_i
- suffixes.each do |k, v|
+ units.each do |k, v|
if timeout.end_with?(k)
timeout_ms *= v
break
end
end
@@ -401,9 +414,19 @@
if timeout.is_a?(String)
timeout
else
timeout.to_i * 1000
end
+ end
+
+ def constraints(table_name)
+ query = <<-SQL
+ SELECT conname AS name, pg_get_constraintdef(oid) AS def FROM pg_constraint
+ WHERE contype = 'c'
+ AND convalidated
+ AND conrelid = #{connection.quote(connection.quote_table_name(table_name))}::regclass
+ SQL
+ connection.select_all(query.squish).to_a
end
def raise_error(message_key, header: nil, append: nil, **vars)
return unless StrongMigrations.check_enabled?(message_key, version: version)