123456789_123456789_123456789_123456789_123456789_

Class: ActiveRecord::SchemaDumper

Do not use. This class is for internal use only.

Overview

This class is used to dump the database schema for some connection to some output format (i.e., Schema).

Class Attribute Summary

  • .chk_ignore_pattern (also: #chk_ignore_pattern) rw

    Specify a custom regular expression matching check constraints which name should not be dumped to db/schema.rb.

  • .excl_ignore_pattern (also: #excl_ignore_pattern) rw

    Specify a custom regular expression matching exclusion constraints which name should not be dumped to db/schema.rb.

  • .fk_ignore_pattern (also: #fk_ignore_pattern) rw

    Specify a custom regular expression matching foreign keys which name should not be dumped to db/schema.rb.

  • .ignore_tables (also: #ignore_tables) rw

    A list of tables which should not be dumped to the schema.

  • .unique_ignore_pattern (also: #unique_ignore_pattern) rw

    Specify a custom regular expression matching unique constraints which name should not be dumped to db/schema.rb.

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(connection, options = {}) ⇒ SchemaDumper (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 71

def initialize(connection, options = {})
  @connection = connection
  @version = connection.migration_context.current_version rescue nil
  @options = options
  @ignore_tables = [
    ActiveRecord::Base.schema_migrations_table_name,
    ActiveRecord::Base.,
    self.class.ignore_tables
  ].flatten
end

Class Attribute Details

.chk_ignore_pattern (rw) Also known as: #chk_ignore_pattern

Specify a custom regular expression matching check constraints which name should not be dumped to db/schema.rb.

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 29

cattr_accessor :chk_ignore_pattern, default: /^chk_rails_[0-9a-f]{10}$/

.excl_ignore_pattern (rw) Also known as: #excl_ignore_pattern

Specify a custom regular expression matching exclusion constraints which name should not be dumped to db/schema.rb.

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 35

cattr_accessor :excl_ignore_pattern, default: /^excl_rails_[0-9a-f]{10}$/

.fk_ignore_pattern (rw) Also known as: #fk_ignore_pattern

Specify a custom regular expression matching foreign keys which name should not be dumped to db/schema.rb.

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 23

cattr_accessor :fk_ignore_pattern, default: /^fk_rails_[0-9a-f]{10}$/

.ignore_tables (rw) Also known as: #ignore_tables

A list of tables which should not be dumped to the schema. Acceptable values are strings and regexps.

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 17

cattr_accessor :ignore_tables, default: []

.unique_ignore_pattern (rw) Also known as: #unique_ignore_pattern

Specify a custom regular expression matching unique constraints which name should not be dumped to db/schema.rb.

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 41

cattr_accessor :unique_ignore_pattern, default: /^uniq_rails_[0-9a-f]{10}$/

Class Method Details

.dump(connection = ActiveRecord::Base.connection, stream = STDOUT, config = ActiveRecord::Base)

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 44

def dump(connection = ActiveRecord::Base.connection, stream = STDOUT, config = ActiveRecord::Base)
  connection.create_schema_dumper(generate_options(config)).dump(stream)
  stream
end

.generate_options(config) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 50

def generate_options(config)
  {
    table_name_prefix: config.table_name_prefix,
    table_name_suffix: config.table_name_suffix
  }
end

Instance Attribute Details

#chk_ignore_pattern (rw)

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 29

cattr_accessor :chk_ignore_pattern, default: /^chk_rails_[0-9a-f]{10}$/

#excl_ignore_pattern (rw)

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 35

cattr_accessor :excl_ignore_pattern, default: /^excl_rails_[0-9a-f]{10}$/

#fk_ignore_pattern (rw)

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 23

cattr_accessor :fk_ignore_pattern, default: /^fk_rails_[0-9a-f]{10}$/

#ignore_tables (rw)

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 17

cattr_accessor :ignore_tables, default: []

#table_name (rw, private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 69

attr_accessor :table_name

#unique_ignore_pattern (rw)

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 41

cattr_accessor :unique_ignore_pattern, default: /^uniq_rails_[0-9a-f]{10}$/

Instance Method Details

#check_constraints_in_create(table, stream) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 263

def check_constraints_in_create(table, stream)
  if (check_constraints = @connection.check_constraints(table)).any?
    add_check_constraint_statements = check_constraints.map do |check_constraint|
      parts = [
        "t.check_constraint #{check_constraint.expression.inspect}"
      ]

      if check_constraint.export_name_on_schema_dump?
        parts << "name: #{check_constraint.name.inspect}"
      end

      parts << "validate: #{check_constraint.validate?.inspect}" unless check_constraint.validate?

      "    #{parts.join(', ')}"
    end

    stream.puts add_check_constraint_statements.sort.join("\n")
  end
end

#define_params (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 89

def define_params
  @version ? "version: #{formatted_version}" : ""
end

#dump(stream)

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 58

def dump(stream)
  header(stream)
  schemas(stream)
  extensions(stream)
  types(stream)
  tables(stream)
  trailer(stream)
  stream
end

#extensions(stream) (private)

extensions are only supported by PostgreSQL

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 116

def extensions(stream)
end

#foreign_keys(table, stream) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 283

def foreign_keys(table, stream)
  if (foreign_keys = @connection.foreign_keys(table)).any?
    add_foreign_key_statements = foreign_keys.map do |foreign_key|
      parts = [
        "add_foreign_key #{remove_prefix_and_suffix(foreign_key.from_table).inspect}",
        remove_prefix_and_suffix(foreign_key.to_table).inspect,
      ]

      if foreign_key.column != @connection.foreign_key_column_for(foreign_key.to_table, "id")
        parts << "column: #{foreign_key.column.inspect}"
      end

      if foreign_key.custom_primary_key?
        parts << "primary_key: #{foreign_key.primary_key.inspect}"
      end

      if foreign_key.export_name_on_schema_dump?
        parts << "name: #{foreign_key.name.inspect}"
      end

      parts << "on_update: #{foreign_key.on_update.inspect}" if foreign_key.on_update
      parts << "on_delete: #{foreign_key.on_delete.inspect}" if foreign_key.on_delete
      parts << "deferrable: #{foreign_key.deferrable.inspect}" if foreign_key.deferrable
      parts << "validate: #{foreign_key.validate?.inspect}" unless foreign_key.validate?

      "  #{parts.join(', ')}"
    end

    stream.puts add_foreign_key_statements.sort.join("\n")
  end
end

#format_colspec(colspec) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 315

def format_colspec(colspec)
  colspec.map do |key, value|
    "#{key}: #{ value.is_a?(Hash) ? "{ #{format_colspec(value)} }" : value }"
  end.join(", ")
end

#format_index_parts(options) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 325

def format_index_parts(options)
  if options.is_a?(Hash)
    "{ #{format_options(options)} }"
  else
    options.inspect
  end
end

#format_options(options) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 321

def format_options(options)
  options.map { |key, value| "#{key}: #{value.inspect}" }.join(", ")
end

#formatted_version (private)

turns 20170404131909 into “2017_04_04_131909”

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 83

def formatted_version
  stringified = @version.to_s
  return stringified unless stringified.length == 14
  stringified.insert(4, "_").insert(7, "_").insert(10, "_")
end

#header(stream) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 93

def header(stream)
  stream.puts <<~HEADER
    # This file is auto-generated from the current state of the database. Instead
    # of editing this file, please use the migrations feature of Active Record to
    # incrementally modify your database, and then regenerate this schema definition.
    #
    # This file is the source Rails uses to define your schema when running `bin/rails
    # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
    # be faster and is potentially less error prone than running all of your
    # migrations from scratch. Old migrations may fail to apply correctly if those
    # migrations use external dependencies or application code.
    #
    # It's strongly recommended that you check this file into your version control system.

    ActiveRecord::Schema[#{ActiveRecord::Migration.current_version}].define(#{define_params}) do
  HEADER
end

#ignored?(table_name) ⇒ Boolean (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 343

def ignored?(table_name)
  @ignore_tables.any? do |ignored|
    ignored === remove_prefix_and_suffix(table_name)
  end
end

#index_parts(index) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 245

def index_parts(index)
  index_parts = [
    index.columns.inspect,
    "name: #{index.name.inspect}",
  ]
  index_parts << "unique: true" if index.unique
  index_parts << "length: #{format_index_parts(index.lengths)}" if index.lengths.present?
  index_parts << "order: #{format_index_parts(index.orders)}" if index.orders.present?
  index_parts << "opclass: #{format_index_parts(index.opclasses)}" if index.opclasses.present?
  index_parts << "where: #{index.where.inspect}" if index.where
  index_parts << "using: #{index.using.inspect}" if !@connection.default_index_type?(index)
  index_parts << "include: #{index.include.inspect}" if index.include
  index_parts << "nulls_not_distinct: #{index.nulls_not_distinct.inspect}" if index.nulls_not_distinct
  index_parts << "type: #{index.type.inspect}" if index.type
  index_parts << "comment: #{index.comment.inspect}" if index.comment
  index_parts
end

#indexes(table, stream) (private)

Keep it for indexing materialized views

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 212

def indexes(table, stream)
  if (indexes = @connection.indexes(table)).any?
    add_index_statements = indexes.map do |index|
      table_name = remove_prefix_and_suffix(index.table).inspect
      "  add_index #{([table_name] + index_parts(index)).join(', ')}"
    end

    stream.puts add_index_statements.sort.join("\n")
    stream.puts
  end
end

#indexes_in_create(table, stream) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 224

def indexes_in_create(table, stream)
  if (indexes = @connection.indexes(table)).any?
    if @connection.supports_exclusion_constraints? && (exclusion_constraints = @connection.exclusion_constraints(table)).any?
      exclusion_constraint_names = exclusion_constraints.collect(&:name)

      indexes = indexes.reject { |index| exclusion_constraint_names.include?(index.name) }
    end

    if @connection.supports_unique_constraints? && (unique_constraints = @connection.unique_constraints(table)).any?
      unique_constraint_names = unique_constraints.collect(&:name)

      indexes = indexes.reject { |index| unique_constraint_names.include?(index.name) }
    end

    index_statements = indexes.map do |index|
      "    t.index #{index_parts(index).join(', ')}"
    end
    stream.puts index_statements.sort.join("\n")
  end
end

#remove_prefix_and_suffix(table) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 333

def remove_prefix_and_suffix(table)
  # This method appears at the top when profiling active_record test cases run.
  # Avoid costly calculation when there are no prefix and suffix.
  return table if @options[:table_name_prefix].blank? && @options[:table_name_suffix].blank?

  prefix = Regexp.escape(@options[:table_name_prefix].to_s)
  suffix = Regexp.escape(@options[:table_name_suffix].to_s)
  table.sub(/\A#{prefix}(.+)#{suffix}\z/, "\\1")
end

#schemas(stream) (private)

schemas are only supported by PostgreSQL

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 124

def schemas(stream)
end

#table(table, stream) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 142

def table(table, stream)
  columns = @connection.columns(table)
  begin
    self.table_name = table

    tbl = StringIO.new

    # first dump primary key column
    pk = @connection.primary_key(table)

    tbl.print "  create_table #{remove_prefix_and_suffix(table).inspect}"

    case pk
    when String
      tbl.print ", primary_key: #{pk.inspect}" unless pk == "id"
      pkcol = columns.detect { |c| c.name == pk }
      pkcolspec = column_spec_for_primary_key(pkcol)
      unless pkcolspec.empty?
        if pkcolspec != pkcolspec.slice(:id, :default)
          pkcolspec = { id: { type: pkcolspec.delete(:id), **pkcolspec }.compact }
        end
        tbl.print ", #{format_colspec(pkcolspec)}"
      end
    when Array
      tbl.print ", primary_key: #{pk.inspect}"
    else
      tbl.print ", id: false"
    end

    table_options = @connection.table_options(table)
    if table_options.present?
      tbl.print ", #{format_options(table_options)}"
    end

    tbl.puts ", force: :cascade do |t|"

    # then dump all non-primary key columns
    columns.each do |column|
      raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" unless @connection.valid_type?(column.type)
      next if column.name == pk

      type, colspec = column_spec(column)
      if type.is_a?(Symbol)
        tbl.print "    t.#{type} #{column.name.inspect}"
      else
        tbl.print "    t.column #{column.name.inspect}, #{type.inspect}"
      end
      tbl.print ", #{format_colspec(colspec)}" if colspec.present?
      tbl.puts
    end

    indexes_in_create(table, tbl)
    check_constraints_in_create(table, tbl) if @connection.supports_check_constraints?
    exclusion_constraints_in_create(table, tbl) if @connection.supports_exclusion_constraints?
    unique_constraints_in_create(table, tbl) if @connection.supports_unique_constraints?

    tbl.puts "  end"
    tbl.puts

    stream.print tbl.string
  rescue => e
    stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
    stream.puts "#   #{e.message}"
    stream.puts
  ensure
    self.table_name = nil
  end
end

#tables(stream) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 127

def tables(stream)
  sorted_tables = @connection.tables.sort

  sorted_tables.each do |table_name|
    table(table_name, stream) unless ignored?(table_name)
  end

  # dump foreign keys at the end to make sure all dependent tables exist.
  if @connection.use_foreign_keys?
    sorted_tables.each do |tbl|
      foreign_keys(tbl, stream) unless ignored?(tbl)
    end
  end
end

#trailer(stream) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 111

def trailer(stream)
  stream.puts "end"
end

#types(stream) (private)

(enum) types are only supported by PostgreSQL

[ GitHub ]

  
# File 'activerecord/lib/active_record/schema_dumper.rb', line 120

def types(stream)
end