class OnlineMigrations::BackgroundMigrations::CopyColumn

@private

Attributes

copy_from[R]
copy_to[R]
model_name[R]
table_name[R]
type_cast_functions[R]

Public Class Methods

new(table_name, copy_from, copy_to, model_name = nil, type_cast_functions = {}) click to toggle source
# File lib/online_migrations/background_migrations/copy_column.rb, line 9
def initialize(table_name, copy_from, copy_to, model_name = nil, type_cast_functions = {})
  @table_name = table_name

  if copy_from.is_a?(Array) && type_cast_functions && !type_cast_functions.is_a?(Hash)
    raise ArgumentError, "type_cast_functions must be a Hash"
  end

  @copy_from = Array.wrap(copy_from)
  @copy_to = Array.wrap(copy_to)

  if @copy_from.size != @copy_to.size
    raise ArgumentError, "Number of source and destination columns must match"
  end

  @model_name = model_name
  @type_cast_functions = type_cast_functions
end

Public Instance Methods

count() click to toggle source
# File lib/online_migrations/background_migrations/copy_column.rb, line 70
def count
  # Exact counts are expensive on large tables, since PostgreSQL
  # needs to do a full scan. An estimated count should give a pretty decent
  # approximation of rows count in this case.
  Utils.estimated_count(connection, table_name)
end
process_batch(relation) click to toggle source
# File lib/online_migrations/background_migrations/copy_column.rb, line 38
def process_batch(relation)
  arel = relation.arel
  arel_table = relation.arel_table

  old_values = copy_from.map do |from_column|
    old_value = arel_table[from_column]
    if (type_cast_function = type_cast_functions[from_column])
      if Utils.ar_version <= 5.2
        # ActiveRecord <= 5.2 does not support quoting of Arel::Nodes::NamedFunction
        old_value = Arel.sql("#{type_cast_function}(#{connection.quote_column_name(from_column)})")
      else
        old_value = Arel::Nodes::NamedFunction.new(type_cast_function, [old_value])
      end
    end
    old_value
  end

  if Utils.ar_version <= 4.2
    stmt = Arel::UpdateManager.new(arel.engine)
  else
    stmt = Arel::UpdateManager.new
  end

  stmt.table(arel_table)
  stmt.wheres = arel.constraints

  updates = copy_to.zip(old_values).map { |to_column, old_value| [arel_table[to_column], old_value] }
  stmt.set(updates)

  connection.update(stmt)
end
relation() click to toggle source
# File lib/online_migrations/background_migrations/copy_column.rb, line 27
def relation
  relation = model
    .unscoped
    .where(copy_to.map { |to_column| [to_column, nil] }.to_h)

  Utils.ar_where_not_multiple_conditions(
    relation,
    copy_from.map { |from_column| [from_column, nil] }.to_h
  )
end

Private Instance Methods

connection() click to toggle source
# File lib/online_migrations/background_migrations/copy_column.rb, line 86
def connection
  model.connection
end
model() click to toggle source
# File lib/online_migrations/background_migrations/copy_column.rb, line 78
def model
  @model ||= if model_name.present?
               Object.const_get(model_name, false)
             else
               Utils.define_model(table_name)
             end
end