require'stringio'require'active_support/core_ext/big_decimal'moduleActiveRecord# = Active Record Schema Dumper## This class is used to dump the database schema for some connection to some# output format (i.e., ActiveRecord::Schema).classSchemaDumper#:nodoc:private_class_method:new### :singleton-method:# A list of tables which should not be dumped to the schema.# Acceptable values are strings as well as regexp.# This setting is only used if ActiveRecord::Base.schema_format == :rubycattr_accessor:ignore_tables@@ignore_tables=[]class<<selfdefdump(connection=ActiveRecord::Base.connection,stream=STDOUT,config=ActiveRecord::Base)new(connection,generate_options(config)).dump(stream)streamendprivatedefgenerate_options(config){table_name_prefix: config.table_name_prefix,table_name_suffix: config.table_name_suffix}endenddefdump(stream)header(stream)extensions(stream)tables(stream)trailer(stream)streamendprivatedefinitialize(connection,options={})@connection=connection@types=@connection.native_database_types@version=Migrator::current_versionrescuenil@options=optionsenddefheader(stream)define_params=@version?"version: #{@version}":""ifstream.respond_to?(:external_encoding)&&stream.external_encodingstream.puts"# encoding: #{stream.external_encoding.name}"endstream.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.
#
# Note that this schema.rb definition is the authoritative source for your
# database schema. If you need to create the application database on another
# system, you should be using db:schema:load, not running all the migrations
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(#{define_params}) do
HEADERenddeftrailer(stream)stream.puts"end"enddefextensions(stream)returnunless@connection.supports_extensions?extensions=@connection.extensionsifextensions.any?stream.puts" # These are extensions that must be enabled in order to support this database"extensions.eachdo|extension|stream.puts" enable_extension #{extension.inspect}"endstream.putsendenddeftables(stream)sorted_tables=@connection.tables.sortsorted_tables.eachdo|table_name|table(table_name,stream)unlessignored?(table_name)end# dump foreign keys at the end to make sure all dependent tables exist.if@connection.supports_foreign_keys?sorted_tables.eachdo|tbl|foreign_keys(tbl,stream)unlessignored?(tbl)endendenddeftable(table,stream)columns=@connection.columns(table)begintbl=StringIO.new# first dump primary key columnpk=@connection.primary_key(table)tbl.print" create_table #{remove_prefix_and_suffix(table).inspect}"pkcol=columns.detect{|c|c.name==pk}ifpkcolifpk!='id'tbl.print%Q(, primary_key: "#{pk}")elsifpkcol.sql_type=='bigint'tbl.print", id: :bigserial"elsifpkcol.sql_type=='uuid'tbl.print", id: :uuid"tbl.print%Q(, default: "#{pkcol.default_function}")ifpkcol.default_functionendelsetbl.print", id: false"endtbl.print", force: true"tbl.puts" do |t|"# then dump all non-primary key columnscolumn_specs=columns.mapdo|column|raiseStandardError,"Unknown type '#{column.sql_type}' for column '#{column.name}'"unless@connection.valid_type?(column.type)nextifcolumn.name==pk@connection.column_spec(column,@types)end.compact# find all migration keys used in this tablekeys=@connection.migration_keys# figure out the lengths for each column based on above keyslengths=keys.map{|key|column_specs.map{|spec|spec[key]?spec[key].length+2:0}.max}# the string we're going to sprintf our values against, with standardized column widthsformat_string=lengths.map{|len|"%-#{len}s"}# find the max length for the 'type' column, which is specialtype_length=column_specs.map{|column|column[:type].length}.max# add column type definition to our format stringformat_string.unshift" t.%-#{type_length}s "format_string*=''column_specs.eachdo|colspec|values=keys.zip(lengths).map{|key,len|colspec.key?(key)?colspec[key]+", ":" "*len}values.unshiftcolspec[:type]tbl.print((format_string%values).gsub(/,\s*$/,''))tbl.putsendtbl.puts" end"tbl.putsindexes(table,tbl)tbl.rewindstream.printtbl.readrescue=>estream.puts"# Could not dump table #{table.inspect} because of following #{e.class}"stream.puts"# #{e.message}"stream.putsendstreamenddefindexes(table,stream)if(indexes=@connection.indexes(table)).any?add_index_statements=indexes.mapdo|index|statement_parts=["add_index #{remove_prefix_and_suffix(index.table).inspect}",index.columns.inspect,"name: #{index.name.inspect}",]statement_parts<<'unique: true'ifindex.uniqueindex_lengths=(index.lengths||[]).compactstatement_parts<<"length: #{Hash[index.columns.zip(index.lengths)].inspect}"ifindex_lengths.any?index_orders=index.orders||{}statement_parts<<"order: #{index.orders.inspect}"ifindex_orders.any?statement_parts<<"where: #{index.where.inspect}"ifindex.wherestatement_parts<<"using: #{index.using.inspect}"ifindex.usingstatement_parts<<"type: #{index.type.inspect}"ifindex.type" #{statement_parts.join(', ')}"endstream.putsadd_index_statements.sort.join("\n")stream.putsendenddefforeign_keys(table,stream)if(foreign_keys=@connection.foreign_keys(table)).any?add_foreign_key_statements=foreign_keys.mapdo|foreign_key|parts=["add_foreign_key #{remove_prefix_and_suffix(foreign_key.from_table).inspect}",remove_prefix_and_suffix(foreign_key.to_table).inspect,]ifforeign_key.column!=@connection.foreign_key_column_for(foreign_key.to_table)parts<<"column: #{foreign_key.column.inspect}"endifforeign_key.custom_primary_key?parts<<"primary_key: #{foreign_key.primary_key.inspect}"endifforeign_key.name!~/^fk_rails_[0-9a-f]{10}$/parts<<"name: #{foreign_key.name.inspect}"endparts<<"on_update: #{foreign_key.on_update.inspect}"ifforeign_key.on_updateparts<<"on_delete: #{foreign_key.on_delete.inspect}"ifforeign_key.on_delete" #{parts.join(', ')}"endstream.putsadd_foreign_key_statements.sort.join("\n")endenddefremove_prefix_and_suffix(table)table.gsub(/^(#{@options[:table_name_prefix]})(.+)(#{@options[:table_name_suffix]})$/,"\\2")enddefignored?(table_name)['schema_migrations',ignore_tables].flatten.any?do|ignored|ignored===remove_prefix_and_suffix(table_name)endendendend