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) }