module PgHaMigrations::UnsafeStatements

Public Class Methods

delegate_unsafe_method_to_migration_base_class(method_name) click to toggle source
# File lib/pg_ha_migrations/unsafe_statements.rb, line 24
def self.delegate_unsafe_method_to_migration_base_class(method_name)
  define_method("unsafe_#{method_name}") do |*args, &block|
    if PgHaMigrations.config.check_for_dependent_objects
      disallow_migration_method_if_dependent_objects!(method_name, arguments: args)
    end

    execute_ancestor_statement(method_name, *args, &block)
  end
end
disable_or_delegate_default_method(method_name, error_message, allow_reentry_from_compatibility_module: false) click to toggle source
# File lib/pg_ha_migrations/unsafe_statements.rb, line 2
def self.disable_or_delegate_default_method(method_name, error_message, allow_reentry_from_compatibility_module: false)
  define_method(method_name) do |*args, &block|
    if PgHaMigrations.config.check_for_dependent_objects
      disallow_migration_method_if_dependent_objects!(method_name, arguments: args)
    end

    if PgHaMigrations.config.disable_default_migration_methods
      # Most migration methods are only ever called by a migration and
      # therefore aren't re-entrant or callable from another migration
      # method, but `execute` is called directly by at least one of the
      # implementations in `ActiveRecord::Migration::Compatibility` so
      # we have to explicitly handle that case by allowing execution of
      # the original implementation by its original name.
      unless  allow_reentry_from_compatibility_module && caller[0] =~ /lib\/active_record\/migration\/compatibility.rb/
        raise PgHaMigrations::UnsafeMigrationError, error_message
      end
    end

    execute_ancestor_statement(method_name, *args, &block)
  end
end

Public Instance Methods

execute_ancestor_statement(method_name, *args, &block) click to toggle source
# File lib/pg_ha_migrations/unsafe_statements.rb, line 81
def execute_ancestor_statement(method_name, *args, &block)
  # Dispatching here is a bit complicated: we need to execute the method
  # belonging to the first member of the inheritance chain (besides
  # UnsafeStatements). If don't find the method in the inheritance chain,
  # we need to rely on the ActiveRecord::Migration#method_missing
  # implementation since much of ActiveRecord::Migration's functionality
  # is not implemented in real methods but rather by proxying.
  #
  # For example, ActiveRecord::Migration doesn't define #create_table.
  # Instead ActiveRecord::Migration#method_missing proxies the method
  # to the connection. However some migration compatibility version
  # subclasses _do_ explicitly define #create_table, so we can't rely
  # on only one way of finding the proper dispatch target.

  # Exclude our `raise` guard implementations.
  ancestors_without_unsafe_statements = self.class.ancestors - [PgHaMigrations::UnsafeStatements]

  delegate_method = self.method(method_name)
  candidate_method = delegate_method

  # Find the first usable method in the ancestor chain
  # or stop looking if there are no more possible
  # implementations.
  until candidate_method.nil? || ancestors_without_unsafe_statements.include?(candidate_method.owner)
    candidate_method = candidate_method.super_method
  end

  if candidate_method
    delegate_method = candidate_method
  end

  # If we failed to find a concrete implementation from the
  # inheritance chain, use ActiveRecord::Migrations# method_missing
  # otherwise use the method from the inheritance chain.
  if delegate_method.owner == PgHaMigrations::UnsafeStatements
    method_missing(method_name, *args, &block)
  else
    delegate_method.call(*args, &block)
  end
end
unsafe_add_index(table, column_names, options = {}) click to toggle source
# File lib/pg_ha_migrations/unsafe_statements.rb, line 71
def unsafe_add_index(table, column_names, options = {})
  if ((ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR >= 2) || ActiveRecord::VERSION::MAJOR > 5) &&
      column_names.is_a?(String) && /\W/.match?(column_names) && options.key?(:opclass)
    raise PgHaMigrations::InvalidMigrationError, "ActiveRecord drops the :opclass option when supplying a string containing an expression or list of columns; instead either supply an array of columns or include the opclass in the string for each column"
  end

  execute_ancestor_statement(:add_index, table, column_names, options)
end
unsafe_create_table(table, options={}, &block) click to toggle source
# File lib/pg_ha_migrations/unsafe_statements.rb, line 63
def unsafe_create_table(table, options={}, &block)
  if options[:force] && !PgHaMigrations.config.allow_force_create_table
    raise PgHaMigrations::UnsafeMigrationError.new(":force is NOT SAFE! Explicitly call unsafe_drop_table first if you want to recreate an existing table")
  end

  execute_ancestor_statement(:create_table, table, options, &block)
end