# frozen_string_literal: true
module ActiveRecord
class Migration
module Compatibility # :nodoc: all
def self.find(version)
version = version.to_s
name = "V#{version.tr('.', '_')}"
unless const_defined?(name)
versions = constants.grep(/\AV[0-9_]+\z/).map { |s| s.to_s.delete("V").tr("_", ".").inspect }
raise ArgumentError, "Unknown migration version #{version.inspect}; expected one of #{versions.sort.join(', ')}"
end
const_get(name)
end
V5_2 = Current
class V5_1 < V5_2
end
class V5_0 < V5_1
module TableDefinition
def primary_key(name, type = :primary_key, **options)
type = :integer if type == :primary_key
super
end
def references(*args, **options)
super(*args, type: :integer, **options)
end
alias :belongs_to :references
end
def create_table(table_name, options = {})
if adapter_name == "PostgreSQL"
if options[:id] == :uuid && !options.key?(:default)
options[:default] = "uuid_generate_v4()"
end
end
unless adapter_name == "Mysql2" && options[:id] == :bigint
if [:integer, :bigint].include?(options[:id]) && !options.key?(:default)
options[:default] = nil
end
end
# Since 5.1 PostgreSQL adapter uses bigserial type for primary
# keys by default and MySQL uses bigint. This compat layer makes old migrations utilize
# serial/int type instead -- the way it used to work before 5.1.
unless options.key?(:id)
options[:id] = :integer
end
if block_given?
super do |t|
yield compatible_table_definition(t)
end
else
super
end
end
def change_table(table_name, options = {})
if block_given?
super do |t|
yield compatible_table_definition(t)
end
else
super
end
end
def create_join_table(table_1, table_2, column_options: {}, **options)
column_options.reverse_merge!(type: :integer)
if block_given?
super do |t|
yield compatible_table_definition(t)
end
else
super
end
end
def add_column(table_name, column_name, type, options = {})
if type == :primary_key
type = :integer
options[:primary_key] = true
end
super
end
def add_reference(table_name, ref_name, **options)
super(table_name, ref_name, type: :integer, **options)
end
alias :add_belongs_to :add_reference
private
def compatible_table_definition(t)
class << t
prepend TableDefinition
end
t
end
end
class V4_2 < V5_0
module TableDefinition
def references(*, **options)
options[:index] ||= false
super
end
alias :belongs_to :references
def timestamps(**options)
options[:null] = true if options[:null].nil?
super
end
end
def create_table(table_name, options = {})
if block_given?
super do |t|
yield compatible_table_definition(t)
end
else
super
end
end
def change_table(table_name, options = {})
if block_given?
super do |t|
yield compatible_table_definition(t)
end
else
super
end
end
def add_reference(*, **options)
options[:index] ||= false
super
end
alias :add_belongs_to :add_reference
def add_timestamps(_, **options)
options[:null] = true if options[:null].nil?
super
end
def index_exists?(table_name, column_name, options = {})
column_names = Array(column_name).map(&:to_s)
options[:name] =
if options[:name].present?
options[:name].to_s
else
index_name(table_name, column: column_names)
end
super
end
def remove_index(table_name, options = {})
options = { column: options } unless options.is_a?(Hash)
options[:name] = index_name_for_remove(table_name, options)
super(table_name, options)
end
private
def compatible_table_definition(t)
class << t
prepend TableDefinition
end
super
end
def index_name_for_remove(table_name, options = {})
index_name = index_name(table_name, options)
unless index_name_exists?(table_name, index_name)
if options.is_a?(Hash) && options.has_key?(:name)
options_without_column = options.dup
options_without_column.delete :column
index_name_without_column = index_name(table_name, options_without_column)
return index_name_without_column if index_name_exists?(table_name, index_name_without_column)
end
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
end
index_name
end
end
end
end
end