lib/strong_migrations/checker.rb in strong_migrations-0.6.2 vs lib/strong_migrations/checker.rb in strong_migrations-0.6.3
- old
+ new
@@ -99,17 +99,54 @@
if type.to_s == "json" && postgresql?
raise_error :add_column_json
end
when :change_column
- table, column, type = args
+ table, column, type, options = args
+ options ||= {}
safe = false
- # assume Postgres 9.1+ since previous versions are EOL
- if postgresql? && type.to_s == "text"
- found_column = connection.columns(table).find { |c| c.name.to_s == column.to_s }
- safe = found_column && found_column.type == :string
+ existing_column = connection.columns(table).find { |c| c.name.to_s == column.to_s }
+ if existing_column
+ sql_type = existing_column.sql_type.split("(").first
+ if postgresql?
+ case type.to_s
+ when "string", "text"
+ # safe to change limit for varchar
+ safe = ["character varying", "text"].include?(sql_type)
+ when "numeric", "decimal"
+ # numeric and decimal are equivalent and can be used interchangably
+ safe = ["numeric", "decimal"].include?(sql_type) &&
+ (
+ (
+ # unconstrained
+ !options[:precision] && !options[:scale]
+ ) || (
+ # increased precision, same scale
+ options[:precision] && existing_column.precision &&
+ options[:precision] >= existing_column.precision &&
+ options[:scale] == existing_column.scale
+ )
+ )
+ when "datetime", "timestamp", "timestamptz"
+ safe = ["timestamp without time zone", "timestamp with time zone"].include?(sql_type) &&
+ postgresql_version >= Gem::Version.new("12") &&
+ connection.select_all("SHOW timezone").first["TimeZone"] == "UTC"
+ end
+ elsif mysql? || mariadb?
+ case type.to_s
+ when "string"
+ # https://dev.mysql.com/doc/refman/5.7/en/innodb-online-ddl-operations.html
+ # https://mariadb.com/kb/en/innodb-online-ddl-operations-with-the-instant-alter-algorithm/#changing-the-data-type-of-a-column
+ # increased limit, but doesn't change number of length bytes
+ # 1-255 = 1 byte, 256-65532 = 2 bytes, 65533+ = too big for varchar
+ limit = options[:limit] || 255
+ safe = ["varchar"].include?(sql_type) &&
+ limit >= existing_column.limit &&
+ (limit <= 255 || existing_column.limit > 255)
+ end
+ end
end
raise_error :change_column unless safe
when :create_table
table, options = args
options ||= {}
@@ -323,10 +360,11 @@
if vars[:append]
vars[:append] = vars[:append].gsub(/%(?!{)/, "%%") % vars
end
# escape % not followed by {
- @migration.stop!(message.gsub(/%(?!{)/, "%%") % vars, header: header || "Dangerous operation detected")
+ message = message.gsub(/%(?!{)/, "%%") % vars if message.include?("%")
+ @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) }