activerecord 7.1.5.1 → 8.0.2
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 +369 -2484
- data/README.rdoc +15 -15
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +2 -1
- data/lib/active_record/associations/alias_tracker.rb +31 -23
- data/lib/active_record/associations/association.rb +43 -12
- data/lib/active_record/associations/belongs_to_association.rb +21 -8
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +7 -6
- data/lib/active_record/associations/builder/belongs_to.rb +1 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
- 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/collection_association.rb +17 -9
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +4 -3
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/singular_association.rb +14 -3
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +92 -295
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/primary_key.rb +25 -61
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +4 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +9 -18
- data/lib/active_record/attribute_methods.rb +71 -75
- data/lib/active_record/attributes.rb +63 -49
- data/lib/active_record/autosave_association.rb +92 -57
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +48 -122
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +286 -77
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +119 -55
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +197 -76
- data/lib/active_record/connection_adapters/abstract/quoting.rb +66 -92
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +12 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +48 -12
- data/lib/active_record/connection_adapters/abstract/transaction.rb +140 -67
- data/lib/active_record/connection_adapters/abstract_adapter.rb +85 -90
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +71 -52
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +56 -45
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +92 -101
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +13 -31
- data/lib/active_record/connection_adapters/pool_config.rb +14 -13
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +86 -41
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +36 -20
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +75 -28
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +73 -113
- data/lib/active_record/connection_adapters/schema_cache.rb +124 -131
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +81 -97
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +29 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +35 -3
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +183 -87
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +39 -69
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
- data/lib/active_record/connection_adapters.rb +65 -0
- data/lib/active_record/connection_handling.rb +74 -37
- data/lib/active_record/core.rb +132 -51
- data/lib/active_record/counter_cache.rb +19 -10
- data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
- data/lib/active_record/database_configurations/database_config.rb +23 -4
- data/lib/active_record/database_configurations/hash_config.rb +46 -34
- data/lib/active_record/database_configurations/url_config.rb +20 -1
- data/lib/active_record/database_configurations.rb +1 -1
- data/lib/active_record/delegated_type.rb +41 -17
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +7 -7
- data/lib/active_record/encryption/encrypted_attribute_type.rb +33 -4
- data/lib/active_record/encryption/encryptor.rb +28 -6
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/key_provider.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +4 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +8 -1
- data/lib/active_record/enum.rb +20 -16
- data/lib/active_record/errors.rb +54 -20
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixtures.rb +37 -33
- data/lib/active_record/future_result.rb +21 -13
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +4 -2
- data/lib/active_record/insert_all.rb +19 -16
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/log_subscriber.rb +5 -32
- data/lib/active_record/message_pack.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +33 -14
- data/lib/active_record/migration/compatibility.rb +8 -3
- data/lib/active_record/migration/default_strategy.rb +4 -5
- data/lib/active_record/migration/pending_migration_connection.rb +2 -2
- data/lib/active_record/migration.rb +104 -98
- data/lib/active_record/model_schema.rb +32 -70
- data/lib/active_record/nested_attributes.rb +15 -9
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +127 -451
- data/lib/active_record/query_cache.rb +19 -8
- data/lib/active_record/query_logs.rb +104 -37
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +24 -12
- data/lib/active_record/railtie.rb +26 -68
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +43 -61
- data/lib/active_record/reflection.rb +112 -53
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +138 -72
- data/lib/active_record/relation/calculations.rb +122 -82
- data/lib/active_record/relation/delegation.rb +30 -22
- data/lib/active_record/relation/finder_methods.rb +32 -18
- data/lib/active_record/relation/merger.rb +12 -14
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +10 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +16 -3
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +317 -101
- data/lib/active_record/relation/spawn_methods.rb +3 -19
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +561 -119
- data/lib/active_record/result.rb +95 -46
- data/lib/active_record/runtime_registry.rb +39 -0
- data/lib/active_record/sanitization.rb +31 -25
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +53 -20
- data/lib/active_record/schema_migration.rb +31 -14
- data/lib/active_record/scoping/named.rb +6 -2
- data/lib/active_record/signed_id.rb +24 -4
- data/lib/active_record/statement_cache.rb +19 -19
- data/lib/active_record/store.rb +7 -3
- data/lib/active_record/table_metadata.rb +2 -13
- data/lib/active_record/tasks/database_tasks.rb +87 -58
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -3
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +4 -3
- data/lib/active_record/test_fixtures.rb +98 -89
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +2 -2
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +72 -17
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/associated.rb +9 -3
- data/lib/active_record/validations/uniqueness.rb +23 -18
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +138 -57
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +4 -2
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +2 -2
- data/lib/arel/collectors/substitute_binds.rb +3 -3
- data/lib/arel/nodes/binary.rb +1 -7
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +5 -4
- data/lib/arel/nodes/sql_literal.rb +8 -1
- data/lib/arel/nodes.rb +2 -2
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/table.rb +3 -7
- data/lib/arel/tree_manager.rb +3 -2
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +29 -16
- data/lib/arel.rb +7 -3
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +18 -16
- data/lib/active_record/relation/record_fetch_warning.rb +0 -49
@@ -13,6 +13,16 @@ module ActiveRecord
|
|
13
13
|
class_attribute :signed_id_verifier_secret, instance_writer: false
|
14
14
|
end
|
15
15
|
|
16
|
+
module RelationMethods # :nodoc:
|
17
|
+
def find_signed(...)
|
18
|
+
scoping { model.find_signed(...) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def find_signed!(...)
|
22
|
+
scoping { model.find_signed!(...) }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
16
26
|
module ClassMethods
|
17
27
|
# Lets you find a record based on a signed id that's safe to put into the world without risk of tampering.
|
18
28
|
# This is particularly useful for things like password reset or email verification, where you want
|
@@ -66,8 +76,9 @@ module ActiveRecord
|
|
66
76
|
end
|
67
77
|
|
68
78
|
# The verifier instance that all signed ids are generated and verified from. By default, it'll be initialized
|
69
|
-
# with the class-level +signed_id_verifier_secret+, which within
|
70
|
-
# Rails.application.key_generator.
|
79
|
+
# with the class-level +signed_id_verifier_secret+, which within Rails comes from
|
80
|
+
# {Rails.application.key_generator}[rdoc-ref:Rails::Application#key_generator].
|
81
|
+
# By default, it's SHA256 for the digest and JSON for the serialization.
|
71
82
|
def signed_id_verifier
|
72
83
|
@signed_id_verifier ||= begin
|
73
84
|
secret = signed_id_verifier_secret
|
@@ -76,14 +87,14 @@ module ActiveRecord
|
|
76
87
|
if secret.nil?
|
77
88
|
raise ArgumentError, "You must set ActiveRecord::Base.signed_id_verifier_secret to use signed ids"
|
78
89
|
else
|
79
|
-
ActiveSupport::MessageVerifier.new secret, digest: "SHA256", serializer: JSON
|
90
|
+
ActiveSupport::MessageVerifier.new secret, digest: "SHA256", serializer: JSON, url_safe: true
|
80
91
|
end
|
81
92
|
end
|
82
93
|
end
|
83
94
|
|
84
95
|
# Allows you to pass in a custom verifier used for the signed ids. This also allows you to use different
|
85
96
|
# verifiers for different classes. This is also helpful if you need to rotate keys, as you can prepare
|
86
|
-
# your custom verifier for that in advance. See
|
97
|
+
# your custom verifier for that in advance. See ActiveSupport::MessageVerifier for details.
|
87
98
|
def signed_id_verifier=(verifier)
|
88
99
|
@signed_id_verifier = verifier
|
89
100
|
end
|
@@ -96,7 +107,16 @@ module ActiveRecord
|
|
96
107
|
|
97
108
|
|
98
109
|
# Returns a signed id that's generated using a preconfigured +ActiveSupport::MessageVerifier+ instance.
|
110
|
+
#
|
99
111
|
# This signed id is tamper proof, so it's safe to send in an email or otherwise share with the outside world.
|
112
|
+
# However, as with any message signed with a +ActiveSupport::MessageVerifier+,
|
113
|
+
# {the signed id is not encrypted}[link:classes/ActiveSupport/MessageVerifier.html#class-ActiveSupport::MessageVerifier-label-Signing+is+not+encryption].
|
114
|
+
# It's just encoded and protected against tampering.
|
115
|
+
#
|
116
|
+
# This means that the ID can be decoded by anyone; however, if tampered with (so to point to a different ID),
|
117
|
+
# the cryptographic signature will no longer match, and the signed id will be considered invalid and return nil
|
118
|
+
# when passed to +find_signed+ (or raise with +find_signed!+).
|
119
|
+
#
|
100
120
|
# It can furthermore be set to expire (the default is not to expire), and scoped down with a specific purpose.
|
101
121
|
# If the expiration date has been exceeded before +find_signed+ is called, the id won't find the designated
|
102
122
|
# record. If a purpose is set, this too must match.
|
@@ -4,14 +4,14 @@ module ActiveRecord
|
|
4
4
|
# Statement cache is used to cache a single statement in order to avoid creating the AST again.
|
5
5
|
# Initializing the cache is done by passing the statement in the create block:
|
6
6
|
#
|
7
|
-
# cache = StatementCache.create(
|
7
|
+
# cache = StatementCache.create(ClothingItem.lease_connection) do |params|
|
8
8
|
# Book.where(name: "my book").where("author_id > 3")
|
9
9
|
# end
|
10
10
|
#
|
11
11
|
# The cached statement is executed by using the
|
12
12
|
# {connection.execute}[rdoc-ref:ConnectionAdapters::DatabaseStatements#execute] method:
|
13
13
|
#
|
14
|
-
# cache.execute([],
|
14
|
+
# cache.execute([], ClothingItem.lease_connection)
|
15
15
|
#
|
16
16
|
# The relation returned by the block is cached, and for each
|
17
17
|
# {execute}[rdoc-ref:ConnectionAdapters::DatabaseStatements#execute]
|
@@ -20,13 +20,13 @@ module ActiveRecord
|
|
20
20
|
# If you want to cache the statement without the values you can use the +bind+ method of the
|
21
21
|
# block parameter.
|
22
22
|
#
|
23
|
-
# cache = StatementCache.create(
|
23
|
+
# cache = StatementCache.create(ClothingItem.lease_connection) do |params|
|
24
24
|
# Book.where(name: params.bind)
|
25
25
|
# end
|
26
26
|
#
|
27
27
|
# And pass the bind values as the first argument of +execute+ call.
|
28
28
|
#
|
29
|
-
# cache.execute(["my book"],
|
29
|
+
# cache.execute(["my book"], ClothingItem.lease_connection)
|
30
30
|
class StatementCache # :nodoc:
|
31
31
|
class Substitute; end # :nodoc:
|
32
32
|
|
@@ -62,7 +62,7 @@ module ActiveRecord
|
|
62
62
|
end
|
63
63
|
|
64
64
|
class PartialQueryCollector
|
65
|
-
attr_accessor :preparable
|
65
|
+
attr_accessor :preparable, :retryable
|
66
66
|
|
67
67
|
def initialize
|
68
68
|
@parts = []
|
@@ -74,13 +74,13 @@ module ActiveRecord
|
|
74
74
|
self
|
75
75
|
end
|
76
76
|
|
77
|
-
def add_bind(obj)
|
77
|
+
def add_bind(obj, &)
|
78
78
|
@binds << obj
|
79
79
|
@parts << Substitute.new
|
80
80
|
self
|
81
81
|
end
|
82
82
|
|
83
|
-
def add_binds(binds, proc_for_binds = nil)
|
83
|
+
def add_binds(binds, proc_for_binds = nil, &)
|
84
84
|
@binds.concat proc_for_binds ? binds.map(&proc_for_binds) : binds
|
85
85
|
binds.size.times do |i|
|
86
86
|
@parts << ", " unless i == 0
|
@@ -133,23 +133,26 @@ module ActiveRecord
|
|
133
133
|
relation = (callable || block).call Params.new
|
134
134
|
query_builder, binds = connection.cacheable_query(self, relation.arel)
|
135
135
|
bind_map = BindMap.new(binds)
|
136
|
-
new(query_builder, bind_map, relation.
|
136
|
+
new(query_builder, bind_map, relation.model)
|
137
137
|
end
|
138
138
|
|
139
|
-
def initialize(query_builder, bind_map,
|
139
|
+
def initialize(query_builder, bind_map, model)
|
140
140
|
@query_builder = query_builder
|
141
141
|
@bind_map = bind_map
|
142
|
-
@
|
142
|
+
@model = model
|
143
143
|
end
|
144
144
|
|
145
|
-
def execute(params, connection, &block)
|
146
|
-
bind_values = bind_map.bind params
|
145
|
+
def execute(params, connection, allow_retry: false, async: false, &block)
|
146
|
+
bind_values = @bind_map.bind params
|
147
|
+
sql = @query_builder.sql_for bind_values, connection
|
147
148
|
|
148
|
-
|
149
|
-
|
150
|
-
|
149
|
+
if async
|
150
|
+
@model.async_find_by_sql(sql, bind_values, preparable: true, allow_retry: allow_retry, &block)
|
151
|
+
else
|
152
|
+
@model.find_by_sql(sql, bind_values, preparable: true, allow_retry: allow_retry, &block)
|
153
|
+
end
|
151
154
|
rescue ::RangeError
|
152
|
-
[]
|
155
|
+
async ? Promise.wrap([]) : []
|
153
156
|
end
|
154
157
|
|
155
158
|
def self.unsupported_value?(value)
|
@@ -157,8 +160,5 @@ module ActiveRecord
|
|
157
160
|
when NilClass, Array, Range, Hash, Relation, Base then true
|
158
161
|
end
|
159
162
|
end
|
160
|
-
|
161
|
-
private
|
162
|
-
attr_reader :query_builder, :bind_map, :klass
|
163
163
|
end
|
164
164
|
end
|
data/lib/active_record/store.rb
CHANGED
@@ -25,8 +25,8 @@ module ActiveRecord
|
|
25
25
|
# You can set custom coder to encode/decode your serialized attributes to/from different formats.
|
26
26
|
# JSON, YAML, Marshal are supported out of the box. Generally it can be any wrapper that provides +load+ and +dump+.
|
27
27
|
#
|
28
|
-
# NOTE: If you are using structured database data types (e.g. PostgreSQL +hstore+/+json+,
|
29
|
-
# +json+) there is no need for the serialization provided by {.store}[rdoc-ref:rdoc-ref:ClassMethods#store].
|
28
|
+
# NOTE: If you are using structured database data types (e.g. PostgreSQL +hstore+/+json+, MySQL 5.7+
|
29
|
+
# +json+, or SQLite 3.38+ +json+) there is no need for the serialization provided by {.store}[rdoc-ref:rdoc-ref:ClassMethods#store].
|
30
30
|
# Simply use {.store_accessor}[rdoc-ref:ClassMethods#store_accessor] instead to generate
|
31
31
|
# the accessor methods. Be aware that these columns use a string keyed hash and do not allow access
|
32
32
|
# using a symbol.
|
@@ -217,7 +217,11 @@ module ActiveRecord
|
|
217
217
|
end
|
218
218
|
|
219
219
|
def store_accessor_for(store_attribute)
|
220
|
-
type_for_attribute(store_attribute).
|
220
|
+
type_for_attribute(store_attribute).tap do |type|
|
221
|
+
unless type.respond_to?(:accessor)
|
222
|
+
raise ConfigurationError, "the column '#{store_attribute}' has not been configured as a store. Please make sure the column is declared serializable via 'ActiveRecord.store' or, if your database supports it, use a structured column type like hstore or json."
|
223
|
+
end
|
224
|
+
end.accessor
|
221
225
|
end
|
222
226
|
|
223
227
|
class HashAccessor # :nodoc:
|
@@ -23,16 +23,7 @@ module ActiveRecord
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def associated_with?(table_name)
|
26
|
-
|
27
|
-
reflection
|
28
|
-
elsif ActiveRecord.allow_deprecated_singular_associations_name && reflection = klass&._reflect_on_association(table_name.singularize)
|
29
|
-
ActiveRecord.deprecator.warn(<<~MSG)
|
30
|
-
Referring to a singular association (e.g. `#{reflection.name}`) by its plural name (e.g. `#{reflection.plural_name}`) is deprecated.
|
31
|
-
|
32
|
-
To convert this deprecation warning to an error and enable more performant behavior, set config.active_record.allow_deprecated_singular_associations_name = false.
|
33
|
-
MSG
|
34
|
-
reflection
|
35
|
-
end
|
26
|
+
klass&._reflect_on_association(table_name)
|
36
27
|
end
|
37
28
|
|
38
29
|
def associated_table(table_name)
|
@@ -78,9 +69,7 @@ module ActiveRecord
|
|
78
69
|
|
79
70
|
def predicate_builder
|
80
71
|
if klass
|
81
|
-
|
82
|
-
predicate_builder.instance_variable_set(:@table, self)
|
83
|
-
predicate_builder
|
72
|
+
klass.predicate_builder.with(self)
|
84
73
|
else
|
85
74
|
PredicateBuilder.new(self)
|
86
75
|
end
|
@@ -125,11 +125,11 @@ module ActiveRecord
|
|
125
125
|
end
|
126
126
|
|
127
127
|
def create_all
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
128
|
+
db_config = migration_connection.pool.db_config
|
129
|
+
|
130
|
+
each_local_configuration { |db_config| create(db_config) }
|
131
|
+
|
132
|
+
migration_class.establish_connection(db_config)
|
133
133
|
end
|
134
134
|
|
135
135
|
def setup_initial_database_yaml # :nodoc:
|
@@ -178,22 +178,9 @@ module ActiveRecord
|
|
178
178
|
dump_db_configs = []
|
179
179
|
|
180
180
|
each_current_configuration(env) do |db_config|
|
181
|
-
|
182
|
-
begin
|
183
|
-
database_initialized = migration_connection.schema_migration.table_exists?
|
184
|
-
rescue ActiveRecord::NoDatabaseError
|
185
|
-
create(db_config)
|
186
|
-
retry
|
187
|
-
end
|
181
|
+
database_initialized = initialize_database(db_config)
|
188
182
|
|
189
|
-
|
190
|
-
if File.exist?(schema_dump_path(db_config))
|
191
|
-
load_schema(db_config, ActiveRecord.schema_format, nil)
|
192
|
-
end
|
193
|
-
|
194
|
-
seed = true
|
195
|
-
end
|
196
|
-
end
|
183
|
+
seed = true if database_initialized && db_config.seeds?
|
197
184
|
end
|
198
185
|
|
199
186
|
each_current_environment(env) do |environment|
|
@@ -253,13 +240,34 @@ module ActiveRecord
|
|
253
240
|
end
|
254
241
|
end
|
255
242
|
|
256
|
-
def
|
243
|
+
def migrate_all
|
244
|
+
db_configs = ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env)
|
245
|
+
db_configs.each { |db_config| initialize_database(db_config) }
|
246
|
+
|
247
|
+
if db_configs.size == 1 && db_configs.first.primary?
|
248
|
+
ActiveRecord::Tasks::DatabaseTasks.migrate(skip_initialize: true)
|
249
|
+
else
|
250
|
+
mapped_versions = ActiveRecord::Tasks::DatabaseTasks.db_configs_with_versions
|
251
|
+
|
252
|
+
mapped_versions.sort.each do |version, db_configs|
|
253
|
+
db_configs.each do |db_config|
|
254
|
+
ActiveRecord::Tasks::DatabaseTasks.with_temporary_connection(db_config) do
|
255
|
+
ActiveRecord::Tasks::DatabaseTasks.migrate(version, skip_initialize: true)
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
def migrate(version = nil, skip_initialize: false)
|
257
263
|
scope = ENV["SCOPE"]
|
258
264
|
verbose_was, Migration.verbose = Migration.verbose, verbose?
|
259
265
|
|
260
266
|
check_target_version
|
261
267
|
|
262
|
-
|
268
|
+
initialize_database(migration_connection_pool.db_config) unless skip_initialize
|
269
|
+
|
270
|
+
migration_connection_pool.migration_context.migrate(target_version) do |migration|
|
263
271
|
if version.blank?
|
264
272
|
scope.blank? || scope == migration.scope
|
265
273
|
else
|
@@ -269,7 +277,7 @@ module ActiveRecord
|
|
269
277
|
Migration.write("No migrations ran. (using #{scope} scope)") if scope.present? && migrations_ran.empty?
|
270
278
|
end
|
271
279
|
|
272
|
-
|
280
|
+
migration_connection_pool.schema_cache.clear!
|
273
281
|
ensure
|
274
282
|
Migration.verbose = verbose_was
|
275
283
|
end
|
@@ -277,9 +285,9 @@ module ActiveRecord
|
|
277
285
|
def db_configs_with_versions(environment = env) # :nodoc:
|
278
286
|
db_configs_with_versions = Hash.new { |h, k| h[k] = [] }
|
279
287
|
|
280
|
-
|
281
|
-
db_config =
|
282
|
-
versions_to_run =
|
288
|
+
with_temporary_pool_for_each(env: environment) do |pool|
|
289
|
+
db_config = pool.db_config
|
290
|
+
versions_to_run = pool.migration_context.pending_migration_versions
|
283
291
|
target_version = ActiveRecord::Tasks::DatabaseTasks.target_version
|
284
292
|
|
285
293
|
versions_to_run.each do |version|
|
@@ -292,15 +300,15 @@ module ActiveRecord
|
|
292
300
|
end
|
293
301
|
|
294
302
|
def migrate_status
|
295
|
-
unless
|
303
|
+
unless migration_connection_pool.schema_migration.table_exists?
|
296
304
|
Kernel.abort "Schema migrations table does not exist yet."
|
297
305
|
end
|
298
306
|
|
299
307
|
# output
|
300
|
-
puts "\ndatabase: #{
|
308
|
+
puts "\ndatabase: #{migration_connection_pool.db_config.database}\n\n"
|
301
309
|
puts "#{'Status'.center(8)} #{'Migration ID'.ljust(14)} Migration Name"
|
302
310
|
puts "-" * 50
|
303
|
-
|
311
|
+
migration_connection_pool.migration_context.migrations_status.each do |status, version, name|
|
304
312
|
puts "#{status.center(8)} #{version.ljust(14)} #{name}"
|
305
313
|
end
|
306
314
|
puts
|
@@ -381,7 +389,7 @@ module ActiveRecord
|
|
381
389
|
raise ArgumentError, "unknown format #{format.inspect}"
|
382
390
|
end
|
383
391
|
|
384
|
-
|
392
|
+
migration_connection_pool.internal_metadata.create_table_and_set_flags(db_config.env_name, schema_sha1(file))
|
385
393
|
ensure
|
386
394
|
Migration.verbose = verbose_was
|
387
395
|
end
|
@@ -393,11 +401,12 @@ module ActiveRecord
|
|
393
401
|
|
394
402
|
return true unless file && File.exist?(file)
|
395
403
|
|
396
|
-
|
397
|
-
|
398
|
-
return false unless
|
404
|
+
with_temporary_pool(db_config) do |pool|
|
405
|
+
internal_metadata = pool.internal_metadata
|
406
|
+
return false unless internal_metadata.enabled?
|
407
|
+
return false unless internal_metadata.table_exists?
|
399
408
|
|
400
|
-
|
409
|
+
internal_metadata[:schema_sha1] == schema_sha1(file)
|
401
410
|
end
|
402
411
|
end
|
403
412
|
|
@@ -408,7 +417,7 @@ module ActiveRecord
|
|
408
417
|
|
409
418
|
with_temporary_pool(db_config, clobber: true) do
|
410
419
|
if schema_up_to_date?(db_config, format, file)
|
411
|
-
truncate_tables(db_config)
|
420
|
+
truncate_tables(db_config) unless ENV["SKIP_TEST_DATABASE_TRUNCATE"]
|
412
421
|
else
|
413
422
|
purge(db_config)
|
414
423
|
load_schema(db_config, format, file)
|
@@ -430,11 +439,11 @@ module ActiveRecord
|
|
430
439
|
case format
|
431
440
|
when :ruby
|
432
441
|
File.open(filename, "w:utf-8") do |file|
|
433
|
-
ActiveRecord::SchemaDumper.dump(
|
442
|
+
ActiveRecord::SchemaDumper.dump(migration_connection_pool, file)
|
434
443
|
end
|
435
444
|
when :sql
|
436
445
|
structure_dump(db_config, filename)
|
437
|
-
if
|
446
|
+
if migration_connection_pool.schema_migration.table_exists?
|
438
447
|
File.open(filename, "a") do |f|
|
439
448
|
f.puts migration_connection.dump_schema_information
|
440
449
|
f.print "\n"
|
@@ -456,14 +465,10 @@ module ActiveRecord
|
|
456
465
|
end
|
457
466
|
end
|
458
467
|
|
459
|
-
def cache_dump_filename(
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
"#{db_config_name}_schema_cache.yml"
|
464
|
-
end
|
465
|
-
|
466
|
-
schema_cache_path || ENV["SCHEMA_CACHE"] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, filename)
|
468
|
+
def cache_dump_filename(db_config, schema_cache_path: nil)
|
469
|
+
schema_cache_path ||
|
470
|
+
db_config.schema_cache_path ||
|
471
|
+
db_config.default_schema_cache_path(ActiveRecord::Tasks::DatabaseTasks.db_dir)
|
467
472
|
end
|
468
473
|
|
469
474
|
def load_schema_current(format = ActiveRecord.schema_format, file = nil, environment = env)
|
@@ -495,29 +500,29 @@ module ActiveRecord
|
|
495
500
|
# Dumps the schema cache in YAML format for the connection into the file
|
496
501
|
#
|
497
502
|
# ==== Examples
|
498
|
-
# ActiveRecord::Tasks::DatabaseTasks.dump_schema_cache(ActiveRecord::Base.
|
499
|
-
def dump_schema_cache(
|
500
|
-
|
503
|
+
# ActiveRecord::Tasks::DatabaseTasks.dump_schema_cache(ActiveRecord::Base.lease_connection, "tmp/schema_dump.yaml")
|
504
|
+
def dump_schema_cache(conn_or_pool, filename)
|
505
|
+
conn_or_pool.schema_cache.dump_to(filename)
|
501
506
|
end
|
502
507
|
|
503
508
|
def clear_schema_cache(filename)
|
504
509
|
FileUtils.rm_f filename, verbose: false
|
505
510
|
end
|
506
511
|
|
507
|
-
def
|
512
|
+
def with_temporary_pool_for_each(env: ActiveRecord::Tasks::DatabaseTasks.env, name: nil, clobber: false, &block) # :nodoc:
|
508
513
|
if name
|
509
514
|
db_config = ActiveRecord::Base.configurations.configs_for(env_name: env, name: name)
|
510
|
-
|
515
|
+
with_temporary_pool(db_config, clobber: clobber, &block)
|
511
516
|
else
|
512
517
|
ActiveRecord::Base.configurations.configs_for(env_name: env, name: name).each do |db_config|
|
513
|
-
|
518
|
+
with_temporary_pool(db_config, clobber: clobber, &block)
|
514
519
|
end
|
515
520
|
end
|
516
521
|
end
|
517
522
|
|
518
|
-
def with_temporary_connection(db_config, clobber: false) # :nodoc:
|
523
|
+
def with_temporary_connection(db_config, clobber: false, &block) # :nodoc:
|
519
524
|
with_temporary_pool(db_config, clobber: clobber) do |pool|
|
520
|
-
|
525
|
+
pool.with_connection(&block)
|
521
526
|
end
|
522
527
|
end
|
523
528
|
|
@@ -526,7 +531,11 @@ module ActiveRecord
|
|
526
531
|
end
|
527
532
|
|
528
533
|
def migration_connection # :nodoc:
|
529
|
-
migration_class.
|
534
|
+
migration_class.lease_connection
|
535
|
+
end
|
536
|
+
|
537
|
+
def migration_connection_pool # :nodoc:
|
538
|
+
migration_class.connection_pool
|
530
539
|
end
|
531
540
|
|
532
541
|
private
|
@@ -625,11 +634,11 @@ module ActiveRecord
|
|
625
634
|
|
626
635
|
def check_current_protected_environment!(db_config)
|
627
636
|
with_temporary_pool(db_config) do |pool|
|
628
|
-
|
629
|
-
current =
|
630
|
-
stored =
|
637
|
+
migration_context = pool.migration_context
|
638
|
+
current = migration_context.current_environment
|
639
|
+
stored = migration_context.last_stored_environment
|
631
640
|
|
632
|
-
if
|
641
|
+
if migration_context.protected_environment?
|
633
642
|
raise ActiveRecord::ProtectedEnvironmentError.new(stored)
|
634
643
|
end
|
635
644
|
|
@@ -639,6 +648,26 @@ module ActiveRecord
|
|
639
648
|
rescue ActiveRecord::NoDatabaseError
|
640
649
|
end
|
641
650
|
end
|
651
|
+
|
652
|
+
def initialize_database(db_config)
|
653
|
+
with_temporary_pool(db_config) do
|
654
|
+
begin
|
655
|
+
database_already_initialized = migration_connection_pool.schema_migration.table_exists?
|
656
|
+
rescue ActiveRecord::NoDatabaseError
|
657
|
+
create(db_config)
|
658
|
+
retry
|
659
|
+
end
|
660
|
+
|
661
|
+
unless database_already_initialized
|
662
|
+
schema_dump_path = schema_dump_path(db_config)
|
663
|
+
if schema_dump_path && File.exist?(schema_dump_path)
|
664
|
+
load_schema(db_config, ActiveRecord.schema_format, nil)
|
665
|
+
end
|
666
|
+
end
|
667
|
+
|
668
|
+
!database_already_initialized
|
669
|
+
end
|
670
|
+
end
|
642
671
|
end
|
643
672
|
end
|
644
673
|
end
|
@@ -3,8 +3,6 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
module Tasks # :nodoc:
|
5
5
|
class MySQLDatabaseTasks # :nodoc:
|
6
|
-
ER_DB_CREATE_EXISTS = 1007
|
7
|
-
|
8
6
|
def self.using_database_configurations?
|
9
7
|
true
|
10
8
|
end
|
@@ -71,7 +69,7 @@ module ActiveRecord
|
|
71
69
|
attr_reader :db_config, :configuration_hash
|
72
70
|
|
73
71
|
def connection
|
74
|
-
ActiveRecord::Base.
|
72
|
+
ActiveRecord::Base.lease_connection
|
75
73
|
end
|
76
74
|
|
77
75
|
def establish_connection(config = db_config)
|
@@ -50,9 +50,9 @@ module ActiveRecord
|
|
50
50
|
if ignore_tables.any?
|
51
51
|
ignore_tables = connection.data_sources.select { |table| ignore_tables.any? { |pattern| pattern === table } }
|
52
52
|
condition = ignore_tables.map { |table| connection.quote(table) }.join(", ")
|
53
|
-
args << "SELECT sql FROM sqlite_master WHERE tbl_name NOT IN (#{condition}) ORDER BY tbl_name, type DESC, name"
|
53
|
+
args << "SELECT sql || ';' FROM sqlite_master WHERE tbl_name NOT IN (#{condition}) ORDER BY tbl_name, type DESC, name"
|
54
54
|
else
|
55
|
-
args << ".schema"
|
55
|
+
args << ".schema --nosys"
|
56
56
|
end
|
57
57
|
run_cmd("sqlite3", args, filename)
|
58
58
|
end
|
@@ -66,11 +66,12 @@ module ActiveRecord
|
|
66
66
|
attr_reader :db_config, :root
|
67
67
|
|
68
68
|
def connection
|
69
|
-
ActiveRecord::Base.
|
69
|
+
ActiveRecord::Base.lease_connection
|
70
70
|
end
|
71
71
|
|
72
72
|
def establish_connection(config = db_config)
|
73
73
|
ActiveRecord::Base.establish_connection(config)
|
74
|
+
connection.connect!
|
74
75
|
end
|
75
76
|
|
76
77
|
def run_cmd(cmd, args, out)
|