activerecord 7.0.0 → 7.2.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 +4 -4
- data/CHANGELOG.md +515 -1268
- data/MIT-LICENSE +1 -1
- data/README.rdoc +31 -31
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +2 -2
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +35 -12
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +23 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +22 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +28 -17
- data/lib/active_record/associations/collection_proxy.rb +36 -13
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +28 -18
- data/lib/active_record/associations/has_many_through_association.rb +10 -6
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/join_dependency.rb +18 -14
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +33 -8
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +2 -4
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +7 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +378 -491
- data/lib/active_record/attribute_assignment.rb +1 -13
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +53 -35
- data/lib/active_record/attribute_methods/primary_key.rb +45 -25
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +8 -7
- data/lib/active_record/attribute_methods/serialization.rb +153 -70
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -6
- data/lib/active_record/attribute_methods/write.rb +6 -6
- data/lib/active_record/attribute_methods.rb +153 -40
- data/lib/active_record/attributes.rb +63 -48
- data/lib/active_record/autosave_association.rb +70 -38
- data/lib/active_record/base.rb +12 -8
- data/lib/active_record/callbacks.rb +16 -32
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -34
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +124 -132
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +297 -88
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +215 -63
- data/lib/active_record/connection_adapters/abstract/quoting.rb +83 -65
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +163 -29
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +319 -135
- data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +512 -126
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +282 -119
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +27 -140
- data/lib/active_record/connection_adapters/mysql/quoting.rb +64 -52
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +45 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +101 -68
- data/lib/active_record/connection_adapters/pool_config.rb +20 -10
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +16 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +101 -48
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +94 -61
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +6 -10
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +151 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +379 -66
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +370 -203
- data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +61 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +64 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +321 -110
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
- data/lib/active_record/connection_adapters.rb +124 -1
- data/lib/active_record/connection_handling.rb +98 -106
- data/lib/active_record/core.rb +220 -177
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -2
- data/lib/active_record/database_configurations/database_config.rb +26 -5
- data/lib/active_record/database_configurations/hash_config.rb +52 -34
- data/lib/active_record/database_configurations/url_config.rb +37 -12
- data/lib/active_record/database_configurations.rb +88 -35
- data/lib/active_record/delegated_type.rb +40 -11
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -1
- data/lib/active_record/disable_joins_association_relation.rb +1 -1
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +13 -14
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +8 -4
- data/lib/active_record/encryption/derived_secret_key_provider.rb +9 -3
- data/lib/active_record/encryption/deterministic_key_provider.rb +1 -1
- data/lib/active_record/encryption/encryptable_record.rb +47 -25
- data/lib/active_record/encryption/encrypted_attribute_type.rb +49 -14
- data/lib/active_record/encryption/encryptor.rb +25 -10
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +3 -3
- data/lib/active_record/encryption/extended_deterministic_queries.rb +83 -86
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/message.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +6 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/properties.rb +4 -4
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +23 -22
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +131 -27
- data/lib/active_record/errors.rb +151 -31
- data/lib/active_record/explain.rb +21 -12
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +29 -8
- data/lib/active_record/fixtures.rb +169 -99
- data/lib/active_record/future_result.rb +47 -8
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +34 -18
- data/lib/active_record/insert_all.rb +72 -22
- data/lib/active_record/integration.rb +13 -10
- data/lib/active_record/internal_metadata.rb +124 -20
- data/lib/active_record/locking/optimistic.rb +39 -24
- data/lib/active_record/locking/pessimistic.rb +8 -5
- data/lib/active_record/log_subscriber.rb +28 -27
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
- data/lib/active_record/middleware/database_selector.rb +18 -13
- data/lib/active_record/middleware/shard_selector.rb +7 -5
- data/lib/active_record/migration/command_recorder.rb +110 -13
- data/lib/active_record/migration/compatibility.rb +174 -64
- data/lib/active_record/migration/default_strategy.rb +22 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +292 -125
- data/lib/active_record/model_schema.rb +113 -112
- data/lib/active_record/nested_attributes.rb +35 -9
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +177 -345
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +19 -25
- data/lib/active_record/query_logs.rb +102 -51
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +34 -9
- data/lib/active_record/railtie.rb +153 -100
- data/lib/active_record/railties/controller_runtime.rb +24 -10
- data/lib/active_record/railties/databases.rake +148 -152
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +278 -69
- data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
- data/lib/active_record/relation/batches.rb +198 -63
- data/lib/active_record/relation/calculations.rb +293 -108
- data/lib/active_record/relation/delegation.rb +31 -20
- data/lib/active_record/relation/finder_methods.rb +93 -18
- data/lib/active_record/relation/merger.rb +6 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +28 -16
- data/lib/active_record/relation/query_attribute.rb +25 -1
- data/lib/active_record/relation/query_methods.rb +625 -107
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +5 -4
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +602 -96
- data/lib/active_record/result.rb +55 -52
- data/lib/active_record/runtime_registry.rb +63 -1
- data/lib/active_record/sanitization.rb +76 -30
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +82 -30
- data/lib/active_record/schema_migration.rb +75 -24
- data/lib/active_record/scoping/default.rb +20 -12
- data/lib/active_record/scoping/named.rb +3 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/serialization.rb +5 -0
- data/lib/active_record/signed_id.rb +29 -8
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +16 -11
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +7 -3
- data/lib/active_record/tasks/database_tasks.rb +191 -121
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +17 -15
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
- data/lib/active_record/test_fixtures.rb +174 -152
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +31 -17
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +12 -7
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +109 -27
- data/lib/active_record/translation.rb +1 -3
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +9 -7
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +12 -6
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +63 -14
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +266 -30
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +1 -1
- data/lib/arel/nodes/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/filter.rb +1 -1
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
- data/lib/arel/nodes/node.rb +115 -5
- data/lib/arel/nodes/sql_literal.rb +13 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +6 -2
- data/lib/arel/predications.rb +3 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/table.rb +9 -5
- data/lib/arel/tree_manager.rb +8 -3
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +17 -5
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/to_sql.rb +112 -34
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +21 -3
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- metadata +59 -17
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
|
@@ -7,6 +7,7 @@ require "active_support/core_ext/array/access"
|
|
|
7
7
|
require "active_support/core_ext/enumerable"
|
|
8
8
|
require "active_support/core_ext/module/attribute_accessors"
|
|
9
9
|
require "active_support/actionable_error"
|
|
10
|
+
require "active_record/migration/pending_migration_connection"
|
|
10
11
|
|
|
11
12
|
module ActiveRecord
|
|
12
13
|
class MigrationError < ActiveRecordError # :nodoc:
|
|
@@ -20,7 +21,7 @@ module ActiveRecord
|
|
|
20
21
|
# For example the following migration is not reversible.
|
|
21
22
|
# Rolling back this migration will raise an ActiveRecord::IrreversibleMigration error.
|
|
22
23
|
#
|
|
23
|
-
# class IrreversibleMigrationExample < ActiveRecord::Migration[7.
|
|
24
|
+
# class IrreversibleMigrationExample < ActiveRecord::Migration[7.2]
|
|
24
25
|
# def change
|
|
25
26
|
# create_table :distributors do |t|
|
|
26
27
|
# t.string :zipcode
|
|
@@ -38,7 +39,7 @@ module ActiveRecord
|
|
|
38
39
|
#
|
|
39
40
|
# 1. Define <tt>#up</tt> and <tt>#down</tt> methods instead of <tt>#change</tt>:
|
|
40
41
|
#
|
|
41
|
-
# class ReversibleMigrationExample < ActiveRecord::Migration[7.
|
|
42
|
+
# class ReversibleMigrationExample < ActiveRecord::Migration[7.2]
|
|
42
43
|
# def up
|
|
43
44
|
# create_table :distributors do |t|
|
|
44
45
|
# t.string :zipcode
|
|
@@ -63,7 +64,7 @@ module ActiveRecord
|
|
|
63
64
|
#
|
|
64
65
|
# 2. Use the #reversible method in <tt>#change</tt> method:
|
|
65
66
|
#
|
|
66
|
-
# class ReversibleMigrationExample < ActiveRecord::Migration[7.
|
|
67
|
+
# class ReversibleMigrationExample < ActiveRecord::Migration[7.2]
|
|
67
68
|
# def change
|
|
68
69
|
# create_table :distributors do |t|
|
|
69
70
|
# t.string :zipcode
|
|
@@ -130,6 +131,22 @@ module ActiveRecord
|
|
|
130
131
|
end
|
|
131
132
|
end
|
|
132
133
|
|
|
134
|
+
class InvalidMigrationTimestampError < MigrationError # :nodoc:
|
|
135
|
+
def initialize(version = nil, name = nil)
|
|
136
|
+
if version && name
|
|
137
|
+
super(<<~MSG)
|
|
138
|
+
Invalid timestamp #{version} for migration file: #{name}.
|
|
139
|
+
Timestamp must be in form YYYYMMDDHHMMSS, and less than #{(Time.now.utc + 1.day).strftime("%Y%m%d%H%M%S")}.
|
|
140
|
+
MSG
|
|
141
|
+
else
|
|
142
|
+
super(<<~MSG)
|
|
143
|
+
Invalid timestamp for migration.
|
|
144
|
+
Timestamp must be in form YYYYMMDDHHMMSS, and less than #{(Time.now.utc + 1.day).strftime("%Y%m%d%H%M%S")}.
|
|
145
|
+
MSG
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
133
150
|
class PendingMigrationError < MigrationError # :nodoc:
|
|
134
151
|
include ActiveSupport::ActionableError
|
|
135
152
|
|
|
@@ -137,32 +154,37 @@ module ActiveRecord
|
|
|
137
154
|
ActiveRecord::Tasks::DatabaseTasks.migrate
|
|
138
155
|
|
|
139
156
|
if ActiveRecord.dump_schema_after_migration
|
|
140
|
-
ActiveRecord::Tasks::DatabaseTasks.
|
|
141
|
-
|
|
142
|
-
)
|
|
157
|
+
connection = ActiveRecord::Tasks::DatabaseTasks.migration_connection
|
|
158
|
+
ActiveRecord::Tasks::DatabaseTasks.dump_schema(connection.pool.db_config)
|
|
143
159
|
end
|
|
144
160
|
end
|
|
145
161
|
|
|
146
|
-
def initialize(message = nil)
|
|
147
|
-
|
|
162
|
+
def initialize(message = nil, pending_migrations: nil)
|
|
163
|
+
if pending_migrations.nil?
|
|
164
|
+
pending_migrations = connection_pool.migration_context.open.pending_migrations
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
super(message || detailed_migration_message(pending_migrations))
|
|
148
168
|
end
|
|
149
169
|
|
|
150
170
|
private
|
|
151
|
-
def detailed_migration_message
|
|
171
|
+
def detailed_migration_message(pending_migrations)
|
|
152
172
|
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)
|
|
173
|
+
message += " RAILS_ENV=#{::Rails.env}" if defined?(Rails.env) && !Rails.env.local?
|
|
154
174
|
message += "\n\n"
|
|
155
175
|
|
|
156
|
-
pending_migrations = ActiveRecord::Base.connection.migration_context.open.pending_migrations
|
|
157
|
-
|
|
158
176
|
message += "You have #{pending_migrations.size} pending #{pending_migrations.size > 1 ? 'migrations:' : 'migration:'}\n\n"
|
|
159
177
|
|
|
160
178
|
pending_migrations.each do |pending_migration|
|
|
161
|
-
message += "#{pending_migration.
|
|
179
|
+
message += "#{pending_migration.filename}\n"
|
|
162
180
|
end
|
|
163
181
|
|
|
164
182
|
message
|
|
165
183
|
end
|
|
184
|
+
|
|
185
|
+
def connection_pool
|
|
186
|
+
ActiveRecord::Tasks::DatabaseTasks.migration_connection_pool
|
|
187
|
+
end
|
|
166
188
|
end
|
|
167
189
|
|
|
168
190
|
class ConcurrentMigrationError < MigrationError # :nodoc:
|
|
@@ -228,7 +250,7 @@ module ActiveRecord
|
|
|
228
250
|
#
|
|
229
251
|
# Example of a simple migration:
|
|
230
252
|
#
|
|
231
|
-
# class AddSsl < ActiveRecord::Migration[7.
|
|
253
|
+
# class AddSsl < ActiveRecord::Migration[7.2]
|
|
232
254
|
# def up
|
|
233
255
|
# add_column :accounts, :ssl_enabled, :boolean, default: true
|
|
234
256
|
# end
|
|
@@ -248,7 +270,7 @@ module ActiveRecord
|
|
|
248
270
|
#
|
|
249
271
|
# Example of a more complex migration that also needs to initialize data:
|
|
250
272
|
#
|
|
251
|
-
# class AddSystemSettings < ActiveRecord::Migration[7.
|
|
273
|
+
# class AddSystemSettings < ActiveRecord::Migration[7.2]
|
|
252
274
|
# def up
|
|
253
275
|
# create_table :system_settings do |t|
|
|
254
276
|
# t.string :name
|
|
@@ -326,7 +348,7 @@ module ActiveRecord
|
|
|
326
348
|
# details.
|
|
327
349
|
# * <tt>change_table(name, options)</tt>: Allows to make column alterations to
|
|
328
350
|
# 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.
|
|
351
|
+
# can then add/remove columns, indexes, or foreign keys to it.
|
|
330
352
|
# * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames
|
|
331
353
|
# a column but keeps the type and content.
|
|
332
354
|
# * <tt>rename_index(table_name, old_name, new_name)</tt>: Renames an index.
|
|
@@ -356,15 +378,16 @@ module ActiveRecord
|
|
|
356
378
|
# == Irreversible transformations
|
|
357
379
|
#
|
|
358
380
|
# Some transformations are destructive in a manner that cannot be reversed.
|
|
359
|
-
# Migrations of that kind should raise an
|
|
381
|
+
# Migrations of that kind should raise an ActiveRecord::IrreversibleMigration
|
|
360
382
|
# exception in their +down+ method.
|
|
361
383
|
#
|
|
362
|
-
# == Running migrations from within Rails
|
|
384
|
+
# == Running migrations from within \Rails
|
|
363
385
|
#
|
|
364
|
-
# The Rails package has several tools to help create and apply migrations.
|
|
386
|
+
# The \Rails package has several tools to help create and apply migrations.
|
|
365
387
|
#
|
|
366
388
|
# To generate a new migration, you can use
|
|
367
|
-
#
|
|
389
|
+
#
|
|
390
|
+
# $ bin/rails generate migration MyNewMigration
|
|
368
391
|
#
|
|
369
392
|
# where MyNewMigration is the name of your migration. The generator will
|
|
370
393
|
# create an empty migration file <tt>timestamp_my_new_migration.rb</tt>
|
|
@@ -373,10 +396,10 @@ module ActiveRecord
|
|
|
373
396
|
#
|
|
374
397
|
# There is a special syntactic shortcut to generate migrations that add fields to a table.
|
|
375
398
|
#
|
|
376
|
-
# bin/rails generate migration add_fieldname_to_tablename fieldname:string
|
|
399
|
+
# $ bin/rails generate migration add_fieldname_to_tablename fieldname:string
|
|
377
400
|
#
|
|
378
401
|
# This will generate the file <tt>timestamp_add_fieldname_to_tablename.rb</tt>, which will look like this:
|
|
379
|
-
# class AddFieldnameToTablename < ActiveRecord::Migration[7.
|
|
402
|
+
# class AddFieldnameToTablename < ActiveRecord::Migration[7.2]
|
|
380
403
|
# def change
|
|
381
404
|
# add_column :tablenames, :fieldname, :string
|
|
382
405
|
# end
|
|
@@ -395,14 +418,14 @@ module ActiveRecord
|
|
|
395
418
|
# wish to rollback last few migrations. <tt>bin/rails db:rollback STEP=2</tt> will rollback
|
|
396
419
|
# the latest two migrations.
|
|
397
420
|
#
|
|
398
|
-
# If any of the migrations throw an
|
|
421
|
+
# If any of the migrations throw an ActiveRecord::IrreversibleMigration exception,
|
|
399
422
|
# that step will fail and you'll have some manual work to do.
|
|
400
423
|
#
|
|
401
424
|
# == More examples
|
|
402
425
|
#
|
|
403
426
|
# Not all migrations change the schema. Some just fix the data:
|
|
404
427
|
#
|
|
405
|
-
# class RemoveEmptyTags < ActiveRecord::Migration[7.
|
|
428
|
+
# class RemoveEmptyTags < ActiveRecord::Migration[7.2]
|
|
406
429
|
# def up
|
|
407
430
|
# Tag.all.each { |tag| tag.destroy if tag.pages.empty? }
|
|
408
431
|
# end
|
|
@@ -415,7 +438,7 @@ module ActiveRecord
|
|
|
415
438
|
#
|
|
416
439
|
# Others remove columns when they migrate up instead of down:
|
|
417
440
|
#
|
|
418
|
-
# class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration[7.
|
|
441
|
+
# class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration[7.2]
|
|
419
442
|
# def up
|
|
420
443
|
# remove_column :items, :incomplete_items_count
|
|
421
444
|
# remove_column :items, :completed_items_count
|
|
@@ -429,7 +452,7 @@ module ActiveRecord
|
|
|
429
452
|
#
|
|
430
453
|
# And sometimes you need to do something in SQL not abstracted directly by migrations:
|
|
431
454
|
#
|
|
432
|
-
# class MakeJoinUnique < ActiveRecord::Migration[7.
|
|
455
|
+
# class MakeJoinUnique < ActiveRecord::Migration[7.2]
|
|
433
456
|
# def up
|
|
434
457
|
# execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)"
|
|
435
458
|
# end
|
|
@@ -446,7 +469,7 @@ module ActiveRecord
|
|
|
446
469
|
# <tt>Base#reset_column_information</tt> in order to ensure that the model has the
|
|
447
470
|
# latest column data from after the new column was added. Example:
|
|
448
471
|
#
|
|
449
|
-
# class AddPeopleSalary < ActiveRecord::Migration[7.
|
|
472
|
+
# class AddPeopleSalary < ActiveRecord::Migration[7.2]
|
|
450
473
|
# def up
|
|
451
474
|
# add_column :people, :salary, :integer
|
|
452
475
|
# Person.reset_column_information
|
|
@@ -462,7 +485,7 @@ module ActiveRecord
|
|
|
462
485
|
# them to the console as they happen, along with benchmarks describing how
|
|
463
486
|
# long each step took.
|
|
464
487
|
#
|
|
465
|
-
# You can quiet them down by setting ActiveRecord::Migration.verbose = false
|
|
488
|
+
# You can quiet them down by setting <tt>ActiveRecord::Migration.verbose = false</tt>.
|
|
466
489
|
#
|
|
467
490
|
# You can also insert your own messages and benchmarks by using the +say_with_time+
|
|
468
491
|
# method:
|
|
@@ -482,11 +505,15 @@ module ActiveRecord
|
|
|
482
505
|
#
|
|
483
506
|
# == Timestamped Migrations
|
|
484
507
|
#
|
|
485
|
-
# By default, Rails generates migrations that look like:
|
|
508
|
+
# By default, \Rails generates migrations that look like:
|
|
486
509
|
#
|
|
487
510
|
# 20080717013526_your_migration_name.rb
|
|
488
511
|
#
|
|
489
|
-
# The prefix is a generation timestamp (in UTC).
|
|
512
|
+
# The prefix is a generation timestamp (in UTC). Timestamps should not be
|
|
513
|
+
# modified manually. To validate that migration timestamps adhere to the
|
|
514
|
+
# format Active Record expects, you can use the following configuration option:
|
|
515
|
+
#
|
|
516
|
+
# config.active_record.validate_migration_timestamps = true
|
|
490
517
|
#
|
|
491
518
|
# If you'd prefer to use numeric prefixes, you can turn timestamped migrations
|
|
492
519
|
# off by setting:
|
|
@@ -504,7 +531,7 @@ module ActiveRecord
|
|
|
504
531
|
# To define a reversible migration, define the +change+ method in your
|
|
505
532
|
# migration like this:
|
|
506
533
|
#
|
|
507
|
-
# class TenderloveMigration < ActiveRecord::Migration[7.
|
|
534
|
+
# class TenderloveMigration < ActiveRecord::Migration[7.2]
|
|
508
535
|
# def change
|
|
509
536
|
# create_table(:horses) do |t|
|
|
510
537
|
# t.column :content, :text
|
|
@@ -521,11 +548,11 @@ module ActiveRecord
|
|
|
521
548
|
# as before.
|
|
522
549
|
#
|
|
523
550
|
# If a command cannot be reversed, an
|
|
524
|
-
#
|
|
551
|
+
# ActiveRecord::IrreversibleMigration exception will be raised when
|
|
525
552
|
# the migration is moving down.
|
|
526
553
|
#
|
|
527
554
|
# For a list of commands that are reversible, please see
|
|
528
|
-
#
|
|
555
|
+
# +ActiveRecord::Migration::CommandRecorder+.
|
|
529
556
|
#
|
|
530
557
|
# == Transactional Migrations
|
|
531
558
|
#
|
|
@@ -534,7 +561,7 @@ module ActiveRecord
|
|
|
534
561
|
# can't execute inside a transaction though, and for these situations
|
|
535
562
|
# you can turn the automatic transactions off.
|
|
536
563
|
#
|
|
537
|
-
# class ChangeEnum < ActiveRecord::Migration[7.
|
|
564
|
+
# class ChangeEnum < ActiveRecord::Migration[7.2]
|
|
538
565
|
# disable_ddl_transaction!
|
|
539
566
|
#
|
|
540
567
|
# def up
|
|
@@ -548,9 +575,46 @@ module ActiveRecord
|
|
|
548
575
|
autoload :CommandRecorder, "active_record/migration/command_recorder"
|
|
549
576
|
autoload :Compatibility, "active_record/migration/compatibility"
|
|
550
577
|
autoload :JoinTable, "active_record/migration/join_table"
|
|
578
|
+
autoload :ExecutionStrategy, "active_record/migration/execution_strategy"
|
|
579
|
+
autoload :DefaultStrategy, "active_record/migration/default_strategy"
|
|
551
580
|
|
|
552
581
|
# This must be defined before the inherited hook, below
|
|
553
582
|
class Current < Migration # :nodoc:
|
|
583
|
+
def create_table(table_name, **options)
|
|
584
|
+
if block_given?
|
|
585
|
+
super { |t| yield compatible_table_definition(t) }
|
|
586
|
+
else
|
|
587
|
+
super
|
|
588
|
+
end
|
|
589
|
+
end
|
|
590
|
+
|
|
591
|
+
def change_table(table_name, **options)
|
|
592
|
+
if block_given?
|
|
593
|
+
super { |t| yield compatible_table_definition(t) }
|
|
594
|
+
else
|
|
595
|
+
super
|
|
596
|
+
end
|
|
597
|
+
end
|
|
598
|
+
|
|
599
|
+
def create_join_table(table_1, table_2, **options)
|
|
600
|
+
if block_given?
|
|
601
|
+
super { |t| yield compatible_table_definition(t) }
|
|
602
|
+
else
|
|
603
|
+
super
|
|
604
|
+
end
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
def drop_table(table_name, **options)
|
|
608
|
+
if block_given?
|
|
609
|
+
super { |t| yield compatible_table_definition(t) }
|
|
610
|
+
else
|
|
611
|
+
super
|
|
612
|
+
end
|
|
613
|
+
end
|
|
614
|
+
|
|
615
|
+
def compatible_table_definition(t)
|
|
616
|
+
t
|
|
617
|
+
end
|
|
554
618
|
end
|
|
555
619
|
|
|
556
620
|
def self.inherited(subclass) # :nodoc:
|
|
@@ -575,8 +639,15 @@ module ActiveRecord
|
|
|
575
639
|
|
|
576
640
|
MigrationFilenameRegexp = /\A([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/ # :nodoc:
|
|
577
641
|
|
|
642
|
+
def self.valid_version_format?(version_string) # :nodoc:
|
|
643
|
+
[
|
|
644
|
+
MigrationFilenameRegexp,
|
|
645
|
+
/\A\d(_?\d)*\z/ # integer with optional underscores
|
|
646
|
+
].any? { |pattern| pattern.match?(version_string) }
|
|
647
|
+
end
|
|
648
|
+
|
|
578
649
|
# This class is used to verify that all migrations have been run before
|
|
579
|
-
# loading a web page if <tt>config.active_record.migration_error</tt> is set to
|
|
650
|
+
# loading a web page if <tt>config.active_record.migration_error</tt> is set to +:page_load+.
|
|
580
651
|
class CheckPending
|
|
581
652
|
def initialize(app, file_watcher: ActiveSupport::FileUpdateChecker)
|
|
582
653
|
@app = app
|
|
@@ -589,7 +660,7 @@ module ActiveRecord
|
|
|
589
660
|
@mutex.synchronize do
|
|
590
661
|
@watcher ||= build_watcher do
|
|
591
662
|
@needs_check = true
|
|
592
|
-
ActiveRecord::Migration.
|
|
663
|
+
ActiveRecord::Migration.check_pending_migrations
|
|
593
664
|
@needs_check = false
|
|
594
665
|
end
|
|
595
666
|
|
|
@@ -605,12 +676,14 @@ module ActiveRecord
|
|
|
605
676
|
|
|
606
677
|
private
|
|
607
678
|
def build_watcher(&block)
|
|
608
|
-
|
|
679
|
+
current_environment = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
|
|
680
|
+
all_configs = ActiveRecord::Base.configurations.configs_for(env_name: current_environment)
|
|
681
|
+
paths = all_configs.flat_map { |config| config.migrations_paths || Migrator.migrations_paths }.uniq
|
|
609
682
|
@file_watcher.new([], paths.index_with(["rb"]), &block)
|
|
610
683
|
end
|
|
611
684
|
|
|
612
685
|
def connection
|
|
613
|
-
ActiveRecord::
|
|
686
|
+
ActiveRecord::Tasks::DatabaseTasks.migration_connection
|
|
614
687
|
end
|
|
615
688
|
end
|
|
616
689
|
|
|
@@ -622,32 +695,36 @@ module ActiveRecord
|
|
|
622
695
|
delegate || superclass.nearest_delegate
|
|
623
696
|
end
|
|
624
697
|
|
|
625
|
-
# Raises
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
698
|
+
# Raises ActiveRecord::PendingMigrationError error if any migrations are pending
|
|
699
|
+
# for all database configurations in an environment.
|
|
700
|
+
def check_all_pending!
|
|
701
|
+
pending_migrations = []
|
|
629
702
|
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
703
|
+
ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(env: env) do |pool|
|
|
704
|
+
if pending = pool.migration_context.open.pending_migrations
|
|
705
|
+
pending_migrations << pending
|
|
706
|
+
end
|
|
707
|
+
end
|
|
633
708
|
|
|
634
|
-
|
|
635
|
-
|
|
709
|
+
migrations = pending_migrations.flatten
|
|
710
|
+
|
|
711
|
+
if migrations.any?
|
|
712
|
+
raise ActiveRecord::PendingMigrationError.new(pending_migrations: migrations)
|
|
636
713
|
end
|
|
714
|
+
end
|
|
637
715
|
|
|
638
|
-
|
|
716
|
+
def load_schema_if_pending!
|
|
717
|
+
if any_schema_needs_update?
|
|
639
718
|
# Roundtrip to Rake to allow plugins to hook into database initialization.
|
|
640
719
|
root = defined?(ENGINE_ROOT) ? ENGINE_ROOT : Rails.root
|
|
720
|
+
|
|
641
721
|
FileUtils.cd(root) do
|
|
642
|
-
Base.clear_all_connections!
|
|
722
|
+
Base.connection_handler.clear_all_connections!(:all)
|
|
643
723
|
system("bin/rails db:test:prepare")
|
|
644
724
|
end
|
|
645
725
|
end
|
|
646
726
|
|
|
647
|
-
|
|
648
|
-
Base.establish_connection(current_db_config)
|
|
649
|
-
|
|
650
|
-
check_pending!
|
|
727
|
+
check_pending_migrations
|
|
651
728
|
end
|
|
652
729
|
|
|
653
730
|
def maintain_test_schema! # :nodoc:
|
|
@@ -656,10 +733,9 @@ module ActiveRecord
|
|
|
656
733
|
end
|
|
657
734
|
end
|
|
658
735
|
|
|
659
|
-
def method_missing(name,
|
|
660
|
-
nearest_delegate.send(name,
|
|
736
|
+
def method_missing(name, ...) # :nodoc:
|
|
737
|
+
nearest_delegate.send(name, ...)
|
|
661
738
|
end
|
|
662
|
-
ruby2_keywords(:method_missing)
|
|
663
739
|
|
|
664
740
|
def migrate(direction)
|
|
665
741
|
new.migrate direction
|
|
@@ -672,12 +748,55 @@ module ActiveRecord
|
|
|
672
748
|
def disable_ddl_transaction!
|
|
673
749
|
@disable_ddl_transaction = true
|
|
674
750
|
end
|
|
751
|
+
|
|
752
|
+
def check_pending_migrations # :nodoc:
|
|
753
|
+
migrations = pending_migrations
|
|
754
|
+
|
|
755
|
+
if migrations.any?
|
|
756
|
+
raise ActiveRecord::PendingMigrationError.new(pending_migrations: migrations)
|
|
757
|
+
end
|
|
758
|
+
end
|
|
759
|
+
|
|
760
|
+
private
|
|
761
|
+
def any_schema_needs_update?
|
|
762
|
+
!db_configs_in_current_env.all? do |db_config|
|
|
763
|
+
Tasks::DatabaseTasks.schema_up_to_date?(db_config, ActiveRecord.schema_format)
|
|
764
|
+
end
|
|
765
|
+
end
|
|
766
|
+
|
|
767
|
+
def db_configs_in_current_env
|
|
768
|
+
ActiveRecord::Base.configurations.configs_for(env_name: env)
|
|
769
|
+
end
|
|
770
|
+
|
|
771
|
+
def pending_migrations
|
|
772
|
+
pending_migrations = []
|
|
773
|
+
|
|
774
|
+
ActiveRecord::Base.configurations.configs_for(env_name: env).each do |db_config|
|
|
775
|
+
ActiveRecord::PendingMigrationConnection.with_temporary_pool(db_config) do |pool|
|
|
776
|
+
if pending = pool.migration_context.open.pending_migrations
|
|
777
|
+
pending_migrations << pending
|
|
778
|
+
end
|
|
779
|
+
end
|
|
780
|
+
end
|
|
781
|
+
|
|
782
|
+
pending_migrations.flatten
|
|
783
|
+
end
|
|
784
|
+
|
|
785
|
+
def env
|
|
786
|
+
ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
|
|
787
|
+
end
|
|
675
788
|
end
|
|
676
789
|
|
|
677
790
|
def disable_ddl_transaction # :nodoc:
|
|
678
791
|
self.class.disable_ddl_transaction
|
|
679
792
|
end
|
|
680
793
|
|
|
794
|
+
##
|
|
795
|
+
# :singleton-method: verbose
|
|
796
|
+
#
|
|
797
|
+
# Specifies if migrations will write the actions they are taking to the console as they
|
|
798
|
+
# happen, along with benchmarks describing how long each step took. Defaults to
|
|
799
|
+
# true.
|
|
681
800
|
cattr_accessor :verbose
|
|
682
801
|
attr_accessor :name, :version
|
|
683
802
|
|
|
@@ -685,6 +804,11 @@ module ActiveRecord
|
|
|
685
804
|
@name = name
|
|
686
805
|
@version = version
|
|
687
806
|
@connection = nil
|
|
807
|
+
@pool = nil
|
|
808
|
+
end
|
|
809
|
+
|
|
810
|
+
def execution_strategy
|
|
811
|
+
@execution_strategy ||= ActiveRecord.migration_strategy.new(self)
|
|
688
812
|
end
|
|
689
813
|
|
|
690
814
|
self.verbose = true
|
|
@@ -698,7 +822,7 @@ module ActiveRecord
|
|
|
698
822
|
# and create the table 'apples' on the way up, and the reverse
|
|
699
823
|
# on the way down.
|
|
700
824
|
#
|
|
701
|
-
# class FixTLMigration < ActiveRecord::Migration[7.
|
|
825
|
+
# class FixTLMigration < ActiveRecord::Migration[7.2]
|
|
702
826
|
# def change
|
|
703
827
|
# revert do
|
|
704
828
|
# create_table(:horses) do |t|
|
|
@@ -717,7 +841,7 @@ module ActiveRecord
|
|
|
717
841
|
#
|
|
718
842
|
# require_relative "20121212123456_tenderlove_migration"
|
|
719
843
|
#
|
|
720
|
-
# class FixupTLMigration < ActiveRecord::Migration[7.
|
|
844
|
+
# class FixupTLMigration < ActiveRecord::Migration[7.2]
|
|
721
845
|
# def change
|
|
722
846
|
# revert TenderloveMigration
|
|
723
847
|
#
|
|
@@ -768,7 +892,7 @@ module ActiveRecord
|
|
|
768
892
|
# when the three columns 'first_name', 'last_name' and 'full_name' exist,
|
|
769
893
|
# even when migrating down:
|
|
770
894
|
#
|
|
771
|
-
# class SplitNameMigration < ActiveRecord::Migration[7.
|
|
895
|
+
# class SplitNameMigration < ActiveRecord::Migration[7.2]
|
|
772
896
|
# def change
|
|
773
897
|
# add_column :users, :first_name, :string
|
|
774
898
|
# add_column :users, :last_name, :string
|
|
@@ -796,7 +920,7 @@ module ActiveRecord
|
|
|
796
920
|
# In the following example, the new column +published+ will be given
|
|
797
921
|
# the value +true+ for all existing records.
|
|
798
922
|
#
|
|
799
|
-
# class AddPublishedToPosts < ActiveRecord::Migration[7.
|
|
923
|
+
# class AddPublishedToPosts < ActiveRecord::Migration[7.2]
|
|
800
924
|
# def change
|
|
801
925
|
# add_column :posts, :published, :boolean, default: false
|
|
802
926
|
# up_only do
|
|
@@ -810,8 +934,9 @@ module ActiveRecord
|
|
|
810
934
|
|
|
811
935
|
# Runs the given migration classes.
|
|
812
936
|
# Last argument can specify options:
|
|
813
|
-
#
|
|
814
|
-
# -
|
|
937
|
+
#
|
|
938
|
+
# - +:direction+ - Default is +:up+.
|
|
939
|
+
# - +:revert+ - Default is +false+.
|
|
815
940
|
def run(*migration_classes)
|
|
816
941
|
opts = migration_classes.extract_options!
|
|
817
942
|
dir = opts[:direction] || :up
|
|
@@ -848,7 +973,7 @@ module ActiveRecord
|
|
|
848
973
|
end
|
|
849
974
|
|
|
850
975
|
time = nil
|
|
851
|
-
ActiveRecord::
|
|
976
|
+
ActiveRecord::Tasks::DatabaseTasks.migration_connection.pool.with_connection do |conn|
|
|
852
977
|
time = Benchmark.measure do
|
|
853
978
|
exec_migration(conn, direction)
|
|
854
979
|
end
|
|
@@ -873,6 +998,7 @@ module ActiveRecord
|
|
|
873
998
|
end
|
|
874
999
|
ensure
|
|
875
1000
|
@connection = nil
|
|
1001
|
+
@execution_strategy = nil
|
|
876
1002
|
end
|
|
877
1003
|
|
|
878
1004
|
def write(text = "")
|
|
@@ -911,13 +1037,15 @@ module ActiveRecord
|
|
|
911
1037
|
end
|
|
912
1038
|
|
|
913
1039
|
def connection
|
|
914
|
-
@connection || ActiveRecord::
|
|
1040
|
+
@connection || ActiveRecord::Tasks::DatabaseTasks.migration_connection
|
|
915
1041
|
end
|
|
916
1042
|
|
|
917
|
-
def
|
|
918
|
-
|
|
1043
|
+
def connection_pool
|
|
1044
|
+
@pool || ActiveRecord::Tasks::DatabaseTasks.migration_connection_pool
|
|
1045
|
+
end
|
|
919
1046
|
|
|
920
|
-
|
|
1047
|
+
def method_missing(method, *arguments, &block)
|
|
1048
|
+
say_with_time "#{method}(#{format_arguments(arguments)})" do
|
|
921
1049
|
unless connection.respond_to? :revert
|
|
922
1050
|
unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method)
|
|
923
1051
|
arguments[0] = proper_table_name(arguments.first, table_name_options)
|
|
@@ -927,22 +1055,23 @@ module ActiveRecord
|
|
|
927
1055
|
end
|
|
928
1056
|
end
|
|
929
1057
|
end
|
|
930
|
-
return super unless
|
|
931
|
-
|
|
1058
|
+
return super unless execution_strategy.respond_to?(method)
|
|
1059
|
+
execution_strategy.send(method, *arguments, &block)
|
|
932
1060
|
end
|
|
933
1061
|
end
|
|
934
1062
|
ruby2_keywords(:method_missing)
|
|
935
1063
|
|
|
936
1064
|
def copy(destination, sources, options = {})
|
|
937
1065
|
copied = []
|
|
938
|
-
schema_migration = options[:schema_migration] || ActiveRecord::SchemaMigration
|
|
939
1066
|
|
|
940
1067
|
FileUtils.mkdir_p(destination) unless File.exist?(destination)
|
|
1068
|
+
schema_migration = SchemaMigration::NullSchemaMigration.new
|
|
1069
|
+
internal_metadata = InternalMetadata::NullInternalMetadata.new
|
|
941
1070
|
|
|
942
|
-
destination_migrations = ActiveRecord::MigrationContext.new(destination, schema_migration).migrations
|
|
1071
|
+
destination_migrations = ActiveRecord::MigrationContext.new(destination, schema_migration, internal_metadata).migrations
|
|
943
1072
|
last = destination_migrations.last
|
|
944
1073
|
sources.each do |scope, path|
|
|
945
|
-
source_migrations = ActiveRecord::MigrationContext.new(path, schema_migration).migrations
|
|
1074
|
+
source_migrations = ActiveRecord::MigrationContext.new(path, schema_migration, internal_metadata).migrations
|
|
946
1075
|
|
|
947
1076
|
source_migrations.each do |migration|
|
|
948
1077
|
source = File.binread(migration.filename)
|
|
@@ -1001,9 +1130,9 @@ module ActiveRecord
|
|
|
1001
1130
|
# Determines the version number of the next migration.
|
|
1002
1131
|
def next_migration_number(number)
|
|
1003
1132
|
if ActiveRecord.timestamped_migrations
|
|
1004
|
-
[Time.now.utc.strftime("%Y%m%d%H%M%S")
|
|
1133
|
+
[Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % number].max
|
|
1005
1134
|
else
|
|
1006
|
-
|
|
1135
|
+
"%.3d" % number.to_i
|
|
1007
1136
|
end
|
|
1008
1137
|
end
|
|
1009
1138
|
|
|
@@ -1025,6 +1154,22 @@ module ActiveRecord
|
|
|
1025
1154
|
end
|
|
1026
1155
|
end
|
|
1027
1156
|
|
|
1157
|
+
def format_arguments(arguments)
|
|
1158
|
+
arg_list = arguments[0...-1].map(&:inspect)
|
|
1159
|
+
last_arg = arguments.last
|
|
1160
|
+
if last_arg.is_a?(Hash)
|
|
1161
|
+
last_arg = last_arg.reject { |k, _v| internal_option?(k) }
|
|
1162
|
+
arg_list << last_arg.inspect unless last_arg.empty?
|
|
1163
|
+
else
|
|
1164
|
+
arg_list << last_arg.inspect
|
|
1165
|
+
end
|
|
1166
|
+
arg_list.join(", ")
|
|
1167
|
+
end
|
|
1168
|
+
|
|
1169
|
+
def internal_option?(option_name)
|
|
1170
|
+
option_name.start_with?("_")
|
|
1171
|
+
end
|
|
1172
|
+
|
|
1028
1173
|
def command_recorder
|
|
1029
1174
|
CommandRecorder.new(connection)
|
|
1030
1175
|
end
|
|
@@ -1057,19 +1202,22 @@ module ActiveRecord
|
|
|
1057
1202
|
end
|
|
1058
1203
|
end
|
|
1059
1204
|
|
|
1205
|
+
# = \Migration \Context
|
|
1206
|
+
#
|
|
1060
1207
|
# MigrationContext sets the context in which a migration is run.
|
|
1061
1208
|
#
|
|
1062
1209
|
# A migration context requires the path to the migrations is set
|
|
1063
1210
|
# in the +migrations_paths+ parameter. Optionally a +schema_migration+
|
|
1064
|
-
# class can be provided.
|
|
1065
|
-
#
|
|
1066
|
-
#
|
|
1211
|
+
# class can be provided. Multiple database applications will instantiate
|
|
1212
|
+
# a +SchemaMigration+ object per database. From the Rake tasks, \Rails will
|
|
1213
|
+
# handle this for you.
|
|
1067
1214
|
class MigrationContext
|
|
1068
|
-
attr_reader :migrations_paths, :schema_migration
|
|
1215
|
+
attr_reader :migrations_paths, :schema_migration, :internal_metadata
|
|
1069
1216
|
|
|
1070
|
-
def initialize(migrations_paths, schema_migration =
|
|
1217
|
+
def initialize(migrations_paths, schema_migration = nil, internal_metadata = nil)
|
|
1071
1218
|
@migrations_paths = migrations_paths
|
|
1072
|
-
@schema_migration = schema_migration
|
|
1219
|
+
@schema_migration = schema_migration || SchemaMigration.new(connection_pool)
|
|
1220
|
+
@internal_metadata = internal_metadata || InternalMetadata.new(connection_pool)
|
|
1073
1221
|
end
|
|
1074
1222
|
|
|
1075
1223
|
# Runs the migrations in the +migrations_path+.
|
|
@@ -1113,7 +1261,7 @@ module ActiveRecord
|
|
|
1113
1261
|
migrations
|
|
1114
1262
|
end
|
|
1115
1263
|
|
|
1116
|
-
Migrator.new(:up, selected_migrations, schema_migration, target_version).migrate
|
|
1264
|
+
Migrator.new(:up, selected_migrations, schema_migration, internal_metadata, target_version).migrate
|
|
1117
1265
|
end
|
|
1118
1266
|
|
|
1119
1267
|
def down(target_version = nil, &block) # :nodoc:
|
|
@@ -1123,20 +1271,20 @@ module ActiveRecord
|
|
|
1123
1271
|
migrations
|
|
1124
1272
|
end
|
|
1125
1273
|
|
|
1126
|
-
Migrator.new(:down, selected_migrations, schema_migration, target_version).migrate
|
|
1274
|
+
Migrator.new(:down, selected_migrations, schema_migration, internal_metadata, target_version).migrate
|
|
1127
1275
|
end
|
|
1128
1276
|
|
|
1129
1277
|
def run(direction, target_version) # :nodoc:
|
|
1130
|
-
Migrator.new(direction, migrations, schema_migration, target_version).run
|
|
1278
|
+
Migrator.new(direction, migrations, schema_migration, internal_metadata, target_version).run
|
|
1131
1279
|
end
|
|
1132
1280
|
|
|
1133
1281
|
def open # :nodoc:
|
|
1134
|
-
Migrator.new(:up, migrations, schema_migration)
|
|
1282
|
+
Migrator.new(:up, migrations, schema_migration, internal_metadata)
|
|
1135
1283
|
end
|
|
1136
1284
|
|
|
1137
1285
|
def get_all_versions # :nodoc:
|
|
1138
1286
|
if schema_migration.table_exists?
|
|
1139
|
-
schema_migration.
|
|
1287
|
+
schema_migration.integer_versions
|
|
1140
1288
|
else
|
|
1141
1289
|
[]
|
|
1142
1290
|
end
|
|
@@ -1159,6 +1307,9 @@ module ActiveRecord
|
|
|
1159
1307
|
migrations = migration_files.map do |file|
|
|
1160
1308
|
version, name, scope = parse_migration_filename(file)
|
|
1161
1309
|
raise IllegalMigrationNameError.new(file) unless version
|
|
1310
|
+
if validate_timestamp? && !valid_migration_timestamp?(version)
|
|
1311
|
+
raise InvalidMigrationTimestampError.new(version, name)
|
|
1312
|
+
end
|
|
1162
1313
|
version = version.to_i
|
|
1163
1314
|
name = name.camelize
|
|
1164
1315
|
|
|
@@ -1174,6 +1325,9 @@ module ActiveRecord
|
|
|
1174
1325
|
file_list = migration_files.filter_map do |file|
|
|
1175
1326
|
version, name, scope = parse_migration_filename(file)
|
|
1176
1327
|
raise IllegalMigrationNameError.new(file) unless version
|
|
1328
|
+
if validate_timestamp? && !valid_migration_timestamp?(version)
|
|
1329
|
+
raise InvalidMigrationTimestampError.new(version, name)
|
|
1330
|
+
end
|
|
1177
1331
|
version = schema_migration.normalize_migration_number(version)
|
|
1178
1332
|
status = db_list.delete(version) ? "up" : "down"
|
|
1179
1333
|
[status, version, (name + scope).humanize]
|
|
@@ -1195,16 +1349,25 @@ module ActiveRecord
|
|
|
1195
1349
|
end
|
|
1196
1350
|
|
|
1197
1351
|
def last_stored_environment # :nodoc:
|
|
1198
|
-
|
|
1352
|
+
internal_metadata = connection_pool.internal_metadata
|
|
1353
|
+
return nil unless internal_metadata.enabled?
|
|
1199
1354
|
return nil if current_version == 0
|
|
1200
|
-
raise NoEnvironmentInSchemaError unless
|
|
1355
|
+
raise NoEnvironmentInSchemaError unless internal_metadata.table_exists?
|
|
1201
1356
|
|
|
1202
|
-
environment =
|
|
1357
|
+
environment = internal_metadata[:environment]
|
|
1203
1358
|
raise NoEnvironmentInSchemaError unless environment
|
|
1204
1359
|
environment
|
|
1205
1360
|
end
|
|
1206
1361
|
|
|
1207
1362
|
private
|
|
1363
|
+
def connection
|
|
1364
|
+
ActiveRecord::Tasks::DatabaseTasks.migration_connection
|
|
1365
|
+
end
|
|
1366
|
+
|
|
1367
|
+
def connection_pool
|
|
1368
|
+
ActiveRecord::Tasks::DatabaseTasks.migration_connection_pool
|
|
1369
|
+
end
|
|
1370
|
+
|
|
1208
1371
|
def migration_files
|
|
1209
1372
|
paths = Array(migrations_paths)
|
|
1210
1373
|
Dir[*paths.flat_map { |path| "#{path}/**/[0-9]*_*.rb" }]
|
|
@@ -1214,8 +1377,16 @@ module ActiveRecord
|
|
|
1214
1377
|
File.basename(filename).scan(Migration::MigrationFilenameRegexp).first
|
|
1215
1378
|
end
|
|
1216
1379
|
|
|
1380
|
+
def validate_timestamp?
|
|
1381
|
+
ActiveRecord.timestamped_migrations && ActiveRecord.validate_migration_timestamps
|
|
1382
|
+
end
|
|
1383
|
+
|
|
1384
|
+
def valid_migration_timestamp?(version)
|
|
1385
|
+
version.to_i < (Time.now.utc + 1.day).strftime("%Y%m%d%H%M%S").to_i
|
|
1386
|
+
end
|
|
1387
|
+
|
|
1217
1388
|
def move(direction, steps)
|
|
1218
|
-
migrator = Migrator.new(direction, migrations, schema_migration)
|
|
1389
|
+
migrator = Migrator.new(direction, migrations, schema_migration, internal_metadata)
|
|
1219
1390
|
|
|
1220
1391
|
if current_version != 0 && !migrator.current_migration
|
|
1221
1392
|
raise UnknownMigrationVersionError.new(current_version)
|
|
@@ -1240,23 +1411,28 @@ module ActiveRecord
|
|
|
1240
1411
|
|
|
1241
1412
|
# For cases where a table doesn't exist like loading from schema cache
|
|
1242
1413
|
def current_version
|
|
1243
|
-
|
|
1414
|
+
connection_pool = ActiveRecord::Tasks::DatabaseTasks.migration_connection_pool
|
|
1415
|
+
schema_migration = SchemaMigration.new(connection_pool)
|
|
1416
|
+
internal_metadata = InternalMetadata.new(connection_pool)
|
|
1417
|
+
|
|
1418
|
+
MigrationContext.new(migrations_paths, schema_migration, internal_metadata).current_version
|
|
1244
1419
|
end
|
|
1245
1420
|
end
|
|
1246
1421
|
|
|
1247
1422
|
self.migrations_paths = ["db/migrate"]
|
|
1248
1423
|
|
|
1249
|
-
def initialize(direction, migrations, schema_migration, target_version = nil)
|
|
1424
|
+
def initialize(direction, migrations, schema_migration, internal_metadata, target_version = nil)
|
|
1250
1425
|
@direction = direction
|
|
1251
1426
|
@target_version = target_version
|
|
1252
1427
|
@migrated_versions = nil
|
|
1253
1428
|
@migrations = migrations
|
|
1254
1429
|
@schema_migration = schema_migration
|
|
1430
|
+
@internal_metadata = internal_metadata
|
|
1255
1431
|
|
|
1256
1432
|
validate(@migrations)
|
|
1257
1433
|
|
|
1258
1434
|
@schema_migration.create_table
|
|
1259
|
-
|
|
1435
|
+
@internal_metadata.create_table
|
|
1260
1436
|
end
|
|
1261
1437
|
|
|
1262
1438
|
def current_version
|
|
@@ -1309,18 +1485,21 @@ module ActiveRecord
|
|
|
1309
1485
|
end
|
|
1310
1486
|
|
|
1311
1487
|
def load_migrated
|
|
1312
|
-
@migrated_versions = Set.new(@schema_migration.
|
|
1488
|
+
@migrated_versions = Set.new(@schema_migration.integer_versions)
|
|
1313
1489
|
end
|
|
1314
1490
|
|
|
1315
1491
|
private
|
|
1492
|
+
def connection
|
|
1493
|
+
ActiveRecord::Tasks::DatabaseTasks.migration_connection
|
|
1494
|
+
end
|
|
1495
|
+
|
|
1316
1496
|
# Used for running a specific migration.
|
|
1317
1497
|
def run_without_lock
|
|
1318
1498
|
migration = migrations.detect { |m| m.version == @target_version }
|
|
1319
1499
|
raise UnknownMigrationVersionError.new(@target_version) if migration.nil?
|
|
1320
|
-
result = execute_migration_in_transaction(migration)
|
|
1321
1500
|
|
|
1322
1501
|
record_environment
|
|
1323
|
-
|
|
1502
|
+
execute_migration_in_transaction(migration)
|
|
1324
1503
|
end
|
|
1325
1504
|
|
|
1326
1505
|
# Used for running multiple migrations up to or down to a certain value.
|
|
@@ -1329,15 +1508,15 @@ module ActiveRecord
|
|
|
1329
1508
|
raise UnknownMigrationVersionError.new(@target_version)
|
|
1330
1509
|
end
|
|
1331
1510
|
|
|
1332
|
-
result = runnable.each(&method(:execute_migration_in_transaction))
|
|
1333
1511
|
record_environment
|
|
1334
|
-
|
|
1512
|
+
runnable.each(&method(:execute_migration_in_transaction))
|
|
1335
1513
|
end
|
|
1336
1514
|
|
|
1337
1515
|
# Stores the current environment in the database.
|
|
1338
1516
|
def record_environment
|
|
1339
1517
|
return if down?
|
|
1340
|
-
|
|
1518
|
+
|
|
1519
|
+
@internal_metadata[:environment] = connection.pool.db_config.env_name
|
|
1341
1520
|
end
|
|
1342
1521
|
|
|
1343
1522
|
def ran?(migration)
|
|
@@ -1389,10 +1568,10 @@ module ActiveRecord
|
|
|
1389
1568
|
def record_version_state_after_migrating(version)
|
|
1390
1569
|
if down?
|
|
1391
1570
|
migrated.delete(version)
|
|
1392
|
-
@schema_migration.
|
|
1571
|
+
@schema_migration.delete_version(version.to_s)
|
|
1393
1572
|
else
|
|
1394
1573
|
migrated << version
|
|
1395
|
-
@schema_migration.
|
|
1574
|
+
@schema_migration.create_version(version.to_s)
|
|
1396
1575
|
end
|
|
1397
1576
|
end
|
|
1398
1577
|
|
|
@@ -1407,50 +1586,38 @@ module ActiveRecord
|
|
|
1407
1586
|
# Wrap the migration in a transaction only if supported by the adapter.
|
|
1408
1587
|
def ddl_transaction(migration, &block)
|
|
1409
1588
|
if use_transaction?(migration)
|
|
1410
|
-
|
|
1589
|
+
connection.transaction(&block)
|
|
1411
1590
|
else
|
|
1412
1591
|
yield
|
|
1413
1592
|
end
|
|
1414
1593
|
end
|
|
1415
1594
|
|
|
1416
1595
|
def use_transaction?(migration)
|
|
1417
|
-
!migration.disable_ddl_transaction &&
|
|
1596
|
+
!migration.disable_ddl_transaction && connection.supports_ddl_transactions?
|
|
1418
1597
|
end
|
|
1419
1598
|
|
|
1420
1599
|
def use_advisory_lock?
|
|
1421
|
-
|
|
1600
|
+
connection.advisory_locks_enabled?
|
|
1422
1601
|
end
|
|
1423
1602
|
|
|
1424
1603
|
def with_advisory_lock
|
|
1425
1604
|
lock_id = generate_migrator_advisory_lock_id
|
|
1426
1605
|
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
yield
|
|
1432
|
-
ensure
|
|
1433
|
-
if got_lock && !connection.release_advisory_lock(lock_id)
|
|
1434
|
-
raise ConcurrentMigrationError.new(
|
|
1435
|
-
ConcurrentMigrationError::RELEASE_LOCK_FAILED_MESSAGE
|
|
1436
|
-
)
|
|
1437
|
-
end
|
|
1438
|
-
end
|
|
1439
|
-
end
|
|
1440
|
-
|
|
1441
|
-
def with_advisory_lock_connection(&block)
|
|
1442
|
-
pool = ActiveRecord::ConnectionAdapters::ConnectionHandler.new.establish_connection(
|
|
1443
|
-
ActiveRecord::Base.connection_db_config
|
|
1444
|
-
)
|
|
1445
|
-
|
|
1446
|
-
pool.with_connection(&block)
|
|
1606
|
+
got_lock = connection.get_advisory_lock(lock_id)
|
|
1607
|
+
raise ConcurrentMigrationError unless got_lock
|
|
1608
|
+
load_migrated # reload schema_migrations to be sure it wasn't changed by another process before we got the lock
|
|
1609
|
+
yield
|
|
1447
1610
|
ensure
|
|
1448
|
-
|
|
1611
|
+
if got_lock && !connection.release_advisory_lock(lock_id)
|
|
1612
|
+
raise ConcurrentMigrationError.new(
|
|
1613
|
+
ConcurrentMigrationError::RELEASE_LOCK_FAILED_MESSAGE
|
|
1614
|
+
)
|
|
1615
|
+
end
|
|
1449
1616
|
end
|
|
1450
1617
|
|
|
1451
1618
|
MIGRATOR_SALT = 2053462845
|
|
1452
1619
|
def generate_migrator_advisory_lock_id
|
|
1453
|
-
db_name_hash = Zlib.crc32(
|
|
1620
|
+
db_name_hash = Zlib.crc32(connection.current_database)
|
|
1454
1621
|
MIGRATOR_SALT * db_name_hash
|
|
1455
1622
|
end
|
|
1456
1623
|
end
|