activerecord 4.2.0 → 6.1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +1221 -796
- data/MIT-LICENSE +4 -2
- data/README.rdoc +15 -14
- data/examples/performance.rb +33 -32
- data/examples/simple.rb +5 -4
- data/lib/active_record/aggregations.rb +267 -249
- data/lib/active_record/association_relation.rb +45 -7
- data/lib/active_record/associations/alias_tracker.rb +40 -43
- data/lib/active_record/associations/association.rb +172 -67
- data/lib/active_record/associations/association_scope.rb +105 -129
- data/lib/active_record/associations/belongs_to_association.rb +85 -59
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -12
- data/lib/active_record/associations/builder/association.rb +57 -43
- data/lib/active_record/associations/builder/belongs_to.rb +74 -57
- data/lib/active_record/associations/builder/collection_association.rb +15 -33
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +57 -70
- data/lib/active_record/associations/builder/has_many.rb +13 -5
- data/lib/active_record/associations/builder/has_one.rb +44 -6
- data/lib/active_record/associations/builder/singular_association.rb +16 -10
- data/lib/active_record/associations/collection_association.rb +168 -279
- data/lib/active_record/associations/collection_proxy.rb +263 -155
- data/lib/active_record/associations/foreign_association.rb +33 -0
- data/lib/active_record/associations/has_many_association.rb +57 -84
- data/lib/active_record/associations/has_many_through_association.rb +70 -82
- data/lib/active_record/associations/has_one_association.rb +74 -47
- data/lib/active_record/associations/has_one_through_association.rb +20 -11
- data/lib/active_record/associations/join_dependency/join_association.rb +54 -73
- data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +14 -14
- data/lib/active_record/associations/join_dependency.rb +175 -164
- data/lib/active_record/associations/preloader/association.rb +107 -112
- data/lib/active_record/associations/preloader/through_association.rb +85 -65
- data/lib/active_record/associations/preloader.rb +99 -96
- data/lib/active_record/associations/singular_association.rb +18 -45
- data/lib/active_record/associations/through_association.rb +49 -24
- data/lib/active_record/associations.rb +1845 -1597
- data/lib/active_record/attribute_assignment.rb +59 -185
- data/lib/active_record/attribute_methods/before_type_cast.rb +20 -7
- data/lib/active_record/attribute_methods/dirty.rb +168 -138
- data/lib/active_record/attribute_methods/primary_key.rb +93 -83
- data/lib/active_record/attribute_methods/query.rb +8 -10
- data/lib/active_record/attribute_methods/read.rb +19 -79
- data/lib/active_record/attribute_methods/serialization.rb +49 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +59 -36
- data/lib/active_record/attribute_methods/write.rb +25 -56
- data/lib/active_record/attribute_methods.rb +153 -162
- data/lib/active_record/attributes.rb +234 -70
- data/lib/active_record/autosave_association.rb +157 -69
- data/lib/active_record/base.rb +49 -50
- data/lib/active_record/callbacks.rb +234 -79
- data/lib/active_record/coders/json.rb +3 -1
- data/lib/active_record/coders/yaml_column.rb +46 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +887 -317
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -41
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +301 -113
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +78 -24
- data/lib/active_record/connection_adapters/abstract/quoting.rb +187 -60
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +9 -7
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +157 -93
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +485 -253
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +79 -36
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +909 -263
- data/lib/active_record/connection_adapters/abstract/transaction.rb +254 -92
- data/lib/active_record/connection_adapters/abstract_adapter.rb +492 -221
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +580 -608
- data/lib/active_record/connection_adapters/column.rb +67 -40
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +196 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +96 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +97 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +103 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +91 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +271 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +81 -199
- data/lib/active_record/connection_adapters/pool_config.rb +73 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +44 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +78 -161
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +49 -57
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +9 -8
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +5 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +8 -6
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +17 -13
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +6 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -20
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
- data/lib/active_record/connection_adapters/postgresql/oid/{infinity.rb → oid.rb} +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +32 -11
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +70 -34
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +67 -51
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +18 -4
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +25 -25
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +171 -48
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +80 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +178 -108
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +499 -293
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +11 -8
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +595 -382
- data/lib/active_record/connection_adapters/schema_cache.rb +191 -29
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +146 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +102 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +170 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +322 -389
- data/lib/active_record/connection_adapters/statement_pool.rb +33 -13
- data/lib/active_record/connection_adapters.rb +52 -0
- data/lib/active_record/connection_handling.rb +314 -41
- data/lib/active_record/core.rb +488 -243
- data/lib/active_record/counter_cache.rb +71 -50
- data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
- data/lib/active_record/database_configurations/database_config.rb +80 -0
- data/lib/active_record/database_configurations/hash_config.rb +96 -0
- data/lib/active_record/database_configurations/url_config.rb +53 -0
- data/lib/active_record/database_configurations.rb +273 -0
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/dynamic_matchers.rb +87 -106
- data/lib/active_record/enum.rb +212 -94
- data/lib/active_record/errors.rb +225 -54
- data/lib/active_record/explain.rb +27 -11
- data/lib/active_record/explain_registry.rb +4 -2
- data/lib/active_record/explain_subscriber.rb +11 -6
- data/lib/active_record/fixture_set/file.rb +33 -14
- data/lib/active_record/fixture_set/model_metadata.rb +32 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +152 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +273 -496
- data/lib/active_record/gem_version.rb +6 -4
- data/lib/active_record/inheritance.rb +175 -110
- data/lib/active_record/insert_all.rb +212 -0
- data/lib/active_record/integration.rb +121 -29
- data/lib/active_record/internal_metadata.rb +64 -0
- data/lib/active_record/legacy_yaml_adapter.rb +52 -0
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +103 -95
- data/lib/active_record/locking/pessimistic.rb +22 -6
- data/lib/active_record/log_subscriber.rb +93 -31
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +77 -0
- data/lib/active_record/migration/command_recorder.rb +185 -90
- data/lib/active_record/migration/compatibility.rb +298 -0
- data/lib/active_record/migration/join_table.rb +8 -7
- data/lib/active_record/migration.rb +685 -309
- data/lib/active_record/model_schema.rb +420 -113
- data/lib/active_record/nested_attributes.rb +265 -216
- data/lib/active_record/no_touching.rb +15 -2
- data/lib/active_record/null_relation.rb +24 -38
- data/lib/active_record/persistence.rb +574 -135
- data/lib/active_record/query_cache.rb +29 -23
- data/lib/active_record/querying.rb +50 -31
- data/lib/active_record/railtie.rb +175 -54
- data/lib/active_record/railties/console_sandbox.rb +3 -3
- data/lib/active_record/railties/controller_runtime.rb +34 -33
- data/lib/active_record/railties/databases.rake +533 -216
- data/lib/active_record/readonly_attributes.rb +9 -4
- data/lib/active_record/reflection.rb +485 -310
- data/lib/active_record/relation/batches/batch_enumerator.rb +85 -0
- data/lib/active_record/relation/batches.rb +217 -59
- data/lib/active_record/relation/calculations.rb +326 -244
- data/lib/active_record/relation/delegation.rb +76 -84
- data/lib/active_record/relation/finder_methods.rb +318 -256
- data/lib/active_record/relation/from_clause.rb +30 -0
- data/lib/active_record/relation/merger.rb +99 -84
- data/lib/active_record/relation/predicate_builder/array_handler.rb +26 -25
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +57 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
- data/lib/active_record/relation/predicate_builder.rb +139 -96
- data/lib/active_record/relation/query_attribute.rb +50 -0
- data/lib/active_record/relation/query_methods.rb +757 -409
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +23 -21
- data/lib/active_record/relation/where_clause.rb +239 -0
- data/lib/active_record/relation.rb +554 -342
- data/lib/active_record/result.rb +91 -47
- data/lib/active_record/runtime_registry.rb +6 -4
- data/lib/active_record/sanitization.rb +134 -122
- data/lib/active_record/schema.rb +21 -24
- data/lib/active_record/schema_dumper.rb +141 -92
- data/lib/active_record/schema_migration.rb +24 -26
- data/lib/active_record/scoping/default.rb +96 -82
- data/lib/active_record/scoping/named.rb +78 -36
- data/lib/active_record/scoping.rb +45 -27
- data/lib/active_record/secure_token.rb +48 -0
- data/lib/active_record/serialization.rb +8 -6
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +89 -36
- data/lib/active_record/store.rb +133 -43
- data/lib/active_record/suppressor.rb +61 -0
- data/lib/active_record/table_metadata.rb +81 -0
- data/lib/active_record/tasks/database_tasks.rb +366 -129
- data/lib/active_record/tasks/mysql_database_tasks.rb +68 -100
- data/lib/active_record/tasks/postgresql_database_tasks.rb +87 -39
- data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -19
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +291 -0
- data/lib/active_record/timestamp.rb +86 -43
- data/lib/active_record/touch_later.rb +65 -0
- data/lib/active_record/transactions.rb +181 -152
- data/lib/active_record/translation.rb +3 -1
- data/lib/active_record/type/adapter_specific_registry.rb +126 -0
- data/lib/active_record/type/date.rb +4 -41
- data/lib/active_record/type/date_time.rb +4 -38
- data/lib/active_record/type/decimal_without_scale.rb +6 -2
- data/lib/active_record/type/hash_lookup_type_map.rb +12 -5
- data/lib/active_record/type/internal/timezone.rb +17 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +33 -15
- data/lib/active_record/type/text.rb +2 -2
- data/lib/active_record/type/time.rb +21 -16
- data/lib/active_record/type/type_map.rb +16 -19
- data/lib/active_record/type/unsigned_integer.rb +9 -8
- data/lib/active_record/type.rb +84 -23
- data/lib/active_record/type_caster/connection.rb +33 -0
- data/lib/active_record/type_caster/map.rb +23 -0
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +12 -4
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/presence.rb +14 -13
- data/lib/active_record/validations/uniqueness.rb +65 -48
- data/lib/active_record/validations.rb +39 -35
- data/lib/active_record/version.rb +3 -1
- data/lib/active_record.rb +44 -28
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +41 -0
- data/lib/arel/collectors/bind.rb +29 -0
- data/lib/arel/collectors/composite.rb +39 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +27 -0
- data/lib/arel/collectors/substitute_binds.rb +35 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +126 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +62 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +15 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +11 -0
- data/lib/arel/nodes/homogeneous_in.rb +76 -0
- data/lib/arel/nodes/in.rb +15 -0
- data/lib/arel/nodes/infix_operation.rb +92 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +51 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +19 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +31 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +44 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/nodes.rb +70 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +250 -0
- data/lib/arel/select_manager.rb +270 -0
- data/lib/arel/table.rb +118 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors/dot.rb +308 -0
- data/lib/arel/visitors/mysql.rb +93 -0
- data/lib/arel/visitors/postgresql.rb +120 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +899 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/visitors.rb +13 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +54 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -37
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +26 -0
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +13 -10
- data/lib/rails/generators/active_record/migration.rb +35 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +55 -22
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +22 -0
- data/lib/rails/generators/active_record.rb +7 -5
- metadata +175 -65
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
- data/lib/active_record/associations/preloader/collection_association.rb +0 -24
- data/lib/active_record/associations/preloader/has_many.rb +0 -17
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -23
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -21
- data/lib/active_record/attribute.rb +0 -149
- data/lib/active_record/attribute_decorators.rb +0 -66
- data/lib/active_record/attribute_set/builder.rb +0 -86
- data/lib/active_record/attribute_set.rb +0 -77
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -275
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -30
- data/lib/active_record/type/decimal.rb +0 -40
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -55
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -36
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/value.rb +0 -101
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +0 -22
- data/lib/rails/generators/active_record/model/templates/model.rb +0 -10
- /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -1,52 +1,221 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "benchmark"
|
4
|
+
require "set"
|
5
|
+
require "zlib"
|
6
|
+
require "active_support/core_ext/array/access"
|
7
|
+
require "active_support/core_ext/enumerable"
|
1
8
|
require "active_support/core_ext/module/attribute_accessors"
|
2
|
-
require
|
9
|
+
require "active_support/actionable_error"
|
3
10
|
|
4
11
|
module ActiveRecord
|
5
|
-
class MigrationError < ActiveRecordError#:nodoc:
|
12
|
+
class MigrationError < ActiveRecordError #:nodoc:
|
6
13
|
def initialize(message = nil)
|
7
14
|
message = "\n\n#{message}\n\n" if message
|
8
15
|
super
|
9
16
|
end
|
10
17
|
end
|
11
18
|
|
12
|
-
# Exception that can be raised to stop migrations from
|
19
|
+
# Exception that can be raised to stop migrations from being rolled back.
|
20
|
+
# For example the following migration is not reversible.
|
21
|
+
# Rolling back this migration will raise an ActiveRecord::IrreversibleMigration error.
|
22
|
+
#
|
23
|
+
# class IrreversibleMigrationExample < ActiveRecord::Migration[6.0]
|
24
|
+
# def change
|
25
|
+
# create_table :distributors do |t|
|
26
|
+
# t.string :zipcode
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# execute <<~SQL
|
30
|
+
# ALTER TABLE distributors
|
31
|
+
# ADD CONSTRAINT zipchk
|
32
|
+
# CHECK (char_length(zipcode) = 5) NO INHERIT;
|
33
|
+
# SQL
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# There are two ways to mitigate this problem.
|
38
|
+
#
|
39
|
+
# 1. Define <tt>#up</tt> and <tt>#down</tt> methods instead of <tt>#change</tt>:
|
40
|
+
#
|
41
|
+
# class ReversibleMigrationExample < ActiveRecord::Migration[6.0]
|
42
|
+
# def up
|
43
|
+
# create_table :distributors do |t|
|
44
|
+
# t.string :zipcode
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# execute <<~SQL
|
48
|
+
# ALTER TABLE distributors
|
49
|
+
# ADD CONSTRAINT zipchk
|
50
|
+
# CHECK (char_length(zipcode) = 5) NO INHERIT;
|
51
|
+
# SQL
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# def down
|
55
|
+
# execute <<~SQL
|
56
|
+
# ALTER TABLE distributors
|
57
|
+
# DROP CONSTRAINT zipchk
|
58
|
+
# SQL
|
59
|
+
#
|
60
|
+
# drop_table :distributors
|
61
|
+
# end
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# 2. Use the #reversible method in <tt>#change</tt> method:
|
65
|
+
#
|
66
|
+
# class ReversibleMigrationExample < ActiveRecord::Migration[6.0]
|
67
|
+
# def change
|
68
|
+
# create_table :distributors do |t|
|
69
|
+
# t.string :zipcode
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
# reversible do |dir|
|
73
|
+
# dir.up do
|
74
|
+
# execute <<~SQL
|
75
|
+
# ALTER TABLE distributors
|
76
|
+
# ADD CONSTRAINT zipchk
|
77
|
+
# CHECK (char_length(zipcode) = 5) NO INHERIT;
|
78
|
+
# SQL
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# dir.down do
|
82
|
+
# execute <<~SQL
|
83
|
+
# ALTER TABLE distributors
|
84
|
+
# DROP CONSTRAINT zipchk
|
85
|
+
# SQL
|
86
|
+
# end
|
87
|
+
# end
|
88
|
+
# end
|
89
|
+
# end
|
13
90
|
class IrreversibleMigration < MigrationError
|
14
91
|
end
|
15
92
|
|
16
|
-
class DuplicateMigrationVersionError < MigrationError#:nodoc:
|
17
|
-
def initialize(version)
|
18
|
-
|
93
|
+
class DuplicateMigrationVersionError < MigrationError #:nodoc:
|
94
|
+
def initialize(version = nil)
|
95
|
+
if version
|
96
|
+
super("Multiple migrations have the version number #{version}.")
|
97
|
+
else
|
98
|
+
super("Duplicate migration version error.")
|
99
|
+
end
|
19
100
|
end
|
20
101
|
end
|
21
102
|
|
22
|
-
class DuplicateMigrationNameError < MigrationError#:nodoc:
|
23
|
-
def initialize(name)
|
24
|
-
|
103
|
+
class DuplicateMigrationNameError < MigrationError #:nodoc:
|
104
|
+
def initialize(name = nil)
|
105
|
+
if name
|
106
|
+
super("Multiple migrations have the name #{name}.")
|
107
|
+
else
|
108
|
+
super("Duplicate migration name.")
|
109
|
+
end
|
25
110
|
end
|
26
111
|
end
|
27
112
|
|
28
113
|
class UnknownMigrationVersionError < MigrationError #:nodoc:
|
29
|
-
def initialize(version)
|
30
|
-
|
114
|
+
def initialize(version = nil)
|
115
|
+
if version
|
116
|
+
super("No migration with version number #{version}.")
|
117
|
+
else
|
118
|
+
super("Unknown migration version.")
|
119
|
+
end
|
31
120
|
end
|
32
121
|
end
|
33
122
|
|
34
|
-
class IllegalMigrationNameError < MigrationError#:nodoc:
|
35
|
-
def initialize(name)
|
36
|
-
|
123
|
+
class IllegalMigrationNameError < MigrationError #:nodoc:
|
124
|
+
def initialize(name = nil)
|
125
|
+
if name
|
126
|
+
super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed).")
|
127
|
+
else
|
128
|
+
super("Illegal name for migration.")
|
129
|
+
end
|
37
130
|
end
|
38
131
|
end
|
39
132
|
|
40
|
-
class PendingMigrationError < MigrationError#:nodoc:
|
133
|
+
class PendingMigrationError < MigrationError #:nodoc:
|
134
|
+
include ActiveSupport::ActionableError
|
135
|
+
|
136
|
+
action "Run pending migrations" do
|
137
|
+
ActiveRecord::Tasks::DatabaseTasks.migrate
|
138
|
+
|
139
|
+
if ActiveRecord::Base.dump_schema_after_migration
|
140
|
+
ActiveRecord::Tasks::DatabaseTasks.dump_schema(
|
141
|
+
ActiveRecord::Base.connection_db_config
|
142
|
+
)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def initialize(message = nil)
|
147
|
+
super(message || detailed_migration_message)
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
def detailed_migration_message
|
152
|
+
message = "Migrations are pending. To resolve this issue, run:\n\n bin/rails db:migrate"
|
153
|
+
message += " RAILS_ENV=#{::Rails.env}" if defined?(Rails.env)
|
154
|
+
message += "\n\n"
|
155
|
+
|
156
|
+
pending_migrations = ActiveRecord::Base.connection.migration_context.open.pending_migrations
|
157
|
+
|
158
|
+
message += "You have #{pending_migrations.size} pending #{pending_migrations.size > 1 ? 'migrations:' : 'migration:'}\n\n"
|
159
|
+
|
160
|
+
pending_migrations.each do |pending_migration|
|
161
|
+
message += "#{pending_migration.basename}\n"
|
162
|
+
end
|
163
|
+
|
164
|
+
message
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
class ConcurrentMigrationError < MigrationError #:nodoc:
|
169
|
+
DEFAULT_MESSAGE = "Cannot run migrations because another migration process is currently running."
|
170
|
+
RELEASE_LOCK_FAILED_MESSAGE = "Failed to release advisory lock"
|
171
|
+
|
172
|
+
def initialize(message = DEFAULT_MESSAGE)
|
173
|
+
super
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
class NoEnvironmentInSchemaError < MigrationError #:nodoc:
|
41
178
|
def initialize
|
42
|
-
|
43
|
-
|
179
|
+
msg = "Environment data not found in the schema. To resolve this issue, run: \n\n bin/rails db:environment:set"
|
180
|
+
if defined?(Rails.env)
|
181
|
+
super("#{msg} RAILS_ENV=#{::Rails.env}")
|
182
|
+
else
|
183
|
+
super(msg)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
class ProtectedEnvironmentError < ActiveRecordError #:nodoc:
|
189
|
+
def initialize(env = "production")
|
190
|
+
msg = +"You are attempting to run a destructive action against your '#{env}' database.\n"
|
191
|
+
msg << "If you are sure you want to continue, run the same command with the environment variable:\n"
|
192
|
+
msg << "DISABLE_DATABASE_ENVIRONMENT_CHECK=1"
|
193
|
+
super(msg)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
class EnvironmentMismatchError < ActiveRecordError
|
198
|
+
def initialize(current: nil, stored: nil)
|
199
|
+
msg = +"You are attempting to modify a database that was last run in `#{ stored }` environment.\n"
|
200
|
+
msg << "You are running in `#{ current }` environment. "
|
201
|
+
msg << "If you are sure you want to continue, first set the environment using:\n\n"
|
202
|
+
msg << " bin/rails db:environment:set"
|
203
|
+
if defined?(Rails.env)
|
204
|
+
super("#{msg} RAILS_ENV=#{::Rails.env}\n\n")
|
44
205
|
else
|
45
|
-
super("
|
206
|
+
super("#{msg}\n\n")
|
46
207
|
end
|
47
208
|
end
|
48
209
|
end
|
49
210
|
|
211
|
+
class EnvironmentStorageError < ActiveRecordError # :nodoc:
|
212
|
+
def initialize
|
213
|
+
msg = +"You are attempting to store the environment in a database where metadata is disabled.\n"
|
214
|
+
msg << "Check your database configuration to see if this is intended."
|
215
|
+
super(msg)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
50
219
|
# = Active Record Migrations
|
51
220
|
#
|
52
221
|
# Migrations can manage the evolution of a schema used by several physical
|
@@ -59,7 +228,7 @@ module ActiveRecord
|
|
59
228
|
#
|
60
229
|
# Example of a simple migration:
|
61
230
|
#
|
62
|
-
# class AddSsl < ActiveRecord::Migration
|
231
|
+
# class AddSsl < ActiveRecord::Migration[6.0]
|
63
232
|
# def up
|
64
233
|
# add_column :accounts, :ssl_enabled, :boolean, default: true
|
65
234
|
# end
|
@@ -79,7 +248,7 @@ module ActiveRecord
|
|
79
248
|
#
|
80
249
|
# Example of a more complex migration that also needs to initialize data:
|
81
250
|
#
|
82
|
-
# class AddSystemSettings < ActiveRecord::Migration
|
251
|
+
# class AddSystemSettings < ActiveRecord::Migration[6.0]
|
83
252
|
# def up
|
84
253
|
# create_table :system_settings do |t|
|
85
254
|
# t.string :name
|
@@ -106,17 +275,18 @@ module ActiveRecord
|
|
106
275
|
#
|
107
276
|
# == Available transformations
|
108
277
|
#
|
278
|
+
# === Creation
|
279
|
+
#
|
280
|
+
# * <tt>create_join_table(table_1, table_2, options)</tt>: Creates a join
|
281
|
+
# table having its name as the lexical order of the first two
|
282
|
+
# arguments. See
|
283
|
+
# ActiveRecord::ConnectionAdapters::SchemaStatements#create_join_table for
|
284
|
+
# details.
|
109
285
|
# * <tt>create_table(name, options)</tt>: Creates a table called +name+ and
|
110
286
|
# makes the table object available to a block that can then add columns to it,
|
111
287
|
# following the same format as +add_column+. See example above. The options hash
|
112
288
|
# is for fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create
|
113
289
|
# table definition.
|
114
|
-
# * <tt>drop_table(name)</tt>: Drops the table called +name+.
|
115
|
-
# * <tt>change_table(name, options)</tt>: Allows to make column alterations to
|
116
|
-
# the table called +name+. It makes the table object available to a block that
|
117
|
-
# can then add/remove columns, indexes or foreign keys to it.
|
118
|
-
# * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+
|
119
|
-
# to +new_name+.
|
120
290
|
# * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column
|
121
291
|
# to the table called +table_name+
|
122
292
|
# named +column_name+ specified to be one of the following types:
|
@@ -127,21 +297,61 @@ module ActiveRecord
|
|
127
297
|
# Other options include <tt>:limit</tt> and <tt>:null</tt> (e.g.
|
128
298
|
# <tt>{ limit: 50, null: false }</tt>) -- see
|
129
299
|
# ActiveRecord::ConnectionAdapters::TableDefinition#column for details.
|
130
|
-
# * <tt>
|
131
|
-
#
|
132
|
-
#
|
133
|
-
# the column to a different type using the same parameters as add_column.
|
134
|
-
# * <tt>remove_column(table_name, column_name, type, options)</tt>: Removes the column
|
135
|
-
# named +column_name+ from the table called +table_name+.
|
300
|
+
# * <tt>add_foreign_key(from_table, to_table, options)</tt>: Adds a new
|
301
|
+
# foreign key. +from_table+ is the table with the key column, +to_table+ contains
|
302
|
+
# the referenced primary key.
|
136
303
|
# * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index
|
137
304
|
# with the name of the column. Other options include
|
138
305
|
# <tt>:name</tt>, <tt>:unique</tt> (e.g.
|
139
306
|
# <tt>{ name: 'users_name_index', unique: true }</tt>) and <tt>:order</tt>
|
140
307
|
# (e.g. <tt>{ order: { name: :desc } }</tt>).
|
141
|
-
# * <tt>
|
142
|
-
#
|
308
|
+
# * <tt>add_reference(:table_name, :reference_name)</tt>: Adds a new column
|
309
|
+
# +reference_name_id+ by default an integer. See
|
310
|
+
# ActiveRecord::ConnectionAdapters::SchemaStatements#add_reference for details.
|
311
|
+
# * <tt>add_timestamps(table_name, options)</tt>: Adds timestamps (+created_at+
|
312
|
+
# and +updated_at+) columns to +table_name+.
|
313
|
+
#
|
314
|
+
# === Modification
|
315
|
+
#
|
316
|
+
# * <tt>change_column(table_name, column_name, type, options)</tt>: Changes
|
317
|
+
# the column to a different type using the same parameters as add_column.
|
318
|
+
# * <tt>change_column_default(table_name, column_name, default_or_changes)</tt>:
|
319
|
+
# Sets a default value for +column_name+ defined by +default_or_changes+ on
|
320
|
+
# +table_name+. Passing a hash containing <tt>:from</tt> and <tt>:to</tt>
|
321
|
+
# as +default_or_changes+ will make this change reversible in the migration.
|
322
|
+
# * <tt>change_column_null(table_name, column_name, null, default = nil)</tt>:
|
323
|
+
# Sets or removes a <tt>NOT NULL</tt> constraint on +column_name+. The +null+ flag
|
324
|
+
# indicates whether the value can be +NULL+. See
|
325
|
+
# ActiveRecord::ConnectionAdapters::SchemaStatements#change_column_null for
|
326
|
+
# details.
|
327
|
+
# * <tt>change_table(name, options)</tt>: Allows to make column alterations to
|
328
|
+
# the table called +name+. It makes the table object available to a block that
|
329
|
+
# can then add/remove columns, indexes or foreign keys to it.
|
330
|
+
# * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames
|
331
|
+
# a column but keeps the type and content.
|
332
|
+
# * <tt>rename_index(table_name, old_name, new_name)</tt>: Renames an index.
|
333
|
+
# * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+
|
334
|
+
# to +new_name+.
|
335
|
+
#
|
336
|
+
# === Deletion
|
337
|
+
#
|
338
|
+
# * <tt>drop_table(name)</tt>: Drops the table called +name+.
|
339
|
+
# * <tt>drop_join_table(table_1, table_2, options)</tt>: Drops the join table
|
340
|
+
# specified by the given arguments.
|
341
|
+
# * <tt>remove_column(table_name, column_name, type, options)</tt>: Removes the column
|
342
|
+
# named +column_name+ from the table called +table_name+.
|
343
|
+
# * <tt>remove_columns(table_name, *column_names)</tt>: Removes the given
|
344
|
+
# columns from the table definition.
|
345
|
+
# * <tt>remove_foreign_key(from_table, to_table = nil, **options)</tt>: Removes the
|
346
|
+
# given foreign key from the table called +table_name+.
|
347
|
+
# * <tt>remove_index(table_name, column: column_names)</tt>: Removes the index
|
348
|
+
# specified by +column_names+.
|
143
349
|
# * <tt>remove_index(table_name, name: index_name)</tt>: Removes the index
|
144
350
|
# specified by +index_name+.
|
351
|
+
# * <tt>remove_reference(table_name, ref_name, options)</tt>: Removes the
|
352
|
+
# reference(s) on +table_name+ specified by +ref_name+.
|
353
|
+
# * <tt>remove_timestamps(table_name, options)</tt>: Removes the timestamp
|
354
|
+
# columns (+created_at+ and +updated_at+) from the table definition.
|
145
355
|
#
|
146
356
|
# == Irreversible transformations
|
147
357
|
#
|
@@ -154,7 +364,7 @@ module ActiveRecord
|
|
154
364
|
# The Rails package has several tools to help create and apply migrations.
|
155
365
|
#
|
156
366
|
# To generate a new migration, you can use
|
157
|
-
# rails generate migration MyNewMigration
|
367
|
+
# bin/rails generate migration MyNewMigration
|
158
368
|
#
|
159
369
|
# where MyNewMigration is the name of your migration. The generator will
|
160
370
|
# create an empty migration file <tt>timestamp_my_new_migration.rb</tt>
|
@@ -163,41 +373,36 @@ module ActiveRecord
|
|
163
373
|
#
|
164
374
|
# There is a special syntactic shortcut to generate migrations that add fields to a table.
|
165
375
|
#
|
166
|
-
# rails generate migration add_fieldname_to_tablename fieldname:string
|
376
|
+
# bin/rails generate migration add_fieldname_to_tablename fieldname:string
|
167
377
|
#
|
168
|
-
# This will generate the file <tt>timestamp_add_fieldname_to_tablename</tt>, which will look like this:
|
169
|
-
# class AddFieldnameToTablename < ActiveRecord::Migration
|
378
|
+
# This will generate the file <tt>timestamp_add_fieldname_to_tablename.rb</tt>, which will look like this:
|
379
|
+
# class AddFieldnameToTablename < ActiveRecord::Migration[6.0]
|
170
380
|
# def change
|
171
|
-
# add_column :tablenames, :
|
381
|
+
# add_column :tablenames, :fieldname, :string
|
172
382
|
# end
|
173
383
|
# end
|
174
384
|
#
|
175
385
|
# To run migrations against the currently configured database, use
|
176
|
-
# <tt>
|
386
|
+
# <tt>bin/rails db:migrate</tt>. This will update the database by running all of the
|
177
387
|
# pending migrations, creating the <tt>schema_migrations</tt> table
|
178
388
|
# (see "About the schema_migrations table" section below) if missing. It will also
|
179
|
-
# invoke the db:schema:dump
|
389
|
+
# invoke the db:schema:dump command, which will update your db/schema.rb file
|
180
390
|
# to match the structure of your database.
|
181
391
|
#
|
182
392
|
# To roll the database back to a previous migration version, use
|
183
|
-
# <tt>
|
393
|
+
# <tt>bin/rails db:rollback VERSION=X</tt> where <tt>X</tt> is the version to which
|
184
394
|
# you wish to downgrade. Alternatively, you can also use the STEP option if you
|
185
|
-
# wish to rollback last few migrations. <tt>
|
395
|
+
# wish to rollback last few migrations. <tt>bin/rails db:rollback STEP=2</tt> will rollback
|
186
396
|
# the latest two migrations.
|
187
397
|
#
|
188
398
|
# If any of the migrations throw an <tt>ActiveRecord::IrreversibleMigration</tt> exception,
|
189
399
|
# that step will fail and you'll have some manual work to do.
|
190
400
|
#
|
191
|
-
# == Database support
|
192
|
-
#
|
193
|
-
# Migrations are currently supported in MySQL, PostgreSQL, SQLite,
|
194
|
-
# SQL Server, and Oracle (all supported databases except DB2).
|
195
|
-
#
|
196
401
|
# == More examples
|
197
402
|
#
|
198
403
|
# Not all migrations change the schema. Some just fix the data:
|
199
404
|
#
|
200
|
-
# class RemoveEmptyTags < ActiveRecord::Migration
|
405
|
+
# class RemoveEmptyTags < ActiveRecord::Migration[6.0]
|
201
406
|
# def up
|
202
407
|
# Tag.all.each { |tag| tag.destroy if tag.pages.empty? }
|
203
408
|
# end
|
@@ -210,7 +415,7 @@ module ActiveRecord
|
|
210
415
|
#
|
211
416
|
# Others remove columns when they migrate up instead of down:
|
212
417
|
#
|
213
|
-
# class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration
|
418
|
+
# class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration[6.0]
|
214
419
|
# def up
|
215
420
|
# remove_column :items, :incomplete_items_count
|
216
421
|
# remove_column :items, :completed_items_count
|
@@ -224,7 +429,7 @@ module ActiveRecord
|
|
224
429
|
#
|
225
430
|
# And sometimes you need to do something in SQL not abstracted directly by migrations:
|
226
431
|
#
|
227
|
-
# class MakeJoinUnique < ActiveRecord::Migration
|
432
|
+
# class MakeJoinUnique < ActiveRecord::Migration[6.0]
|
228
433
|
# def up
|
229
434
|
# execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)"
|
230
435
|
# end
|
@@ -241,7 +446,7 @@ module ActiveRecord
|
|
241
446
|
# <tt>Base#reset_column_information</tt> in order to ensure that the model has the
|
242
447
|
# latest column data from after the new column was added. Example:
|
243
448
|
#
|
244
|
-
# class AddPeopleSalary < ActiveRecord::Migration
|
449
|
+
# class AddPeopleSalary < ActiveRecord::Migration[6.0]
|
245
450
|
# def up
|
246
451
|
# add_column :people, :salary, :integer
|
247
452
|
# Person.reset_column_information
|
@@ -275,21 +480,6 @@ module ActiveRecord
|
|
275
480
|
# The phrase "Updating salaries..." would then be printed, along with the
|
276
481
|
# benchmark for the block when the block completes.
|
277
482
|
#
|
278
|
-
# == About the schema_migrations table
|
279
|
-
#
|
280
|
-
# Rails versions 2.0 and prior used to create a table called
|
281
|
-
# <tt>schema_info</tt> when using migrations. This table contained the
|
282
|
-
# version of the schema as of the last applied migration.
|
283
|
-
#
|
284
|
-
# Starting with Rails 2.1, the <tt>schema_info</tt> table is
|
285
|
-
# (automatically) replaced by the <tt>schema_migrations</tt> table, which
|
286
|
-
# contains the version numbers of all the migrations applied.
|
287
|
-
#
|
288
|
-
# As a result, it is now possible to add migration files that are numbered
|
289
|
-
# lower than the current schema version: when migrating up, those
|
290
|
-
# never-applied "interleaved" migrations will be automatically applied, and
|
291
|
-
# when migrating down, never-applied "interleaved" migrations will be skipped.
|
292
|
-
#
|
293
483
|
# == Timestamped Migrations
|
294
484
|
#
|
295
485
|
# By default, Rails generates migrations that look like:
|
@@ -307,15 +497,14 @@ module ActiveRecord
|
|
307
497
|
#
|
308
498
|
# == Reversible Migrations
|
309
499
|
#
|
310
|
-
# Starting with Rails 3.1, you will be able to define reversible migrations.
|
311
500
|
# Reversible migrations are migrations that know how to go +down+ for you.
|
312
|
-
# You simply supply the +up+ logic, and the Migration system
|
501
|
+
# You simply supply the +up+ logic, and the Migration system figures out
|
313
502
|
# how to execute the down commands for you.
|
314
503
|
#
|
315
504
|
# To define a reversible migration, define the +change+ method in your
|
316
505
|
# migration like this:
|
317
506
|
#
|
318
|
-
# class TenderloveMigration < ActiveRecord::Migration
|
507
|
+
# class TenderloveMigration < ActiveRecord::Migration[6.0]
|
319
508
|
# def change
|
320
509
|
# create_table(:horses) do |t|
|
321
510
|
# t.column :content, :text
|
@@ -327,9 +516,9 @@ module ActiveRecord
|
|
327
516
|
# This migration will create the horses table for you on the way up, and
|
328
517
|
# automatically figure out how to drop the table on the way down.
|
329
518
|
#
|
330
|
-
# Some commands
|
331
|
-
#
|
332
|
-
#
|
519
|
+
# Some commands cannot be reversed. If you care to define how to move up
|
520
|
+
# and down in these cases, you should define the +up+ and +down+ methods
|
521
|
+
# as before.
|
333
522
|
#
|
334
523
|
# If a command cannot be reversed, an
|
335
524
|
# <tt>ActiveRecord::IrreversibleMigration</tt> exception will be raised when
|
@@ -345,7 +534,7 @@ module ActiveRecord
|
|
345
534
|
# can't execute inside a transaction though, and for these situations
|
346
535
|
# you can turn the automatic transactions off.
|
347
536
|
#
|
348
|
-
# class ChangeEnum < ActiveRecord::Migration
|
537
|
+
# class ChangeEnum < ActiveRecord::Migration[6.0]
|
349
538
|
# disable_ddl_transaction!
|
350
539
|
#
|
351
540
|
# def up
|
@@ -356,78 +545,134 @@ module ActiveRecord
|
|
356
545
|
# Remember that you can still open your own transactions, even if you
|
357
546
|
# are in a Migration with <tt>self.disable_ddl_transaction!</tt>.
|
358
547
|
class Migration
|
359
|
-
autoload :CommandRecorder,
|
548
|
+
autoload :CommandRecorder, "active_record/migration/command_recorder"
|
549
|
+
autoload :Compatibility, "active_record/migration/compatibility"
|
550
|
+
autoload :JoinTable, "active_record/migration/join_table"
|
551
|
+
|
552
|
+
# This must be defined before the inherited hook, below
|
553
|
+
class Current < Migration #:nodoc:
|
554
|
+
end
|
555
|
+
|
556
|
+
def self.inherited(subclass) #:nodoc:
|
557
|
+
super
|
558
|
+
if subclass.superclass == Migration
|
559
|
+
raise StandardError, "Directly inheriting from ActiveRecord::Migration is not supported. " \
|
560
|
+
"Please specify the Rails release the migration was written for:\n" \
|
561
|
+
"\n" \
|
562
|
+
" class #{subclass} < ActiveRecord::Migration[4.2]"
|
563
|
+
end
|
564
|
+
end
|
565
|
+
|
566
|
+
def self.[](version)
|
567
|
+
Compatibility.find(version)
|
568
|
+
end
|
569
|
+
|
570
|
+
def self.current_version
|
571
|
+
ActiveRecord::VERSION::STRING.to_f
|
572
|
+
end
|
360
573
|
|
574
|
+
MigrationFilenameRegexp = /\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/ #:nodoc:
|
361
575
|
|
362
576
|
# This class is used to verify that all migrations have been run before
|
363
|
-
# loading a web page if config.active_record.migration_error is set to :page_load
|
577
|
+
# loading a web page if <tt>config.active_record.migration_error</tt> is set to :page_load
|
364
578
|
class CheckPending
|
365
|
-
def initialize(app)
|
579
|
+
def initialize(app, file_watcher: ActiveSupport::FileUpdateChecker)
|
366
580
|
@app = app
|
367
|
-
@
|
581
|
+
@needs_check = true
|
582
|
+
@mutex = Mutex.new
|
583
|
+
@file_watcher = file_watcher
|
368
584
|
end
|
369
585
|
|
370
586
|
def call(env)
|
371
|
-
|
372
|
-
|
373
|
-
|
587
|
+
@mutex.synchronize do
|
588
|
+
@watcher ||= build_watcher do
|
589
|
+
@needs_check = true
|
374
590
|
ActiveRecord::Migration.check_pending!(connection)
|
375
|
-
@
|
591
|
+
@needs_check = false
|
592
|
+
end
|
593
|
+
|
594
|
+
if @needs_check
|
595
|
+
@watcher.execute
|
596
|
+
else
|
597
|
+
@watcher.execute_if_updated
|
376
598
|
end
|
377
599
|
end
|
600
|
+
|
378
601
|
@app.call(env)
|
379
602
|
end
|
380
603
|
|
381
604
|
private
|
605
|
+
def build_watcher(&block)
|
606
|
+
paths = Array(connection.migration_context.migrations_paths)
|
607
|
+
@file_watcher.new([], paths.index_with(["rb"]), &block)
|
608
|
+
end
|
382
609
|
|
383
|
-
|
384
|
-
|
385
|
-
|
610
|
+
def connection
|
611
|
+
ActiveRecord::Base.connection
|
612
|
+
end
|
386
613
|
end
|
387
614
|
|
388
615
|
class << self
|
389
|
-
attr_accessor :delegate
|
390
|
-
attr_accessor :disable_ddl_transaction
|
616
|
+
attr_accessor :delegate #:nodoc:
|
617
|
+
attr_accessor :disable_ddl_transaction #:nodoc:
|
618
|
+
|
619
|
+
def nearest_delegate #:nodoc:
|
620
|
+
delegate || superclass.nearest_delegate
|
621
|
+
end
|
391
622
|
|
623
|
+
# Raises <tt>ActiveRecord::PendingMigrationError</tt> error if any migrations are pending.
|
392
624
|
def check_pending!(connection = Base.connection)
|
393
|
-
raise ActiveRecord::PendingMigrationError if
|
625
|
+
raise ActiveRecord::PendingMigrationError if connection.migration_context.needs_migration?
|
394
626
|
end
|
395
627
|
|
396
628
|
def load_schema_if_pending!
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
629
|
+
current_db_config = Base.connection_db_config
|
630
|
+
all_configs = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env)
|
631
|
+
|
632
|
+
needs_update = !all_configs.all? do |db_config|
|
633
|
+
Tasks::DatabaseTasks.schema_up_to_date?(db_config, ActiveRecord::Base.schema_format)
|
634
|
+
end
|
635
|
+
|
636
|
+
if needs_update
|
637
|
+
# Roundtrip to Rake to allow plugins to hook into database initialization.
|
638
|
+
root = defined?(ENGINE_ROOT) ? ENGINE_ROOT : Rails.root
|
639
|
+
FileUtils.cd(root) do
|
401
640
|
Base.clear_all_connections!
|
402
|
-
system("bin/
|
403
|
-
# Establish a new connection, the old database may be gone (db:test:prepare uses purge)
|
404
|
-
Base.establish_connection(current_config)
|
641
|
+
system("bin/rails db:test:prepare")
|
405
642
|
end
|
406
|
-
check_pending!
|
407
643
|
end
|
644
|
+
|
645
|
+
# Establish a new connection, the old database may be gone (db:test:prepare uses purge)
|
646
|
+
Base.establish_connection(current_db_config)
|
647
|
+
|
648
|
+
check_pending!
|
408
649
|
end
|
409
650
|
|
410
|
-
def maintain_test_schema!
|
651
|
+
def maintain_test_schema! #:nodoc:
|
411
652
|
if ActiveRecord::Base.maintain_test_schema
|
412
653
|
suppress_messages { load_schema_if_pending! }
|
413
654
|
end
|
414
655
|
end
|
415
656
|
|
416
|
-
def method_missing(name, *args, &block)
|
417
|
-
|
657
|
+
def method_missing(name, *args, &block) #:nodoc:
|
658
|
+
nearest_delegate.send(name, *args, &block)
|
418
659
|
end
|
660
|
+
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
|
419
661
|
|
420
662
|
def migrate(direction)
|
421
663
|
new.migrate direction
|
422
664
|
end
|
423
665
|
|
424
|
-
# Disable
|
666
|
+
# Disable the transaction wrapping this migration.
|
667
|
+
# You can still create your own transactions even after calling #disable_ddl_transaction!
|
668
|
+
#
|
669
|
+
# For more details read the {"Transactional Migrations" section above}[rdoc-ref:Migration].
|
425
670
|
def disable_ddl_transaction!
|
426
671
|
@disable_ddl_transaction = true
|
427
672
|
end
|
428
673
|
end
|
429
674
|
|
430
|
-
def disable_ddl_transaction
|
675
|
+
def disable_ddl_transaction #:nodoc:
|
431
676
|
self.class.disable_ddl_transaction
|
432
677
|
end
|
433
678
|
|
@@ -451,7 +696,7 @@ module ActiveRecord
|
|
451
696
|
# and create the table 'apples' on the way up, and the reverse
|
452
697
|
# on the way down.
|
453
698
|
#
|
454
|
-
# class FixTLMigration < ActiveRecord::Migration
|
699
|
+
# class FixTLMigration < ActiveRecord::Migration[6.0]
|
455
700
|
# def change
|
456
701
|
# revert do
|
457
702
|
# create_table(:horses) do |t|
|
@@ -468,9 +713,9 @@ module ActiveRecord
|
|
468
713
|
# Or equivalently, if +TenderloveMigration+ is defined as in the
|
469
714
|
# documentation for Migration:
|
470
715
|
#
|
471
|
-
# require_relative
|
716
|
+
# require_relative "20121212123456_tenderlove_migration"
|
472
717
|
#
|
473
|
-
# class FixupTLMigration < ActiveRecord::Migration
|
718
|
+
# class FixupTLMigration < ActiveRecord::Migration[6.0]
|
474
719
|
# def change
|
475
720
|
# revert TenderloveMigration
|
476
721
|
#
|
@@ -484,27 +729,25 @@ module ActiveRecord
|
|
484
729
|
def revert(*migration_classes)
|
485
730
|
run(*migration_classes.reverse, revert: true) unless migration_classes.empty?
|
486
731
|
if block_given?
|
487
|
-
if
|
488
|
-
|
732
|
+
if connection.respond_to? :revert
|
733
|
+
connection.revert { yield }
|
489
734
|
else
|
490
|
-
recorder =
|
735
|
+
recorder = command_recorder
|
491
736
|
@connection = recorder
|
492
737
|
suppress_messages do
|
493
|
-
|
738
|
+
connection.revert { yield }
|
494
739
|
end
|
495
740
|
@connection = recorder.delegate
|
496
|
-
recorder.
|
497
|
-
send(cmd, *args, &block)
|
498
|
-
end
|
741
|
+
recorder.replay(self)
|
499
742
|
end
|
500
743
|
end
|
501
744
|
end
|
502
745
|
|
503
746
|
def reverting?
|
504
|
-
|
747
|
+
connection.respond_to?(:reverting) && connection.reverting
|
505
748
|
end
|
506
749
|
|
507
|
-
|
750
|
+
ReversibleBlockHelper = Struct.new(:reverting) do #:nodoc:
|
508
751
|
def up
|
509
752
|
yield unless reverting
|
510
753
|
end
|
@@ -523,7 +766,7 @@ module ActiveRecord
|
|
523
766
|
# when the three columns 'first_name', 'last_name' and 'full_name' exist,
|
524
767
|
# even when migrating down:
|
525
768
|
#
|
526
|
-
# class SplitNameMigration < ActiveRecord::Migration
|
769
|
+
# class SplitNameMigration < ActiveRecord::Migration[6.0]
|
527
770
|
# def change
|
528
771
|
# add_column :users, :first_name, :string
|
529
772
|
# add_column :users, :last_name, :string
|
@@ -542,7 +785,25 @@ module ActiveRecord
|
|
542
785
|
# end
|
543
786
|
def reversible
|
544
787
|
helper = ReversibleBlockHelper.new(reverting?)
|
545
|
-
execute_block{ yield helper }
|
788
|
+
execute_block { yield helper }
|
789
|
+
end
|
790
|
+
|
791
|
+
# Used to specify an operation that is only run when migrating up
|
792
|
+
# (for example, populating a new column with its initial values).
|
793
|
+
#
|
794
|
+
# In the following example, the new column +published+ will be given
|
795
|
+
# the value +true+ for all existing records.
|
796
|
+
#
|
797
|
+
# class AddPublishedToPosts < ActiveRecord::Migration[6.0]
|
798
|
+
# def change
|
799
|
+
# add_column :posts, :published, :boolean, default: false
|
800
|
+
# up_only do
|
801
|
+
# execute "update posts set published = 'true'"
|
802
|
+
# end
|
803
|
+
# end
|
804
|
+
# end
|
805
|
+
def up_only
|
806
|
+
execute_block { yield } unless reverting?
|
546
807
|
end
|
547
808
|
|
548
809
|
# Runs the given migration classes.
|
@@ -558,7 +819,7 @@ module ActiveRecord
|
|
558
819
|
revert { run(*migration_classes, direction: dir, revert: true) }
|
559
820
|
else
|
560
821
|
migration_classes.each do |migration_class|
|
561
|
-
migration_class.new.exec_migration(
|
822
|
+
migration_class.new.exec_migration(connection, dir)
|
562
823
|
end
|
563
824
|
end
|
564
825
|
end
|
@@ -584,7 +845,7 @@ module ActiveRecord
|
|
584
845
|
when :down then announce "reverting"
|
585
846
|
end
|
586
847
|
|
587
|
-
time
|
848
|
+
time = nil
|
588
849
|
ActiveRecord::Base.connection_pool.with_connection do |conn|
|
589
850
|
time = Benchmark.measure do
|
590
851
|
exec_migration(conn, direction)
|
@@ -606,13 +867,13 @@ module ActiveRecord
|
|
606
867
|
change
|
607
868
|
end
|
608
869
|
else
|
609
|
-
|
870
|
+
public_send(direction)
|
610
871
|
end
|
611
872
|
ensure
|
612
873
|
@connection = nil
|
613
874
|
end
|
614
875
|
|
615
|
-
def write(text="")
|
876
|
+
def write(text = "")
|
616
877
|
puts(text) if verbose
|
617
878
|
end
|
618
879
|
|
@@ -622,10 +883,14 @@ module ActiveRecord
|
|
622
883
|
write "== %s %s" % [text, "=" * length]
|
623
884
|
end
|
624
885
|
|
625
|
-
|
886
|
+
# Takes a message argument and outputs it as is.
|
887
|
+
# A second boolean argument can be passed to specify whether to indent or not.
|
888
|
+
def say(message, subitem = false)
|
626
889
|
write "#{subitem ? " ->" : "--"} #{message}"
|
627
890
|
end
|
628
891
|
|
892
|
+
# Outputs text along with how long it took to run its block.
|
893
|
+
# If the block returns an integer it assumes it is the number of rows affected.
|
629
894
|
def say_with_time(message)
|
630
895
|
say(message)
|
631
896
|
result = nil
|
@@ -635,6 +900,7 @@ module ActiveRecord
|
|
635
900
|
result
|
636
901
|
end
|
637
902
|
|
903
|
+
# Takes a block as an argument and suppresses any output generated by the block.
|
638
904
|
def suppress_messages
|
639
905
|
save, self.verbose = verbose, false
|
640
906
|
yield
|
@@ -647,13 +913,14 @@ module ActiveRecord
|
|
647
913
|
end
|
648
914
|
|
649
915
|
def method_missing(method, *arguments, &block)
|
650
|
-
arg_list = arguments.map
|
916
|
+
arg_list = arguments.map(&:inspect) * ", "
|
651
917
|
|
652
918
|
say_with_time "#{method}(#{arg_list})" do
|
653
|
-
unless
|
919
|
+
unless connection.respond_to? :revert
|
654
920
|
unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method)
|
655
921
|
arguments[0] = proper_table_name(arguments.first, table_name_options)
|
656
|
-
if [:rename_table, :add_foreign_key].include?(method)
|
922
|
+
if [:rename_table, :add_foreign_key].include?(method) ||
|
923
|
+
(method == :remove_foreign_key && !arguments.second.is_a?(Hash))
|
657
924
|
arguments[1] = proper_table_name(arguments.second, table_name_options)
|
658
925
|
end
|
659
926
|
end
|
@@ -662,29 +929,33 @@ module ActiveRecord
|
|
662
929
|
connection.send(method, *arguments, &block)
|
663
930
|
end
|
664
931
|
end
|
932
|
+
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
|
665
933
|
|
666
934
|
def copy(destination, sources, options = {})
|
667
935
|
copied = []
|
936
|
+
schema_migration = options[:schema_migration] || ActiveRecord::SchemaMigration
|
668
937
|
|
669
938
|
FileUtils.mkdir_p(destination) unless File.exist?(destination)
|
670
939
|
|
671
|
-
destination_migrations = ActiveRecord::
|
940
|
+
destination_migrations = ActiveRecord::MigrationContext.new(destination, schema_migration).migrations
|
672
941
|
last = destination_migrations.last
|
673
942
|
sources.each do |scope, path|
|
674
|
-
source_migrations = ActiveRecord::
|
943
|
+
source_migrations = ActiveRecord::MigrationContext.new(path, schema_migration).migrations
|
675
944
|
|
676
945
|
source_migrations.each do |migration|
|
677
946
|
source = File.binread(migration.filename)
|
678
947
|
inserted_comment = "# This migration comes from #{scope} (originally #{migration.version})\n"
|
679
|
-
|
948
|
+
magic_comments = +""
|
949
|
+
loop do
|
680
950
|
# If we have a magic comment in the original migration,
|
681
951
|
# insert our comment after the first newline(end of the magic comment line)
|
682
952
|
# so the magic keep working.
|
683
953
|
# Note that magic comments must be at the first line(except sh-bang).
|
684
|
-
source
|
685
|
-
|
686
|
-
|
954
|
+
source.sub!(/\A(?:#.*\b(?:en)?coding:\s*\S+|#\s*frozen_string_literal:\s*(?:true|false)).*\n/) do |magic_comment|
|
955
|
+
magic_comments << magic_comment; ""
|
956
|
+
end || break
|
687
957
|
end
|
958
|
+
source = "#{magic_comments}#{inserted_comment}#{source}"
|
688
959
|
|
689
960
|
if duplicate = destination_migrations.detect { |m| m.name == migration.name }
|
690
961
|
if options[:on_skip] && duplicate.scope != scope.to_s
|
@@ -728,7 +999,9 @@ module ActiveRecord
|
|
728
999
|
end
|
729
1000
|
end
|
730
1001
|
|
731
|
-
|
1002
|
+
# Builds a hash for use in ActiveRecord::Migration#proper_table_name using
|
1003
|
+
# the Active Record object's table_name prefix and suffix
|
1004
|
+
def table_name_options(config = ActiveRecord::Base) #:nodoc:
|
732
1005
|
{
|
733
1006
|
table_name_prefix: config.table_name_prefix,
|
734
1007
|
table_name_suffix: config.table_name_suffix
|
@@ -736,19 +1009,22 @@ module ActiveRecord
|
|
736
1009
|
end
|
737
1010
|
|
738
1011
|
private
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
1012
|
+
def execute_block
|
1013
|
+
if connection.respond_to? :execute_block
|
1014
|
+
super # use normal delegation to record the block
|
1015
|
+
else
|
1016
|
+
yield
|
1017
|
+
end
|
1018
|
+
end
|
1019
|
+
|
1020
|
+
def command_recorder
|
1021
|
+
CommandRecorder.new(connection)
|
744
1022
|
end
|
745
|
-
end
|
746
1023
|
end
|
747
1024
|
|
748
1025
|
# MigrationProxy is used to defer loading of the actual migration classes
|
749
1026
|
# until they are needed
|
750
|
-
|
751
|
-
|
1027
|
+
MigrationProxy = Struct.new(:name, :version, :filename, :scope) do
|
752
1028
|
def initialize(name, version, filename, scope)
|
753
1029
|
super
|
754
1030
|
@migration = nil
|
@@ -758,14 +1034,9 @@ module ActiveRecord
|
|
758
1034
|
File.basename(filename)
|
759
1035
|
end
|
760
1036
|
|
761
|
-
def mtime
|
762
|
-
File.mtime filename
|
763
|
-
end
|
764
|
-
|
765
1037
|
delegate :migrate, :announce, :write, :disable_ddl_transaction, to: :migration
|
766
1038
|
|
767
1039
|
private
|
768
|
-
|
769
1040
|
def migration
|
770
1041
|
@migration ||= load_migration
|
771
1042
|
end
|
@@ -774,152 +1045,188 @@ module ActiveRecord
|
|
774
1045
|
require(File.expand_path(filename))
|
775
1046
|
name.constantize.new(name, version)
|
776
1047
|
end
|
777
|
-
|
778
1048
|
end
|
779
1049
|
|
780
|
-
class
|
781
|
-
|
782
|
-
super(nil, 0, nil, nil)
|
783
|
-
end
|
1050
|
+
class MigrationContext #:nodoc:
|
1051
|
+
attr_reader :migrations_paths, :schema_migration
|
784
1052
|
|
785
|
-
def
|
786
|
-
|
1053
|
+
def initialize(migrations_paths, schema_migration)
|
1054
|
+
@migrations_paths = migrations_paths
|
1055
|
+
@schema_migration = schema_migration
|
787
1056
|
end
|
788
|
-
end
|
789
1057
|
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
|
800
|
-
[]
|
801
|
-
when current_version > target_version
|
802
|
-
down(migrations_paths, target_version, &block)
|
803
|
-
else
|
804
|
-
up(migrations_paths, target_version, &block)
|
805
|
-
end
|
1058
|
+
def migrate(target_version = nil, &block)
|
1059
|
+
case
|
1060
|
+
when target_version.nil?
|
1061
|
+
up(target_version, &block)
|
1062
|
+
when current_version == 0 && target_version == 0
|
1063
|
+
[]
|
1064
|
+
when current_version > target_version
|
1065
|
+
down(target_version, &block)
|
1066
|
+
else
|
1067
|
+
up(target_version, &block)
|
806
1068
|
end
|
1069
|
+
end
|
807
1070
|
|
808
|
-
|
809
|
-
|
810
|
-
|
1071
|
+
def rollback(steps = 1)
|
1072
|
+
move(:down, steps)
|
1073
|
+
end
|
811
1074
|
|
812
|
-
|
813
|
-
|
1075
|
+
def forward(steps = 1)
|
1076
|
+
move(:up, steps)
|
1077
|
+
end
|
1078
|
+
|
1079
|
+
def up(target_version = nil)
|
1080
|
+
selected_migrations = if block_given?
|
1081
|
+
migrations.select { |m| yield m }
|
1082
|
+
else
|
1083
|
+
migrations
|
814
1084
|
end
|
815
1085
|
|
816
|
-
|
817
|
-
|
818
|
-
migrations.select! { |m| yield m } if block_given?
|
1086
|
+
Migrator.new(:up, selected_migrations, schema_migration, target_version).migrate
|
1087
|
+
end
|
819
1088
|
|
820
|
-
|
1089
|
+
def down(target_version = nil)
|
1090
|
+
selected_migrations = if block_given?
|
1091
|
+
migrations.select { |m| yield m }
|
1092
|
+
else
|
1093
|
+
migrations
|
821
1094
|
end
|
822
1095
|
|
823
|
-
|
824
|
-
|
825
|
-
migrations.select! { |m| yield m } if block_given?
|
1096
|
+
Migrator.new(:down, selected_migrations, schema_migration, target_version).migrate
|
1097
|
+
end
|
826
1098
|
|
827
|
-
|
828
|
-
|
1099
|
+
def run(direction, target_version)
|
1100
|
+
Migrator.new(direction, migrations, schema_migration, target_version).run
|
1101
|
+
end
|
829
1102
|
|
830
|
-
|
831
|
-
|
832
|
-
|
1103
|
+
def open
|
1104
|
+
Migrator.new(:up, migrations, schema_migration)
|
1105
|
+
end
|
833
1106
|
|
834
|
-
|
835
|
-
|
1107
|
+
def get_all_versions
|
1108
|
+
if schema_migration.table_exists?
|
1109
|
+
schema_migration.all_versions.map(&:to_i)
|
1110
|
+
else
|
1111
|
+
[]
|
836
1112
|
end
|
1113
|
+
end
|
837
1114
|
|
838
|
-
|
839
|
-
|
840
|
-
|
1115
|
+
def current_version
|
1116
|
+
get_all_versions.max || 0
|
1117
|
+
rescue ActiveRecord::NoDatabaseError
|
1118
|
+
end
|
841
1119
|
|
842
|
-
|
843
|
-
|
844
|
-
|
845
|
-
else
|
846
|
-
[]
|
847
|
-
end
|
848
|
-
end
|
1120
|
+
def needs_migration?
|
1121
|
+
(migrations.collect(&:version) - get_all_versions).size > 0
|
1122
|
+
end
|
849
1123
|
|
850
|
-
|
851
|
-
|
852
|
-
|
1124
|
+
def any_migrations?
|
1125
|
+
migrations.any?
|
1126
|
+
end
|
853
1127
|
|
854
|
-
|
855
|
-
|
856
|
-
|
1128
|
+
def migrations
|
1129
|
+
migrations = migration_files.map do |file|
|
1130
|
+
version, name, scope = parse_migration_filename(file)
|
1131
|
+
raise IllegalMigrationNameError.new(file) unless version
|
1132
|
+
version = version.to_i
|
1133
|
+
name = name.camelize
|
857
1134
|
|
858
|
-
|
859
|
-
migrations(migrations_paths).any?
|
1135
|
+
MigrationProxy.new(name, version, file, scope)
|
860
1136
|
end
|
861
1137
|
|
862
|
-
|
863
|
-
|
864
|
-
end
|
1138
|
+
migrations.sort_by(&:version)
|
1139
|
+
end
|
865
1140
|
|
866
|
-
|
867
|
-
|
868
|
-
end
|
1141
|
+
def migrations_status
|
1142
|
+
db_list = schema_migration.normalized_versions
|
869
1143
|
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
1144
|
+
file_list = migration_files.map do |file|
|
1145
|
+
version, name, scope = parse_migration_filename(file)
|
1146
|
+
raise IllegalMigrationNameError.new(file) unless version
|
1147
|
+
version = schema_migration.normalize_migration_number(version)
|
1148
|
+
status = db_list.delete(version) ? "up" : "down"
|
1149
|
+
[status, version, (name + scope).humanize]
|
1150
|
+
end.compact
|
875
1151
|
|
876
|
-
|
877
|
-
|
1152
|
+
db_list.map! do |version|
|
1153
|
+
["up", version, "********** NO FILE **********"]
|
878
1154
|
end
|
879
1155
|
|
880
|
-
|
881
|
-
|
1156
|
+
(db_list + file_list).sort_by { |_, version, _| version }
|
1157
|
+
end
|
882
1158
|
|
883
|
-
|
1159
|
+
def current_environment
|
1160
|
+
ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
|
1161
|
+
end
|
884
1162
|
|
885
|
-
|
886
|
-
|
1163
|
+
def protected_environment?
|
1164
|
+
ActiveRecord::Base.protected_environments.include?(last_stored_environment) if last_stored_environment
|
1165
|
+
end
|
887
1166
|
|
888
|
-
|
889
|
-
|
890
|
-
|
1167
|
+
def last_stored_environment
|
1168
|
+
return nil unless ActiveRecord::InternalMetadata.enabled?
|
1169
|
+
return nil if current_version == 0
|
1170
|
+
raise NoEnvironmentInSchemaError unless ActiveRecord::InternalMetadata.table_exists?
|
891
1171
|
|
892
|
-
|
893
|
-
|
1172
|
+
environment = ActiveRecord::InternalMetadata[:environment]
|
1173
|
+
raise NoEnvironmentInSchemaError unless environment
|
1174
|
+
environment
|
1175
|
+
end
|
894
1176
|
|
895
|
-
|
1177
|
+
private
|
1178
|
+
def migration_files
|
1179
|
+
paths = Array(migrations_paths)
|
1180
|
+
Dir[*paths.flat_map { |path| "#{path}/**/[0-9]*_*.rb" }]
|
896
1181
|
end
|
897
1182
|
|
898
|
-
|
1183
|
+
def parse_migration_filename(filename)
|
1184
|
+
File.basename(filename).scan(Migration::MigrationFilenameRegexp).first
|
1185
|
+
end
|
899
1186
|
|
900
|
-
def move(direction,
|
901
|
-
migrator = new(direction, migrations
|
902
|
-
start_index = migrator.migrations.index(migrator.current_migration)
|
1187
|
+
def move(direction, steps)
|
1188
|
+
migrator = Migrator.new(direction, migrations, schema_migration)
|
903
1189
|
|
904
|
-
if
|
905
|
-
|
906
|
-
version = finish ? finish.version : 0
|
907
|
-
send(direction, migrations_paths, version)
|
1190
|
+
if current_version != 0 && !migrator.current_migration
|
1191
|
+
raise UnknownMigrationVersionError.new(current_version)
|
908
1192
|
end
|
1193
|
+
|
1194
|
+
start_index =
|
1195
|
+
if current_version == 0
|
1196
|
+
0
|
1197
|
+
else
|
1198
|
+
migrator.migrations.index(migrator.current_migration)
|
1199
|
+
end
|
1200
|
+
|
1201
|
+
finish = migrator.migrations[start_index + steps]
|
1202
|
+
version = finish ? finish.version : 0
|
1203
|
+
public_send(direction, version)
|
1204
|
+
end
|
1205
|
+
end
|
1206
|
+
|
1207
|
+
class Migrator # :nodoc:
|
1208
|
+
class << self
|
1209
|
+
attr_accessor :migrations_paths
|
1210
|
+
|
1211
|
+
# For cases where a table doesn't exist like loading from schema cache
|
1212
|
+
def current_version
|
1213
|
+
MigrationContext.new(migrations_paths, SchemaMigration).current_version
|
909
1214
|
end
|
910
1215
|
end
|
911
1216
|
|
912
|
-
|
913
|
-
raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations?
|
1217
|
+
self.migrations_paths = ["db/migrate"]
|
914
1218
|
|
1219
|
+
def initialize(direction, migrations, schema_migration, target_version = nil)
|
915
1220
|
@direction = direction
|
916
1221
|
@target_version = target_version
|
917
1222
|
@migrated_versions = nil
|
918
1223
|
@migrations = migrations
|
1224
|
+
@schema_migration = schema_migration
|
919
1225
|
|
920
1226
|
validate(@migrations)
|
921
1227
|
|
922
|
-
|
1228
|
+
@schema_migration.create_table
|
1229
|
+
ActiveRecord::InternalMetadata.create_table
|
923
1230
|
end
|
924
1231
|
|
925
1232
|
def current_version
|
@@ -932,32 +1239,18 @@ module ActiveRecord
|
|
932
1239
|
alias :current :current_migration
|
933
1240
|
|
934
1241
|
def run
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
execute_migration_in_transaction(migration, @direction)
|
940
|
-
rescue => e
|
941
|
-
canceled_msg = use_transaction?(migration) ? ", this migration was canceled" : ""
|
942
|
-
raise StandardError, "An error has occurred#{canceled_msg}:\n\n#{e}", e.backtrace
|
943
|
-
end
|
1242
|
+
if use_advisory_lock?
|
1243
|
+
with_advisory_lock { run_without_lock }
|
1244
|
+
else
|
1245
|
+
run_without_lock
|
944
1246
|
end
|
945
1247
|
end
|
946
1248
|
|
947
1249
|
def migrate
|
948
|
-
if
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
runnable.each do |migration|
|
953
|
-
Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
|
954
|
-
|
955
|
-
begin
|
956
|
-
execute_migration_in_transaction(migration, @direction)
|
957
|
-
rescue => e
|
958
|
-
canceled_msg = use_transaction?(migration) ? "this and " : ""
|
959
|
-
raise StandardError, "An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}", e.backtrace
|
960
|
-
end
|
1250
|
+
if use_advisory_lock?
|
1251
|
+
with_advisory_lock { migrate_without_lock }
|
1252
|
+
else
|
1253
|
+
migrate_without_lock
|
961
1254
|
end
|
962
1255
|
end
|
963
1256
|
|
@@ -982,70 +1275,153 @@ module ActiveRecord
|
|
982
1275
|
end
|
983
1276
|
|
984
1277
|
def migrated
|
985
|
-
@migrated_versions
|
1278
|
+
@migrated_versions || load_migrated
|
986
1279
|
end
|
987
1280
|
|
988
|
-
|
989
|
-
|
990
|
-
migrated.include?(migration.version.to_i)
|
1281
|
+
def load_migrated
|
1282
|
+
@migrated_versions = Set.new(@schema_migration.all_versions.map(&:to_i))
|
991
1283
|
end
|
992
1284
|
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
1285
|
+
private
|
1286
|
+
# Used for running a specific migration.
|
1287
|
+
def run_without_lock
|
1288
|
+
migration = migrations.detect { |m| m.version == @target_version }
|
1289
|
+
raise UnknownMigrationVersionError.new(@target_version) if migration.nil?
|
1290
|
+
result = execute_migration_in_transaction(migration)
|
1291
|
+
|
1292
|
+
record_environment
|
1293
|
+
result
|
997
1294
|
end
|
998
|
-
end
|
999
1295
|
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1296
|
+
# Used for running multiple migrations up to or down to a certain value.
|
1297
|
+
def migrate_without_lock
|
1298
|
+
if invalid_target?
|
1299
|
+
raise UnknownMigrationVersionError.new(@target_version)
|
1300
|
+
end
|
1003
1301
|
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1302
|
+
result = runnable.each(&method(:execute_migration_in_transaction))
|
1303
|
+
record_environment
|
1304
|
+
result
|
1305
|
+
end
|
1007
1306
|
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1307
|
+
# Stores the current environment in the database.
|
1308
|
+
def record_environment
|
1309
|
+
return if down?
|
1310
|
+
ActiveRecord::InternalMetadata[:environment] = ActiveRecord::Base.connection.migration_context.current_environment
|
1311
|
+
end
|
1011
1312
|
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1313
|
+
def ran?(migration)
|
1314
|
+
migrated.include?(migration.version.to_i)
|
1315
|
+
end
|
1015
1316
|
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
1317
|
+
# Return true if a valid version is not provided.
|
1318
|
+
def invalid_target?
|
1319
|
+
@target_version && @target_version != 0 && !target
|
1320
|
+
end
|
1019
1321
|
|
1020
|
-
|
1021
|
-
|
1022
|
-
migrated.
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1322
|
+
def execute_migration_in_transaction(migration)
|
1323
|
+
return if down? && !migrated.include?(migration.version.to_i)
|
1324
|
+
return if up? && migrated.include?(migration.version.to_i)
|
1325
|
+
|
1326
|
+
Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
|
1327
|
+
|
1328
|
+
ddl_transaction(migration) do
|
1329
|
+
migration.migrate(@direction)
|
1330
|
+
record_version_state_after_migrating(migration.version)
|
1331
|
+
end
|
1332
|
+
rescue => e
|
1333
|
+
msg = +"An error has occurred, "
|
1334
|
+
msg << "this and " if use_transaction?(migration)
|
1335
|
+
msg << "all later migrations canceled:\n\n#{e}"
|
1336
|
+
raise StandardError, msg, e.backtrace
|
1027
1337
|
end
|
1028
|
-
end
|
1029
1338
|
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1339
|
+
def target
|
1340
|
+
migrations.detect { |m| m.version == @target_version }
|
1341
|
+
end
|
1033
1342
|
|
1034
|
-
|
1035
|
-
|
1036
|
-
|
1343
|
+
def finish
|
1344
|
+
migrations.index(target) || migrations.size - 1
|
1345
|
+
end
|
1037
1346
|
|
1038
|
-
|
1039
|
-
|
1040
|
-
if use_transaction?(migration)
|
1041
|
-
Base.transaction { yield }
|
1042
|
-
else
|
1043
|
-
yield
|
1347
|
+
def start
|
1348
|
+
up? ? 0 : (migrations.index(current) || 0)
|
1044
1349
|
end
|
1045
|
-
end
|
1046
1350
|
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1351
|
+
def validate(migrations)
|
1352
|
+
name, = migrations.group_by(&:name).find { |_, v| v.length > 1 }
|
1353
|
+
raise DuplicateMigrationNameError.new(name) if name
|
1354
|
+
|
1355
|
+
version, = migrations.group_by(&:version).find { |_, v| v.length > 1 }
|
1356
|
+
raise DuplicateMigrationVersionError.new(version) if version
|
1357
|
+
end
|
1358
|
+
|
1359
|
+
def record_version_state_after_migrating(version)
|
1360
|
+
if down?
|
1361
|
+
migrated.delete(version)
|
1362
|
+
@schema_migration.delete_by(version: version.to_s)
|
1363
|
+
else
|
1364
|
+
migrated << version
|
1365
|
+
@schema_migration.create!(version: version.to_s)
|
1366
|
+
end
|
1367
|
+
end
|
1368
|
+
|
1369
|
+
def up?
|
1370
|
+
@direction == :up
|
1371
|
+
end
|
1372
|
+
|
1373
|
+
def down?
|
1374
|
+
@direction == :down
|
1375
|
+
end
|
1376
|
+
|
1377
|
+
# Wrap the migration in a transaction only if supported by the adapter.
|
1378
|
+
def ddl_transaction(migration)
|
1379
|
+
if use_transaction?(migration)
|
1380
|
+
Base.transaction { yield }
|
1381
|
+
else
|
1382
|
+
yield
|
1383
|
+
end
|
1384
|
+
end
|
1385
|
+
|
1386
|
+
def use_transaction?(migration)
|
1387
|
+
!migration.disable_ddl_transaction && Base.connection.supports_ddl_transactions?
|
1388
|
+
end
|
1389
|
+
|
1390
|
+
def use_advisory_lock?
|
1391
|
+
Base.connection.advisory_locks_enabled?
|
1392
|
+
end
|
1393
|
+
|
1394
|
+
def with_advisory_lock
|
1395
|
+
lock_id = generate_migrator_advisory_lock_id
|
1396
|
+
|
1397
|
+
with_advisory_lock_connection do |connection|
|
1398
|
+
got_lock = connection.get_advisory_lock(lock_id)
|
1399
|
+
raise ConcurrentMigrationError unless got_lock
|
1400
|
+
load_migrated # reload schema_migrations to be sure it wasn't changed by another process before we got the lock
|
1401
|
+
yield
|
1402
|
+
ensure
|
1403
|
+
if got_lock && !connection.release_advisory_lock(lock_id)
|
1404
|
+
raise ConcurrentMigrationError.new(
|
1405
|
+
ConcurrentMigrationError::RELEASE_LOCK_FAILED_MESSAGE
|
1406
|
+
)
|
1407
|
+
end
|
1408
|
+
end
|
1409
|
+
end
|
1410
|
+
|
1411
|
+
def with_advisory_lock_connection
|
1412
|
+
pool = ActiveRecord::ConnectionAdapters::ConnectionHandler.new.establish_connection(
|
1413
|
+
ActiveRecord::Base.connection_db_config
|
1414
|
+
)
|
1415
|
+
|
1416
|
+
pool.with_connection { |connection| yield(connection) }
|
1417
|
+
ensure
|
1418
|
+
pool&.disconnect!
|
1419
|
+
end
|
1420
|
+
|
1421
|
+
MIGRATOR_SALT = 2053462845
|
1422
|
+
def generate_migrator_advisory_lock_id
|
1423
|
+
db_name_hash = Zlib.crc32(Base.connection.current_database)
|
1424
|
+
MIGRATOR_SALT * db_name_hash
|
1425
|
+
end
|
1050
1426
|
end
|
1051
1427
|
end
|