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 74

def initialize(connection, options = {})
  @connection = connection
  @version = connection.pool.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(pool = ActiveRecord::Base.connection_pool, stream = $stdout, config = ActiveRecord::Base)

[ GitHub ]

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

def dump(pool = ActiveRecord::Base.connection_pool, stream = $stdout, config = ActiveRecord::Base)
  pool.with_connection do |connection|
    connection.create_schema_dumper(generate_options(config)).dump(stream)
  end
  stream
end

.generate_options(config) (private)

[ GitHub ]

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

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 72

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 278

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 92

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

#dump(stream)

[ GitHub ]

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

def dump(stream)
  header(stream)
  schemas(stream)
  extensions(stream)
  types(stream)
  tables(stream)
  virtual_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 119

def extensions(stream)
end

#foreign_keys(table, stream) (private)

[ GitHub ]

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

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 330

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 340

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 336

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 86

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 96

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 358

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 260

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 227

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 239

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 348

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 127

def schemas(stream)
end

#table(table, stream) (private)

[ GitHub ]

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

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"

    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 134

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

  not_ignored_tables = sorted_tables.reject { |table_name| ignored?(table_name) }

  not_ignored_tables.each_with_index do |table_name, index|
    table(table_name, stream)
    stream.puts if index < not_ignored_tables.count - 1
  end

  # dump foreign keys at the end to make sure all dependent tables exist.
  if @connection.supports_foreign_keys?
    foreign_keys_stream = StringIO.new
    not_ignored_tables.each do |tbl|
      foreign_keys(tbl, foreign_keys_stream)
    end

    foreign_keys_string = foreign_keys_stream.string
    stream.puts if foreign_keys_string.length > 0

    stream.print foreign_keys_string
  end
end

#trailer(stream) (private)

[ GitHub ]

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

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 123

def types(stream)
end

#virtual_tables(stream) (private)

virtual tables are only supported by SQLite

[ GitHub ]

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

def virtual_tables(stream)
end