Class: DynamicMigrations::Postgres::Generator
- Inherits:
-
Object
- Object
- DynamicMigrations::Postgres::Generator
- Includes:
- Column, Enum, Extension, ForeignKeyConstraint, Function, Index, PrimaryKey, Schema, Table, Trigger, UniqueConstraint, Validation
- Defined in:
- lib/dynamic_migrations/postgres/generator.rb,
lib/dynamic_migrations/postgres/generator/enum.rb,
lib/dynamic_migrations/postgres/generator/index.rb,
lib/dynamic_migrations/postgres/generator/table.rb,
lib/dynamic_migrations/postgres/generator/column.rb,
lib/dynamic_migrations/postgres/generator/schema.rb,
lib/dynamic_migrations/postgres/generator/trigger.rb,
lib/dynamic_migrations/postgres/generator/fragment.rb,
lib/dynamic_migrations/postgres/generator/function.rb,
lib/dynamic_migrations/postgres/generator/extension.rb,
lib/dynamic_migrations/postgres/generator/migration.rb,
lib/dynamic_migrations/postgres/generator/validation.rb,
lib/dynamic_migrations/postgres/generator/primary_key.rb,
lib/dynamic_migrations/postgres/generator/table_migration.rb,
lib/dynamic_migrations/postgres/generator/schema_migration.rb,
lib/dynamic_migrations/postgres/generator/unique_constraint.rb,
lib/dynamic_migrations/postgres/generator/database_migration.rb,
lib/dynamic_migrations/postgres/generator/trigger_template_base.rb,
lib/dynamic_migrations/postgres/generator/foreign_key_constraint.rb,
lib/dynamic_migrations/postgres/generator/validation_template_base.rb,
lib/dynamic_migrations/postgres/generator/migration_dependency_sorter.rb
Defined Under Namespace
Modules: Column, Enum, Extension, ForeignKeyConstraint, Function, Index, PrimaryKey, Schema, Table, Trigger, UniqueConstraint, Validation Classes: DatabaseMigration, DeferrableOptionsError, ExpectedSymbolError, Fragment, Migration, MigrationDependencySorter, MissingDescriptionError, NoDifferenceError, SchemaMigration, TableMigration, TableMigrationNotFound, TriggerTemplateBase, UnprocessableFragmentError, ValidationTemplateBase
Instance Method Summary collapse
-
#initialize ⇒ Generator
constructor
A new instance of Generator.
-
#migrations ⇒ Object
builds the final migrations.
Methods included from Extension
#disable_extension, #enable_extension
Methods included from Enum
#create_enum, #drop_enum, #optional_enum_table, #remove_enum_comment, #set_enum_comment, #update_enum
Methods included from Trigger
add_template, #add_trigger, has_template?, #recreate_trigger, #remove_trigger, #remove_trigger_comment, #set_trigger_comment, template
Methods included from Function
#create_function, #drop_function, #optional_function_table, #remove_function_comment, #set_function_comment, #update_function
Methods included from Validation
add_template, #add_validation, has_template?, #recreate_validation, #remove_validation, #remove_validation_comment, #set_validation_comment, template
Methods included from UniqueConstraint
#add_unique_constraint, #recreate_unique_constraint, #remove_unique_constraint, #remove_unique_constraint_comment, #set_unique_constraint_comment
Methods included from PrimaryKey
#add_primary_key, #recreate_primary_key, #remove_primary_key, #remove_primary_key_comment, #set_primary_key_comment
Methods included from Index
#add_index, #recreate_index, #remove_index, #remove_index_comment, #set_index_comment
Methods included from ForeignKeyConstraint
#add_foreign_key_constraint, #recreate_foreign_key_constraint, #remove_foreign_key_constraint, #remove_foreign_key_constraint_comment, #set_foreign_key_constraint_comment
Methods included from Column
#add_column, #change_column, #remove_column, #remove_column_comment, #set_column_comment
Methods included from Table
#create_table, #drop_table, #remove_table_comment, #set_table_comment
Methods included from Schema
Constructor Details
#initialize ⇒ Generator
Returns a new instance of Generator.
35 36 37 38 |
# File 'lib/dynamic_migrations/postgres/generator.rb', line 35 def initialize @fragments = [] @logger = Logging.logger[self] end |
Instance Method Details
#migrations ⇒ Object
builds the final migrations
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
# File 'lib/dynamic_migrations/postgres/generator.rb', line 41 def migrations log.info "Generating migrations" # a hash to hold the generated migrations orgnized by their schema and table # this makes it easier and faster to work with them within this method database_migrations = {} # the database_specific_migrations are for migrations which dont belong # within a specific schema, they are important enough that we have a # dedicated migration for each one database_specific_migrations = [] # Process each fragment, and organize them into migrations. We create a shared # Migration for each table, and a single shared migration for any schema migrations # which do not relate to a table. log.info " Organizing migration fragments" @fragments.each do |fragment| # The first time this schema is encountered we create an object to hold the migrations # and organize the different migrations. schema_migrations = database_migrations[fragment.schema_name] ||= { schema_migration: nil, table_migrations: {}, # this array will hold any migrations which were created by splitting apart table # migrations to resolve circular dependencies additional_migrations: [] } schema_name = fragment.schema_name table_name = fragment.table_name # If we have a table name, then add the migration fragment to a # TableMigration which holds all of the migrations for this table if table_name && schema_name table_migration = schema_migrations[:table_migrations][table_name] ||= TableMigration.new(schema_name, table_name) table_migration.add_fragment fragment # migration fragments which do have a schema, but do not belong to a specific table are added # to a dedicated SchemaMigration object elsif schema_name && table_name.nil? schema_migration = schema_migrations[:schema_migration] ||= SchemaMigration.new(schema_name) schema_migration.add_fragment fragment # migrations with no schema or table, are added to a database # migration (these are really just creating/dropping schemas and extensions) elsif schema_name.nil? && table_name.nil? database_specific_migration = DatabaseMigration.new database_specific_migration.add_fragment(fragment) database_specific_migrations << database_specific_migration else raise UnprocessableFragmentError end end # Convert the hash of migrations into an array of migrations, this is # passed to the `circular_dependency?` method below, and any new migrations # required to resolve circular dependencies will be added to this array all_table_migrations = database_migrations.values.map { |m| m[:table_migrations].values }.flatten # For each migration, we recursively traverse the dependency graph to detect and handle circular # dependencies. # # Initially, all the fragments which pertain to a particular table are grouped together in # the same migration. If a circular dependency between migrations is detected, then we simply # pop the offending migration fragments out of the dedicated table migration and into a new # migration. This allows the migration to be processed later, and resolves the circular dependency. log.info " Resolving circular dependencies between migrations" completed_table_migrations = [] all_table_migrations.each do |table_migration| # skip it if it's already been processed next if completed_table_migrations.include? table_migration # recusrsively resolve the circular dependencies for this migration resolve_circular_dependencies table_migration, all_table_migrations, database_migrations, completed_table_migrations end # Prepare a dependency sorter, this is used to sort the migrations via rubys included Tsort module # The object used to sort the migrations is extended from a hash, and takes the form: # { # # every migration exists as a key, and its corresponding array is all the # # migrations which it depends on # migration1 => [migration2, migration3], # migration3 => [migration2] # } log.info " Preparing migrations for sorting" dependency_sorter = MigrationDependencySorter.new database_migrations.each do |schema_name, schema_migrations| if schema_migrations[:schema_migration] # the schema migration never has any dependencies dependency_sorter[schema_migrations[:schema_migration]] = [] end # add each table migration, and its dependencies schema_migrations[:table_migrations].values.each do |table_migration| deps = dependency_sorter[table_migration] = [] # if there is a schema migration, then it should always come first # so make the table migration depend on it deps << schema_migrations[:schema_migration] if schema_migrations[:schema_migration] # if the table migration has any dependencies on other tables, then add them table_migration.table_dependencies.each do |dependency| # find the migration which matches the dependency dependent_migration = database_migrations[dependency[:schema_name]] && database_migrations[dependency[:schema_name]][:table_migrations][dependency[:table_name]] # if the table migration is not found, then it's safe to assume the table was created # by an earlier set of migrations unless dependent_migration.nil? # add the dependent migration to the list of dependencies deps << dependent_migration end end # if the table migration has any dependencies on functions or enums, then add them (table_migration.function_dependencies + table_migration.enum_dependencies).each do |dependency| # functions are always added to a schema specific migration, if it does not exist then # we can assume the function was added in a previous set of migrations if (dependencies_schema_migration = database_migrations[dependency[:schema_name]] && database_migrations[dependency[:schema_name]][:schema_migration]) deps << dependencies_schema_migration end end end # add each additional migration, and its dependencies schema_migrations[:additional_migrations].each do |additional_migration| deps = dependency_sorter[additional_migration] = [] # if there is a schema migration, then it should always come first # so make the table migration depend on it deps << schema_migrations[:schema_migration] if schema_migrations[:schema_migration] # additional migrations are always dependent on the table migration which they came from table_migration = schema_migrations[:table_migrations][additional_migration.table_name] # if the table migration is not found, then it's safe to assume the table was created # by an earlier set of migrations unless table_migration.nil? deps << table_migration # if the table migration has any dependencies on functions or enums, then add them (table_migration.function_dependencies + table_migration.enum_dependencies).each do |dependency| # functions are always added to a schema specific migration, if it does not exist then # we can assume the function was added in a previous set of migrations if (dependencies_schema_migration = database_migrations[dependency[:schema_name]] && database_migrations[dependency[:schema_name]][:schema_migration]) deps << dependencies_schema_migration end end end # if the additional_migration has any dependencies on other tables, then add them too additional_migration.table_dependencies.each do |dependency| # find the table migration which matches the dependency dependent_migration = database_migrations[dependency[:schema_name]] && database_migrations[dependency[:schema_name]][:table_migrations][dependency[:table_name]] # if the table migration is not found, then it's safe to assume the table was created # by an earlier set of migrations unless dependent_migration.nil? deps << dependent_migration end end end end # sort the migrations so that they are executed in the correct order # the order is determined by their dependencies log.info " Sorting migrations based on their dependencies" final_migrations = dependency_sorter.tsort # if any database only migrations exist, then add them to the front of the array here if database_specific_migrations.any? final_migrations = database_specific_migrations + final_migrations end # return the final migrations in the expected format final_migrations.map do |migration| { schema_name: migration.schema_name, name: migration.name, content: migration.content } end end |