activerecord 6.0.3.4 → 6.1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +799 -713
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -2
- data/lib/active_record.rb +7 -14
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/association_relation.rb +22 -14
- data/lib/active_record/associations.rb +114 -11
- data/lib/active_record/associations/alias_tracker.rb +19 -15
- data/lib/active_record/associations/association.rb +44 -28
- data/lib/active_record/associations/association_scope.rb +17 -15
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +9 -3
- data/lib/active_record/associations/builder/belongs_to.rb +10 -7
- data/lib/active_record/associations/builder/collection_association.rb +5 -4
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
- data/lib/active_record/associations/builder/has_many.rb +6 -2
- data/lib/active_record/associations/builder/has_one.rb +11 -14
- data/lib/active_record/associations/builder/singular_association.rb +1 -1
- data/lib/active_record/associations/collection_association.rb +19 -6
- data/lib/active_record/associations/collection_proxy.rb +13 -5
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +24 -2
- data/lib/active_record/associations/has_many_through_association.rb +10 -4
- data/lib/active_record/associations/has_one_association.rb +15 -1
- data/lib/active_record/associations/join_dependency.rb +72 -50
- data/lib/active_record/associations/join_dependency/join_association.rb +36 -14
- data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
- data/lib/active_record/associations/preloader.rb +11 -5
- data/lib/active_record/associations/preloader/association.rb +51 -25
- data/lib/active_record/associations/preloader/through_association.rb +2 -2
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/attribute_assignment.rb +10 -8
- data/lib/active_record/attribute_methods.rb +64 -54
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
- data/lib/active_record/attribute_methods/dirty.rb +1 -11
- data/lib/active_record/attribute_methods/primary_key.rb +6 -2
- data/lib/active_record/attribute_methods/query.rb +3 -6
- data/lib/active_record/attribute_methods/read.rb +8 -11
- data/lib/active_record/attribute_methods/serialization.rb +11 -5
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
- data/lib/active_record/attribute_methods/write.rb +12 -20
- data/lib/active_record/attributes.rb +32 -7
- data/lib/active_record/autosave_association.rb +57 -40
- data/lib/active_record/base.rb +2 -14
- data/lib/active_record/callbacks.rb +152 -22
- data/lib/active_record/coders/yaml_column.rb +1 -1
- data/lib/active_record/connection_adapters.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +191 -134
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -22
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -7
- data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +112 -27
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +224 -85
- data/lib/active_record/connection_adapters/abstract/transaction.rb +80 -32
- data/lib/active_record/connection_adapters/abstract_adapter.rb +54 -71
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +129 -88
- data/lib/active_record/connection_adapters/column.rb +15 -1
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -25
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -6
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +11 -7
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
- data/lib/active_record/connection_adapters/pool_config.rb +63 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +13 -54
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -5
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +72 -55
- data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +31 -6
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +37 -4
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +49 -50
- data/lib/active_record/connection_handling.rb +210 -71
- data/lib/active_record/core.rb +229 -63
- data/lib/active_record/database_configurations.rb +124 -85
- data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
- data/lib/active_record/database_configurations/database_config.rb +52 -9
- data/lib/active_record/database_configurations/hash_config.rb +54 -8
- data/lib/active_record/database_configurations/url_config.rb +15 -40
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/enum.rb +40 -16
- data/lib/active_record/errors.rb +47 -12
- data/lib/active_record/explain.rb +9 -4
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +10 -17
- data/lib/active_record/fixture_set/model_metadata.rb +1 -2
- data/lib/active_record/fixture_set/render_context.rb +1 -1
- data/lib/active_record/fixture_set/table_row.rb +2 -2
- data/lib/active_record/fixtures.rb +54 -8
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +40 -18
- data/lib/active_record/insert_all.rb +35 -6
- data/lib/active_record/integration.rb +3 -5
- data/lib/active_record/internal_metadata.rb +16 -7
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +22 -16
- data/lib/active_record/locking/pessimistic.rb +6 -2
- data/lib/active_record/log_subscriber.rb +26 -8
- data/lib/active_record/middleware/database_selector.rb +4 -1
- data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/migration.rb +113 -83
- data/lib/active_record/migration/command_recorder.rb +47 -27
- data/lib/active_record/migration/compatibility.rb +67 -17
- data/lib/active_record/model_schema.rb +117 -13
- data/lib/active_record/nested_attributes.rb +2 -3
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +50 -45
- data/lib/active_record/query_cache.rb +15 -5
- data/lib/active_record/querying.rb +11 -6
- data/lib/active_record/railtie.rb +64 -44
- data/lib/active_record/railties/databases.rake +266 -95
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +71 -57
- data/lib/active_record/relation.rb +96 -67
- data/lib/active_record/relation/batches.rb +38 -31
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/calculations.rb +101 -44
- data/lib/active_record/relation/delegation.rb +2 -1
- data/lib/active_record/relation/finder_methods.rb +45 -15
- data/lib/active_record/relation/from_clause.rb +1 -1
- data/lib/active_record/relation/merger.rb +27 -25
- data/lib/active_record/relation/predicate_builder.rb +57 -33
- data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +330 -195
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +8 -7
- data/lib/active_record/relation/where_clause.rb +104 -57
- data/lib/active_record/result.rb +41 -33
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +6 -17
- data/lib/active_record/schema_dumper.rb +34 -4
- data/lib/active_record/schema_migration.rb +2 -8
- data/lib/active_record/scoping/named.rb +6 -17
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +5 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +20 -4
- data/lib/active_record/store.rb +2 -2
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +39 -51
- data/lib/active_record/tasks/database_tasks.rb +139 -113
- data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
- data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +37 -16
- data/lib/active_record/timestamp.rb +4 -6
- data/lib/active_record/touch_later.rb +21 -21
- data/lib/active_record/transactions.rb +15 -64
- data/lib/active_record/type.rb +8 -1
- data/lib/active_record/type/serialized.rb +6 -2
- data/lib/active_record/type/time.rb +10 -0
- data/lib/active_record/type_caster/connection.rb +0 -1
- data/lib/active_record/type_caster/map.rb +8 -5
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +24 -4
- data/lib/arel.rb +5 -13
- data/lib/arel/attributes/attribute.rb +4 -0
- data/lib/arel/collectors/bind.rb +5 -0
- data/lib/arel/collectors/composite.rb +8 -0
- data/lib/arel/collectors/sql_string.rb +7 -0
- data/lib/arel/collectors/substitute_binds.rb +7 -0
- data/lib/arel/nodes.rb +3 -1
- data/lib/arel/nodes/binary.rb +82 -8
- data/lib/arel/nodes/bind_param.rb +8 -0
- data/lib/arel/nodes/casted.rb +21 -9
- data/lib/arel/nodes/equality.rb +6 -9
- data/lib/arel/nodes/grouping.rb +3 -0
- data/lib/arel/nodes/homogeneous_in.rb +72 -0
- data/lib/arel/nodes/in.rb +8 -1
- data/lib/arel/nodes/infix_operation.rb +13 -1
- data/lib/arel/nodes/join_source.rb +1 -1
- data/lib/arel/nodes/node.rb +7 -6
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/sql_literal.rb +3 -0
- data/lib/arel/nodes/table_alias.rb +7 -3
- data/lib/arel/nodes/unary.rb +0 -1
- data/lib/arel/predications.rb +12 -18
- data/lib/arel/select_manager.rb +1 -2
- data/lib/arel/table.rb +13 -5
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel/visitors/dot.rb +14 -2
- data/lib/arel/visitors/mysql.rb +11 -1
- data/lib/arel/visitors/postgresql.rb +15 -4
- data/lib/arel/visitors/to_sql.rb +89 -78
- data/lib/rails/generators/active_record/migration.rb +6 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
- data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- metadata +27 -28
- data/lib/active_record/advisory_lock_base.rb +0 -18
- data/lib/active_record/attribute_decorators.rb +0 -88
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
- data/lib/active_record/relation/where_clause_factory.rb +0 -33
- data/lib/arel/attributes.rb +0 -22
- data/lib/arel/visitors/depth_first.rb +0 -203
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -156
- data/lib/arel/visitors/oracle.rb +0 -158
- data/lib/arel/visitors/oracle12.rb +0 -65
- data/lib/arel/visitors/where_sql.rb +0 -22
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/enumerable"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
class InsertAll # :nodoc:
|
5
7
|
attr_reader :model, :connection, :inserts, :keys
|
@@ -8,13 +10,19 @@ module ActiveRecord
|
|
8
10
|
def initialize(model, inserts, on_duplicate:, returning: nil, unique_by: nil)
|
9
11
|
raise ArgumentError, "Empty list of attributes passed" if inserts.blank?
|
10
12
|
|
11
|
-
@model, @connection, @inserts, @keys = model, model.connection, inserts, inserts.first.keys.map(&:to_s)
|
13
|
+
@model, @connection, @inserts, @keys = model, model.connection, inserts, inserts.first.keys.map(&:to_s)
|
12
14
|
@on_duplicate, @returning, @unique_by = on_duplicate, returning, unique_by
|
13
15
|
|
16
|
+
if model.scope_attributes?
|
17
|
+
@scope_attributes = model.scope_attributes
|
18
|
+
@keys |= @scope_attributes.keys
|
19
|
+
end
|
20
|
+
@keys = @keys.to_set
|
21
|
+
|
14
22
|
@returning = (connection.supports_insert_returning? ? primary_keys : false) if @returning.nil?
|
15
23
|
@returning = false if @returning == []
|
16
24
|
|
17
|
-
@unique_by = find_unique_index_for(unique_by)
|
25
|
+
@unique_by = find_unique_index_for(unique_by)
|
18
26
|
@on_duplicate = :skip if @on_duplicate == :update && updatable_columns.empty?
|
19
27
|
|
20
28
|
ensure_valid_options_for_connection!
|
@@ -32,7 +40,7 @@ module ActiveRecord
|
|
32
40
|
end
|
33
41
|
|
34
42
|
def primary_keys
|
35
|
-
Array(model.
|
43
|
+
Array(connection.schema_cache.primary_keys(model.table_name))
|
36
44
|
end
|
37
45
|
|
38
46
|
|
@@ -47,6 +55,8 @@ module ActiveRecord
|
|
47
55
|
def map_key_with_value
|
48
56
|
inserts.map do |attributes|
|
49
57
|
attributes = attributes.stringify_keys
|
58
|
+
attributes.merge!(scope_attributes) if scope_attributes
|
59
|
+
|
50
60
|
verify_attributes(attributes)
|
51
61
|
|
52
62
|
keys.map do |key|
|
@@ -56,13 +66,20 @@ module ActiveRecord
|
|
56
66
|
end
|
57
67
|
|
58
68
|
private
|
69
|
+
attr_reader :scope_attributes
|
70
|
+
|
59
71
|
def find_unique_index_for(unique_by)
|
60
|
-
|
72
|
+
return unique_by if !connection.supports_insert_conflict_target?
|
73
|
+
|
74
|
+
name_or_columns = unique_by || model.primary_key
|
75
|
+
match = Array(name_or_columns).map(&:to_s)
|
61
76
|
|
62
77
|
if index = unique_indexes.find { |i| match.include?(i.name) || i.columns == match }
|
63
78
|
index
|
79
|
+
elsif match == primary_keys
|
80
|
+
unique_by.nil? ? nil : ActiveRecord::ConnectionAdapters::IndexDefinition.new(model.table_name, "#{model.table_name}_primary_key", true, match)
|
64
81
|
else
|
65
|
-
raise ArgumentError, "No unique index found for #{
|
82
|
+
raise ArgumentError, "No unique index found for #{name_or_columns}"
|
66
83
|
end
|
67
84
|
end
|
68
85
|
|
@@ -151,9 +168,21 @@ module ActiveRecord
|
|
151
168
|
quote_columns(insert_all.updatable_columns)
|
152
169
|
end
|
153
170
|
|
171
|
+
def touch_model_timestamps_unless(&block)
|
172
|
+
model.send(:timestamp_attributes_for_update_in_model).map do |column_name|
|
173
|
+
if touch_timestamp_attribute?(column_name)
|
174
|
+
"#{column_name}=(CASE WHEN (#{updatable_columns.map(&block).join(" AND ")}) THEN #{model.quoted_table_name}.#{column_name} ELSE CURRENT_TIMESTAMP END),"
|
175
|
+
end
|
176
|
+
end.compact.join
|
177
|
+
end
|
178
|
+
|
154
179
|
private
|
155
180
|
attr_reader :connection, :insert_all
|
156
181
|
|
182
|
+
def touch_timestamp_attribute?(column_name)
|
183
|
+
update_duplicates? && !insert_all.updatable_columns.include?(column_name)
|
184
|
+
end
|
185
|
+
|
157
186
|
def columns_list
|
158
187
|
format_columns(insert_all.keys)
|
159
188
|
end
|
@@ -168,7 +197,7 @@ module ActiveRecord
|
|
168
197
|
end
|
169
198
|
|
170
199
|
def format_columns(columns)
|
171
|
-
quote_columns(columns).join(",")
|
200
|
+
columns.respond_to?(:map) ? quote_columns(columns).join(",") : columns
|
172
201
|
end
|
173
202
|
|
174
203
|
def quote_columns(columns)
|
@@ -93,7 +93,7 @@ module ActiveRecord
|
|
93
93
|
# cache_version, but this method can be overwritten to return something else.
|
94
94
|
#
|
95
95
|
# Note, this method will return nil if ActiveRecord::Base.cache_versioning is set to
|
96
|
-
# +false
|
96
|
+
# +false+.
|
97
97
|
def cache_version
|
98
98
|
return unless cache_versioning
|
99
99
|
|
@@ -104,10 +104,8 @@ module ActiveRecord
|
|
104
104
|
elsif timestamp = updated_at
|
105
105
|
timestamp.utc.to_s(cache_timestamp_format)
|
106
106
|
end
|
107
|
-
|
108
|
-
|
109
|
-
raise ActiveModel::MissingAttributeError, "missing attribute: updated_at"
|
110
|
-
end
|
107
|
+
elsif self.class.has_attribute?("updated_at")
|
108
|
+
raise ActiveModel::MissingAttributeError, "missing attribute: updated_at"
|
111
109
|
end
|
112
110
|
end
|
113
111
|
|
@@ -6,8 +6,15 @@ require "active_record/scoping/named"
|
|
6
6
|
module ActiveRecord
|
7
7
|
# This class is used to create a table that keeps track of values and keys such
|
8
8
|
# as which environment migrations were run in.
|
9
|
+
#
|
10
|
+
# This is enabled by default. To disable this functionality set
|
11
|
+
# `use_metadata_table` to false in your database configuration.
|
9
12
|
class InternalMetadata < ActiveRecord::Base # :nodoc:
|
10
13
|
class << self
|
14
|
+
def enabled?
|
15
|
+
ActiveRecord::Base.connection.use_metadata_table?
|
16
|
+
end
|
17
|
+
|
11
18
|
def _internal?
|
12
19
|
true
|
13
20
|
end
|
@@ -21,24 +28,24 @@ module ActiveRecord
|
|
21
28
|
end
|
22
29
|
|
23
30
|
def []=(key, value)
|
31
|
+
return unless enabled?
|
32
|
+
|
24
33
|
find_or_initialize_by(key: key).update!(value: value)
|
25
34
|
end
|
26
35
|
|
27
36
|
def [](key)
|
28
|
-
|
29
|
-
end
|
37
|
+
return unless enabled?
|
30
38
|
|
31
|
-
|
32
|
-
connection.table_exists?(table_name)
|
39
|
+
where(key: key).pluck(:value).first
|
33
40
|
end
|
34
41
|
|
35
42
|
# Creates an internal metadata table with columns +key+ and +value+
|
36
43
|
def create_table
|
37
|
-
unless
|
38
|
-
key_options = connection.internal_string_options_for_primary_key
|
44
|
+
return unless enabled?
|
39
45
|
|
46
|
+
unless connection.table_exists?(table_name)
|
40
47
|
connection.create_table(table_name, id: false) do |t|
|
41
|
-
t.string :key, **
|
48
|
+
t.string :key, **connection.internal_string_options_for_primary_key
|
42
49
|
t.string :value
|
43
50
|
t.timestamps
|
44
51
|
end
|
@@ -46,6 +53,8 @@ module ActiveRecord
|
|
46
53
|
end
|
47
54
|
|
48
55
|
def drop_table
|
56
|
+
return unless enabled?
|
57
|
+
|
49
58
|
connection.drop_table table_name, if_exists: true
|
50
59
|
end
|
51
60
|
end
|
@@ -1,13 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
|
-
module LegacyYamlAdapter
|
4
|
+
module LegacyYamlAdapter # :nodoc:
|
5
5
|
def self.convert(klass, coder)
|
6
6
|
return coder unless coder.is_a?(Psych::Coder)
|
7
7
|
|
8
8
|
case coder["active_record_yaml_version"]
|
9
9
|
when 1, 2 then coder
|
10
10
|
else
|
11
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
12
|
+
YAML loading from legacy format older than Rails 5.0 is deprecated
|
13
|
+
and will be removed in Rails 6.2.
|
14
|
+
MSG
|
11
15
|
if coder["attributes"].is_a?(ActiveModel::AttributeSet)
|
12
16
|
Rails420.convert(klass, coder)
|
13
17
|
else
|
@@ -16,7 +20,7 @@ module ActiveRecord
|
|
16
20
|
end
|
17
21
|
end
|
18
22
|
|
19
|
-
module Rails420
|
23
|
+
module Rails420 # :nodoc:
|
20
24
|
def self.convert(klass, coder)
|
21
25
|
attribute_set = coder["attributes"]
|
22
26
|
|
@@ -32,7 +36,7 @@ module ActiveRecord
|
|
32
36
|
end
|
33
37
|
end
|
34
38
|
|
35
|
-
module Rails41
|
39
|
+
module Rails41 # :nodoc:
|
36
40
|
def self.convert(klass, coder)
|
37
41
|
attributes = klass.attributes_builder
|
38
42
|
.build_from_database(coder["attributes"])
|
@@ -60,6 +60,15 @@ module ActiveRecord
|
|
60
60
|
self.class.locking_enabled?
|
61
61
|
end
|
62
62
|
|
63
|
+
def increment!(*, **) #:nodoc:
|
64
|
+
super.tap do
|
65
|
+
if locking_enabled?
|
66
|
+
self[self.class.locking_column] += 1
|
67
|
+
clear_attribute_change(self.class.locking_column)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
63
72
|
private
|
64
73
|
def _create_record(attribute_names = self.attribute_names)
|
65
74
|
if locking_enabled?
|
@@ -80,7 +89,8 @@ module ActiveRecord
|
|
80
89
|
|
81
90
|
begin
|
82
91
|
locking_column = self.class.locking_column
|
83
|
-
previous_lock_value =
|
92
|
+
previous_lock_value = attribute_before_type_cast(locking_column)
|
93
|
+
attribute_names = attribute_names.dup if attribute_names.frozen?
|
84
94
|
attribute_names << locking_column
|
85
95
|
|
86
96
|
self[locking_column] += 1
|
@@ -88,7 +98,7 @@ module ActiveRecord
|
|
88
98
|
affected_rows = self.class._update_record(
|
89
99
|
attributes_with_values(attribute_names),
|
90
100
|
@primary_key => id_in_database,
|
91
|
-
locking_column =>
|
101
|
+
locking_column => @attributes[locking_column].original_value_for_database
|
92
102
|
)
|
93
103
|
|
94
104
|
if affected_rows != 1
|
@@ -111,7 +121,7 @@ module ActiveRecord
|
|
111
121
|
|
112
122
|
affected_rows = self.class._delete_record(
|
113
123
|
@primary_key => id_in_database,
|
114
|
-
locking_column =>
|
124
|
+
locking_column => attribute_before_type_cast(locking_column)
|
115
125
|
)
|
116
126
|
|
117
127
|
if affected_rows != 1
|
@@ -155,20 +165,12 @@ module ActiveRecord
|
|
155
165
|
super
|
156
166
|
end
|
157
167
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
# sub class being decorated. As such, changes to `lock_optimistically`, or
|
162
|
-
# `locking_column` would not be picked up.
|
163
|
-
def inherited(subclass)
|
164
|
-
subclass.class_eval do
|
165
|
-
is_lock_column = ->(name, _) { lock_optimistically && name == locking_column }
|
166
|
-
decorate_matching_attribute_types(is_lock_column, "_optimistic_locking") do |type|
|
167
|
-
LockingType.new(type)
|
168
|
-
end
|
169
|
-
end
|
170
|
-
super
|
168
|
+
def define_attribute(name, cast_type, **) # :nodoc:
|
169
|
+
if lock_optimistically && name == locking_column
|
170
|
+
cast_type = LockingType.new(cast_type)
|
171
171
|
end
|
172
|
+
super
|
173
|
+
end
|
172
174
|
end
|
173
175
|
end
|
174
176
|
|
@@ -176,6 +178,10 @@ module ActiveRecord
|
|
176
178
|
# `nil` values to `lock_version`, and not result in `ActiveRecord::StaleObjectError`
|
177
179
|
# during update record.
|
178
180
|
class LockingType < DelegateClass(Type::Value) # :nodoc:
|
181
|
+
def self.new(subtype)
|
182
|
+
self === subtype ? subtype : super
|
183
|
+
end
|
184
|
+
|
179
185
|
def deserialize(value)
|
180
186
|
super.to_i
|
181
187
|
end
|
@@ -53,8 +53,12 @@ module ActiveRecord
|
|
53
53
|
# end
|
54
54
|
#
|
55
55
|
# Database-specific information on row locking:
|
56
|
-
#
|
57
|
-
#
|
56
|
+
#
|
57
|
+
# [MySQL]
|
58
|
+
# https://dev.mysql.com/doc/refman/en/innodb-locking-reads.html
|
59
|
+
#
|
60
|
+
# [PostgreSQL]
|
61
|
+
# https://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
|
58
62
|
module Pessimistic
|
59
63
|
# Obtain a row lock on this record. Reloads the record to obtain the requested
|
60
64
|
# lock. Pass an SQL locking clause to append the end of the SELECT statement
|
@@ -19,6 +19,15 @@ module ActiveRecord
|
|
19
19
|
rt
|
20
20
|
end
|
21
21
|
|
22
|
+
def strict_loading_violation(event)
|
23
|
+
debug do
|
24
|
+
owner = event.payload[:owner]
|
25
|
+
association = event.payload[:association]
|
26
|
+
|
27
|
+
color("Strict loading violation: #{association} lazily loaded on #{owner}.", RED)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
22
31
|
def sql(event)
|
23
32
|
self.class.runtime += event.duration
|
24
33
|
return unless logger.debug?
|
@@ -32,11 +41,15 @@ module ActiveRecord
|
|
32
41
|
sql = payload[:sql]
|
33
42
|
binds = nil
|
34
43
|
|
35
|
-
|
44
|
+
if payload[:binds]&.any?
|
36
45
|
casted_params = type_casted_binds(payload[:type_casted_binds])
|
37
|
-
|
38
|
-
|
39
|
-
|
46
|
+
|
47
|
+
binds = []
|
48
|
+
payload[:binds].each_with_index do |attr, i|
|
49
|
+
binds << render_bind(attr, casted_params[i])
|
50
|
+
end
|
51
|
+
binds = binds.inspect
|
52
|
+
binds.prepend(" ")
|
40
53
|
end
|
41
54
|
|
42
55
|
name = colorize_payload_name(name, payload[:name])
|
@@ -51,13 +64,18 @@ module ActiveRecord
|
|
51
64
|
end
|
52
65
|
|
53
66
|
def render_bind(attr, value)
|
54
|
-
|
67
|
+
case attr
|
68
|
+
when ActiveModel::Attribute
|
69
|
+
if attr.type.binary? && attr.value
|
70
|
+
value = "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
|
71
|
+
end
|
72
|
+
when Array
|
55
73
|
attr = attr.first
|
56
|
-
|
57
|
-
|
74
|
+
else
|
75
|
+
attr = nil
|
58
76
|
end
|
59
77
|
|
60
|
-
[attr
|
78
|
+
[attr&.name, value]
|
61
79
|
end
|
62
80
|
|
63
81
|
def colorize_payload_name(name, payload_name)
|
@@ -59,11 +59,14 @@ module ActiveRecord
|
|
59
59
|
context = context_klass.call(request)
|
60
60
|
resolver = resolver_klass.call(context, options)
|
61
61
|
|
62
|
-
if reading_request?(request)
|
62
|
+
response = if reading_request?(request)
|
63
63
|
resolver.read(&blk)
|
64
64
|
else
|
65
65
|
resolver.write(&blk)
|
66
66
|
end
|
67
|
+
|
68
|
+
resolver.update_context(response)
|
69
|
+
response
|
67
70
|
end
|
68
71
|
|
69
72
|
def reading_request?(request)
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_record/middleware/database_selector/resolver/session"
|
4
|
+
require "active_support/core_ext/numeric/time"
|
4
5
|
|
5
6
|
module ActiveRecord
|
6
7
|
module Middleware
|
@@ -43,6 +44,10 @@ module ActiveRecord
|
|
43
44
|
write_to_primary(&blk)
|
44
45
|
end
|
45
46
|
|
47
|
+
def update_context(response)
|
48
|
+
context.save(response)
|
49
|
+
end
|
50
|
+
|
46
51
|
private
|
47
52
|
def read_from_primary(&blk)
|
48
53
|
ActiveRecord::Base.connected_to(role: ActiveRecord::Base.writing_role, prevent_writes: true) do
|
@@ -3,6 +3,8 @@
|
|
3
3
|
require "benchmark"
|
4
4
|
require "set"
|
5
5
|
require "zlib"
|
6
|
+
require "active_support/core_ext/array/access"
|
7
|
+
require "active_support/core_ext/enumerable"
|
6
8
|
require "active_support/core_ext/module/attribute_accessors"
|
7
9
|
require "active_support/actionable_error"
|
8
10
|
|
@@ -18,7 +20,7 @@ module ActiveRecord
|
|
18
20
|
# For example the following migration is not reversible.
|
19
21
|
# Rolling back this migration will raise an ActiveRecord::IrreversibleMigration error.
|
20
22
|
#
|
21
|
-
# class IrreversibleMigrationExample < ActiveRecord::Migration[
|
23
|
+
# class IrreversibleMigrationExample < ActiveRecord::Migration[6.0]
|
22
24
|
# def change
|
23
25
|
# create_table :distributors do |t|
|
24
26
|
# t.string :zipcode
|
@@ -36,7 +38,7 @@ module ActiveRecord
|
|
36
38
|
#
|
37
39
|
# 1. Define <tt>#up</tt> and <tt>#down</tt> methods instead of <tt>#change</tt>:
|
38
40
|
#
|
39
|
-
# class ReversibleMigrationExample < ActiveRecord::Migration[
|
41
|
+
# class ReversibleMigrationExample < ActiveRecord::Migration[6.0]
|
40
42
|
# def up
|
41
43
|
# create_table :distributors do |t|
|
42
44
|
# t.string :zipcode
|
@@ -61,7 +63,7 @@ module ActiveRecord
|
|
61
63
|
#
|
62
64
|
# 2. Use the #reversible method in <tt>#change</tt> method:
|
63
65
|
#
|
64
|
-
# class ReversibleMigrationExample < ActiveRecord::Migration[
|
66
|
+
# class ReversibleMigrationExample < ActiveRecord::Migration[6.0]
|
65
67
|
# def change
|
66
68
|
# create_table :distributors do |t|
|
67
69
|
# t.string :zipcode
|
@@ -133,17 +135,34 @@ module ActiveRecord
|
|
133
135
|
|
134
136
|
action "Run pending migrations" do
|
135
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
|
136
144
|
end
|
137
145
|
|
138
146
|
def initialize(message = nil)
|
139
|
-
|
140
|
-
super("Migrations are pending. To resolve this issue, run:\n\n rails db:migrate RAILS_ENV=#{::Rails.env}")
|
141
|
-
elsif !message
|
142
|
-
super("Migrations are pending. To resolve this issue, run:\n\n rails db:migrate")
|
143
|
-
else
|
144
|
-
super
|
145
|
-
end
|
147
|
+
super(message || detailed_migration_message)
|
146
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
|
147
166
|
end
|
148
167
|
|
149
168
|
class ConcurrentMigrationError < MigrationError #:nodoc:
|
@@ -157,7 +176,7 @@ module ActiveRecord
|
|
157
176
|
|
158
177
|
class NoEnvironmentInSchemaError < MigrationError #:nodoc:
|
159
178
|
def initialize
|
160
|
-
msg = "Environment data not found in the schema. To resolve this issue, run: \n\n rails db:environment:set"
|
179
|
+
msg = "Environment data not found in the schema. To resolve this issue, run: \n\n bin/rails db:environment:set"
|
161
180
|
if defined?(Rails.env)
|
162
181
|
super("#{msg} RAILS_ENV=#{::Rails.env}")
|
163
182
|
else
|
@@ -180,7 +199,7 @@ module ActiveRecord
|
|
180
199
|
msg = +"You are attempting to modify a database that was last run in `#{ stored }` environment.\n"
|
181
200
|
msg << "You are running in `#{ current }` environment. "
|
182
201
|
msg << "If you are sure you want to continue, first set the environment using:\n\n"
|
183
|
-
msg << " rails db:environment:set"
|
202
|
+
msg << " bin/rails db:environment:set"
|
184
203
|
if defined?(Rails.env)
|
185
204
|
super("#{msg} RAILS_ENV=#{::Rails.env}\n\n")
|
186
205
|
else
|
@@ -189,6 +208,14 @@ module ActiveRecord
|
|
189
208
|
end
|
190
209
|
end
|
191
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
|
+
|
192
219
|
# = Active Record Migrations
|
193
220
|
#
|
194
221
|
# Migrations can manage the evolution of a schema used by several physical
|
@@ -201,7 +228,7 @@ module ActiveRecord
|
|
201
228
|
#
|
202
229
|
# Example of a simple migration:
|
203
230
|
#
|
204
|
-
# class AddSsl < ActiveRecord::Migration[
|
231
|
+
# class AddSsl < ActiveRecord::Migration[6.0]
|
205
232
|
# def up
|
206
233
|
# add_column :accounts, :ssl_enabled, :boolean, default: true
|
207
234
|
# end
|
@@ -221,7 +248,7 @@ module ActiveRecord
|
|
221
248
|
#
|
222
249
|
# Example of a more complex migration that also needs to initialize data:
|
223
250
|
#
|
224
|
-
# class AddSystemSettings < ActiveRecord::Migration[
|
251
|
+
# class AddSystemSettings < ActiveRecord::Migration[6.0]
|
225
252
|
# def up
|
226
253
|
# create_table :system_settings do |t|
|
227
254
|
# t.string :name
|
@@ -337,7 +364,7 @@ module ActiveRecord
|
|
337
364
|
# The Rails package has several tools to help create and apply migrations.
|
338
365
|
#
|
339
366
|
# To generate a new migration, you can use
|
340
|
-
# rails generate migration MyNewMigration
|
367
|
+
# bin/rails generate migration MyNewMigration
|
341
368
|
#
|
342
369
|
# where MyNewMigration is the name of your migration. The generator will
|
343
370
|
# create an empty migration file <tt>timestamp_my_new_migration.rb</tt>
|
@@ -346,41 +373,36 @@ module ActiveRecord
|
|
346
373
|
#
|
347
374
|
# There is a special syntactic shortcut to generate migrations that add fields to a table.
|
348
375
|
#
|
349
|
-
# rails generate migration add_fieldname_to_tablename fieldname:string
|
376
|
+
# bin/rails generate migration add_fieldname_to_tablename fieldname:string
|
350
377
|
#
|
351
378
|
# This will generate the file <tt>timestamp_add_fieldname_to_tablename.rb</tt>, which will look like this:
|
352
|
-
# class AddFieldnameToTablename < ActiveRecord::Migration[
|
379
|
+
# class AddFieldnameToTablename < ActiveRecord::Migration[6.0]
|
353
380
|
# def change
|
354
381
|
# add_column :tablenames, :fieldname, :string
|
355
382
|
# end
|
356
383
|
# end
|
357
384
|
#
|
358
385
|
# To run migrations against the currently configured database, use
|
359
|
-
# <tt>rails db:migrate</tt>. This will update the database by running all of the
|
386
|
+
# <tt>bin/rails db:migrate</tt>. This will update the database by running all of the
|
360
387
|
# pending migrations, creating the <tt>schema_migrations</tt> table
|
361
388
|
# (see "About the schema_migrations table" section below) if missing. It will also
|
362
389
|
# invoke the db:schema:dump command, which will update your db/schema.rb file
|
363
390
|
# to match the structure of your database.
|
364
391
|
#
|
365
392
|
# To roll the database back to a previous migration version, use
|
366
|
-
# <tt>rails db:rollback VERSION=X</tt> where <tt>X</tt> is the version to which
|
393
|
+
# <tt>bin/rails db:rollback VERSION=X</tt> where <tt>X</tt> is the version to which
|
367
394
|
# you wish to downgrade. Alternatively, you can also use the STEP option if you
|
368
|
-
# wish to rollback last few migrations. <tt>rails db:rollback STEP=2</tt> will rollback
|
395
|
+
# wish to rollback last few migrations. <tt>bin/rails db:rollback STEP=2</tt> will rollback
|
369
396
|
# the latest two migrations.
|
370
397
|
#
|
371
398
|
# If any of the migrations throw an <tt>ActiveRecord::IrreversibleMigration</tt> exception,
|
372
399
|
# that step will fail and you'll have some manual work to do.
|
373
400
|
#
|
374
|
-
# == Database support
|
375
|
-
#
|
376
|
-
# Migrations are currently supported in MySQL, PostgreSQL, SQLite,
|
377
|
-
# SQL Server, and Oracle (all supported databases except DB2).
|
378
|
-
#
|
379
401
|
# == More examples
|
380
402
|
#
|
381
403
|
# Not all migrations change the schema. Some just fix the data:
|
382
404
|
#
|
383
|
-
# class RemoveEmptyTags < ActiveRecord::Migration[
|
405
|
+
# class RemoveEmptyTags < ActiveRecord::Migration[6.0]
|
384
406
|
# def up
|
385
407
|
# Tag.all.each { |tag| tag.destroy if tag.pages.empty? }
|
386
408
|
# end
|
@@ -393,7 +415,7 @@ module ActiveRecord
|
|
393
415
|
#
|
394
416
|
# Others remove columns when they migrate up instead of down:
|
395
417
|
#
|
396
|
-
# class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration[
|
418
|
+
# class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration[6.0]
|
397
419
|
# def up
|
398
420
|
# remove_column :items, :incomplete_items_count
|
399
421
|
# remove_column :items, :completed_items_count
|
@@ -407,7 +429,7 @@ module ActiveRecord
|
|
407
429
|
#
|
408
430
|
# And sometimes you need to do something in SQL not abstracted directly by migrations:
|
409
431
|
#
|
410
|
-
# class MakeJoinUnique < ActiveRecord::Migration[
|
432
|
+
# class MakeJoinUnique < ActiveRecord::Migration[6.0]
|
411
433
|
# def up
|
412
434
|
# execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)"
|
413
435
|
# end
|
@@ -424,7 +446,7 @@ module ActiveRecord
|
|
424
446
|
# <tt>Base#reset_column_information</tt> in order to ensure that the model has the
|
425
447
|
# latest column data from after the new column was added. Example:
|
426
448
|
#
|
427
|
-
# class AddPeopleSalary < ActiveRecord::Migration[
|
449
|
+
# class AddPeopleSalary < ActiveRecord::Migration[6.0]
|
428
450
|
# def up
|
429
451
|
# add_column :people, :salary, :integer
|
430
452
|
# Person.reset_column_information
|
@@ -482,7 +504,7 @@ module ActiveRecord
|
|
482
504
|
# To define a reversible migration, define the +change+ method in your
|
483
505
|
# migration like this:
|
484
506
|
#
|
485
|
-
# class TenderloveMigration < ActiveRecord::Migration[
|
507
|
+
# class TenderloveMigration < ActiveRecord::Migration[6.0]
|
486
508
|
# def change
|
487
509
|
# create_table(:horses) do |t|
|
488
510
|
# t.column :content, :text
|
@@ -512,7 +534,7 @@ module ActiveRecord
|
|
512
534
|
# can't execute inside a transaction though, and for these situations
|
513
535
|
# you can turn the automatic transactions off.
|
514
536
|
#
|
515
|
-
# class ChangeEnum < ActiveRecord::Migration[
|
537
|
+
# class ChangeEnum < ActiveRecord::Migration[6.0]
|
516
538
|
# disable_ddl_transaction!
|
517
539
|
#
|
518
540
|
# def up
|
@@ -525,6 +547,7 @@ module ActiveRecord
|
|
525
547
|
class Migration
|
526
548
|
autoload :CommandRecorder, "active_record/migration/command_recorder"
|
527
549
|
autoload :Compatibility, "active_record/migration/compatibility"
|
550
|
+
autoload :JoinTable, "active_record/migration/join_table"
|
528
551
|
|
529
552
|
# This must be defined before the inherited hook, below
|
530
553
|
class Current < Migration #:nodoc:
|
@@ -553,21 +576,37 @@ module ActiveRecord
|
|
553
576
|
# This class is used to verify that all migrations have been run before
|
554
577
|
# loading a web page if <tt>config.active_record.migration_error</tt> is set to :page_load
|
555
578
|
class CheckPending
|
556
|
-
def initialize(app)
|
579
|
+
def initialize(app, file_watcher: ActiveSupport::FileUpdateChecker)
|
557
580
|
@app = app
|
558
|
-
@
|
581
|
+
@needs_check = true
|
582
|
+
@mutex = Mutex.new
|
583
|
+
@file_watcher = file_watcher
|
559
584
|
end
|
560
585
|
|
561
586
|
def call(env)
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
587
|
+
@mutex.synchronize do
|
588
|
+
@watcher ||= build_watcher do
|
589
|
+
@needs_check = true
|
590
|
+
ActiveRecord::Migration.check_pending!(connection)
|
591
|
+
@needs_check = false
|
592
|
+
end
|
593
|
+
|
594
|
+
if @needs_check
|
595
|
+
@watcher.execute
|
596
|
+
else
|
597
|
+
@watcher.execute_if_updated
|
598
|
+
end
|
566
599
|
end
|
600
|
+
|
567
601
|
@app.call(env)
|
568
602
|
end
|
569
603
|
|
570
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
|
609
|
+
|
571
610
|
def connection
|
572
611
|
ActiveRecord::Base.connection
|
573
612
|
end
|
@@ -587,11 +626,11 @@ module ActiveRecord
|
|
587
626
|
end
|
588
627
|
|
589
628
|
def load_schema_if_pending!
|
590
|
-
|
629
|
+
current_db_config = Base.connection_db_config
|
591
630
|
all_configs = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env)
|
592
631
|
|
593
632
|
needs_update = !all_configs.all? do |db_config|
|
594
|
-
Tasks::DatabaseTasks.schema_up_to_date?(db_config
|
633
|
+
Tasks::DatabaseTasks.schema_up_to_date?(db_config, ActiveRecord::Base.schema_format)
|
595
634
|
end
|
596
635
|
|
597
636
|
if needs_update
|
@@ -604,7 +643,7 @@ module ActiveRecord
|
|
604
643
|
end
|
605
644
|
|
606
645
|
# Establish a new connection, the old database may be gone (db:test:prepare uses purge)
|
607
|
-
Base.establish_connection(
|
646
|
+
Base.establish_connection(current_db_config)
|
608
647
|
|
609
648
|
check_pending!
|
610
649
|
end
|
@@ -657,7 +696,7 @@ module ActiveRecord
|
|
657
696
|
# and create the table 'apples' on the way up, and the reverse
|
658
697
|
# on the way down.
|
659
698
|
#
|
660
|
-
# class FixTLMigration < ActiveRecord::Migration[
|
699
|
+
# class FixTLMigration < ActiveRecord::Migration[6.0]
|
661
700
|
# def change
|
662
701
|
# revert do
|
663
702
|
# create_table(:horses) do |t|
|
@@ -674,9 +713,9 @@ module ActiveRecord
|
|
674
713
|
# Or equivalently, if +TenderloveMigration+ is defined as in the
|
675
714
|
# documentation for Migration:
|
676
715
|
#
|
677
|
-
# require_relative
|
716
|
+
# require_relative "20121212123456_tenderlove_migration"
|
678
717
|
#
|
679
|
-
# class FixupTLMigration < ActiveRecord::Migration[
|
718
|
+
# class FixupTLMigration < ActiveRecord::Migration[6.0]
|
680
719
|
# def change
|
681
720
|
# revert TenderloveMigration
|
682
721
|
#
|
@@ -727,7 +766,7 @@ module ActiveRecord
|
|
727
766
|
# when the three columns 'first_name', 'last_name' and 'full_name' exist,
|
728
767
|
# even when migrating down:
|
729
768
|
#
|
730
|
-
# class SplitNameMigration < ActiveRecord::Migration[
|
769
|
+
# class SplitNameMigration < ActiveRecord::Migration[6.0]
|
731
770
|
# def change
|
732
771
|
# add_column :users, :first_name, :string
|
733
772
|
# add_column :users, :last_name, :string
|
@@ -755,7 +794,7 @@ module ActiveRecord
|
|
755
794
|
# In the following example, the new column +published+ will be given
|
756
795
|
# the value +true+ for all existing records.
|
757
796
|
#
|
758
|
-
# class AddPublishedToPosts < ActiveRecord::Migration[
|
797
|
+
# class AddPublishedToPosts < ActiveRecord::Migration[6.0]
|
759
798
|
# def change
|
760
799
|
# add_column :posts, :published, :boolean, default: false
|
761
800
|
# up_only do
|
@@ -828,7 +867,7 @@ module ActiveRecord
|
|
828
867
|
change
|
829
868
|
end
|
830
869
|
else
|
831
|
-
|
870
|
+
public_send(direction)
|
832
871
|
end
|
833
872
|
ensure
|
834
873
|
@connection = nil
|
@@ -995,10 +1034,6 @@ module ActiveRecord
|
|
995
1034
|
File.basename(filename)
|
996
1035
|
end
|
997
1036
|
|
998
|
-
def mtime
|
999
|
-
File.mtime filename
|
1000
|
-
end
|
1001
|
-
|
1002
1037
|
delegate :migrate, :announce, :write, :disable_ddl_transaction, to: :migration
|
1003
1038
|
|
1004
1039
|
private
|
@@ -1012,16 +1047,6 @@ module ActiveRecord
|
|
1012
1047
|
end
|
1013
1048
|
end
|
1014
1049
|
|
1015
|
-
class NullMigration < MigrationProxy #:nodoc:
|
1016
|
-
def initialize
|
1017
|
-
super(nil, 0, nil, nil)
|
1018
|
-
end
|
1019
|
-
|
1020
|
-
def mtime
|
1021
|
-
0
|
1022
|
-
end
|
1023
|
-
end
|
1024
|
-
|
1025
1050
|
class MigrationContext #:nodoc:
|
1026
1051
|
attr_reader :migrations_paths, :schema_migration
|
1027
1052
|
|
@@ -1100,10 +1125,6 @@ module ActiveRecord
|
|
1100
1125
|
migrations.any?
|
1101
1126
|
end
|
1102
1127
|
|
1103
|
-
def last_migration #:nodoc:
|
1104
|
-
migrations.last || NullMigration.new
|
1105
|
-
end
|
1106
|
-
|
1107
1128
|
def migrations
|
1108
1129
|
migrations = migration_files.map do |file|
|
1109
1130
|
version, name, scope = parse_migration_filename(file)
|
@@ -1144,6 +1165,7 @@ module ActiveRecord
|
|
1144
1165
|
end
|
1145
1166
|
|
1146
1167
|
def last_stored_environment
|
1168
|
+
return nil unless ActiveRecord::InternalMetadata.enabled?
|
1147
1169
|
return nil if current_version == 0
|
1148
1170
|
raise NoEnvironmentInSchemaError unless ActiveRecord::InternalMetadata.table_exists?
|
1149
1171
|
|
@@ -1178,7 +1200,7 @@ module ActiveRecord
|
|
1178
1200
|
|
1179
1201
|
finish = migrator.migrations[start_index + steps]
|
1180
1202
|
version = finish ? finish.version : 0
|
1181
|
-
|
1203
|
+
public_send(direction, version)
|
1182
1204
|
end
|
1183
1205
|
end
|
1184
1206
|
|
@@ -1265,7 +1287,7 @@ module ActiveRecord
|
|
1265
1287
|
def run_without_lock
|
1266
1288
|
migration = migrations.detect { |m| m.version == @target_version }
|
1267
1289
|
raise UnknownMigrationVersionError.new(@target_version) if migration.nil?
|
1268
|
-
result = execute_migration_in_transaction(migration
|
1290
|
+
result = execute_migration_in_transaction(migration)
|
1269
1291
|
|
1270
1292
|
record_environment
|
1271
1293
|
result
|
@@ -1277,10 +1299,7 @@ module ActiveRecord
|
|
1277
1299
|
raise UnknownMigrationVersionError.new(@target_version)
|
1278
1300
|
end
|
1279
1301
|
|
1280
|
-
result = runnable.each
|
1281
|
-
execute_migration_in_transaction(migration, @direction)
|
1282
|
-
end
|
1283
|
-
|
1302
|
+
result = runnable.each(&method(:execute_migration_in_transaction))
|
1284
1303
|
record_environment
|
1285
1304
|
result
|
1286
1305
|
end
|
@@ -1300,14 +1319,14 @@ module ActiveRecord
|
|
1300
1319
|
@target_version && @target_version != 0 && !target
|
1301
1320
|
end
|
1302
1321
|
|
1303
|
-
def execute_migration_in_transaction(migration
|
1322
|
+
def execute_migration_in_transaction(migration)
|
1304
1323
|
return if down? && !migrated.include?(migration.version.to_i)
|
1305
1324
|
return if up? && migrated.include?(migration.version.to_i)
|
1306
1325
|
|
1307
1326
|
Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
|
1308
1327
|
|
1309
1328
|
ddl_transaction(migration) do
|
1310
|
-
migration.migrate(direction)
|
1329
|
+
migration.migrate(@direction)
|
1311
1330
|
record_version_state_after_migrating(migration.version)
|
1312
1331
|
end
|
1313
1332
|
rescue => e
|
@@ -1374,20 +1393,31 @@ module ActiveRecord
|
|
1374
1393
|
|
1375
1394
|
def with_advisory_lock
|
1376
1395
|
lock_id = generate_migrator_advisory_lock_id
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1380
|
-
|
1381
|
-
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1385
|
-
|
1386
|
-
|
1387
|
-
|
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
|
1388
1408
|
end
|
1389
1409
|
end
|
1390
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
|
+
|
1391
1421
|
MIGRATOR_SALT = 2053462845
|
1392
1422
|
def generate_migrator_advisory_lock_id
|
1393
1423
|
db_name_hash = Zlib.crc32(Base.connection.current_database)
|