activerecord 7.0.0 → 7.1.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 +1701 -1039
- data/MIT-LICENSE +1 -1
- data/README.rdoc +18 -18
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +18 -3
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +14 -6
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +21 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +17 -12
- data/lib/active_record/associations/collection_proxy.rb +22 -12
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +27 -17
- 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.rb +20 -14
- data/lib/active_record/associations/preloader/association.rb +27 -6
- data/lib/active_record/associations/preloader/through_association.rb +1 -1
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +362 -236
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/dirty.rb +52 -34
- data/lib/active_record/attribute_methods/primary_key.rb +76 -24
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +18 -5
- data/lib/active_record/attribute_methods/serialization.rb +172 -69
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +110 -28
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +56 -10
- data/lib/active_record/base.rb +10 -5
- 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 +164 -89
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +129 -31
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +52 -8
- 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 +302 -131
- data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +513 -106
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +217 -104
- 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 +23 -144
- data/lib/active_record/connection_adapters/mysql/quoting.rb +29 -12
- 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 +38 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
- data/lib/active_record/connection_adapters/pool_config.rb +14 -5
- 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 +75 -45
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -2
- 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/quoting.rb +41 -8
- 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 +131 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +372 -63
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +359 -197
- data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +52 -39
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +22 -5
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +41 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +242 -81
- 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 +254 -0
- data/lib/active_record/connection_adapters.rb +3 -1
- data/lib/active_record/connection_handling.rb +73 -96
- data/lib/active_record/core.rb +142 -153
- data/lib/active_record/counter_cache.rb +46 -25
- data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -0
- data/lib/active_record/database_configurations/database_config.rb +9 -3
- data/lib/active_record/database_configurations/hash_config.rb +22 -12
- data/lib/active_record/database_configurations/url_config.rb +17 -11
- data/lib/active_record/database_configurations.rb +87 -34
- data/lib/active_record/delegated_type.rb +9 -4
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +2 -0
- data/lib/active_record/disable_joins_association_relation.rb +1 -1
- 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 +38 -22
- data/lib/active_record/encryption/encrypted_attribute_type.rb +19 -8
- data/lib/active_record/encryption/encryptor.rb +7 -7
- 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_serializer.rb +2 -0
- data/lib/active_record/encryption/properties.rb +4 -4
- data/lib/active_record/encryption/scheme.rb +20 -23
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +113 -29
- data/lib/active_record/errors.rb +108 -15
- data/lib/active_record/explain.rb +23 -3
- 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 +121 -73
- data/lib/active_record/future_result.rb +30 -5
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +30 -16
- data/lib/active_record/insert_all.rb +57 -10
- data/lib/active_record/integration.rb +10 -10
- data/lib/active_record/internal_metadata.rb +120 -30
- data/lib/active_record/locking/optimistic.rb +32 -18
- data/lib/active_record/locking/pessimistic.rb +8 -5
- data/lib/active_record/log_subscriber.rb +39 -17
- 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 +108 -10
- data/lib/active_record/migration/compatibility.rb +158 -64
- data/lib/active_record/migration/default_strategy.rb +23 -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 +274 -117
- data/lib/active_record/model_schema.rb +86 -54
- data/lib/active_record/nested_attributes.rb +24 -6
- data/lib/active_record/normalization.rb +167 -0
- data/lib/active_record/persistence.rb +200 -47
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- data/lib/active_record/query_logs.rb +87 -51
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +16 -3
- data/lib/active_record/railtie.rb +128 -62
- data/lib/active_record/railties/controller_runtime.rb +12 -8
- data/lib/active_record/railties/databases.rake +145 -146
- 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 +189 -45
- data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
- data/lib/active_record/relation/batches.rb +190 -61
- data/lib/active_record/relation/calculations.rb +208 -83
- data/lib/active_record/relation/delegation.rb +23 -9
- data/lib/active_record/relation/finder_methods.rb +77 -16
- data/lib/active_record/relation/merger.rb +2 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +26 -14
- data/lib/active_record/relation/query_attribute.rb +25 -1
- data/lib/active_record/relation/query_methods.rb +430 -77
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +98 -41
- data/lib/active_record/result.rb +25 -9
- data/lib/active_record/runtime_registry.rb +10 -1
- data/lib/active_record/sanitization.rb +57 -16
- data/lib/active_record/schema.rb +36 -22
- data/lib/active_record/schema_dumper.rb +65 -23
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +20 -12
- data/lib/active_record/scoping/named.rb +2 -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 +9 -7
- data/lib/active_record/store.rb +16 -11
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +16 -3
- data/lib/active_record/tasks/database_tasks.rb +138 -107
- 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 +15 -7
- data/lib/active_record/test_fixtures.rb +123 -99
- data/lib/active_record/timestamp.rb +27 -15
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +39 -13
- data/lib/active_record/translation.rb +1 -1
- 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 +8 -4
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +3 -3
- 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 +50 -5
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +143 -16
- 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/and.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -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/node.rb +111 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +4 -0
- data/lib/arel/predications.rb +2 -0
- data/lib/arel/table.rb +9 -5
- data/lib/arel/visitors/mysql.rb +8 -1
- data/lib/arel/visitors/to_sql.rb +81 -17
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +16 -2
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- 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 +51 -15
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record/runtime_registry"
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
module Railties # :nodoc:
|
7
|
+
module JobRuntime # :nodoc:
|
8
|
+
private
|
9
|
+
def instrument(operation, payload = {}, &block)
|
10
|
+
if operation == :perform && block
|
11
|
+
super(operation, payload) do
|
12
|
+
db_runtime_before_perform = ActiveRecord::RuntimeRegistry.sql_runtime
|
13
|
+
result = block.call
|
14
|
+
payload[:db_runtime] = ActiveRecord::RuntimeRegistry.sql_runtime - db_runtime_before_perform
|
15
|
+
result
|
16
|
+
end
|
17
|
+
else
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,6 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
|
+
class ReadonlyAttributeError < ActiveRecordError
|
5
|
+
end
|
6
|
+
|
4
7
|
module ReadonlyAttributes
|
5
8
|
extend ActiveSupport::Concern
|
6
9
|
|
@@ -9,10 +12,11 @@ module ActiveRecord
|
|
9
12
|
end
|
10
13
|
|
11
14
|
module ClassMethods
|
12
|
-
# Attributes listed as readonly will be used to create a new record
|
13
|
-
#
|
15
|
+
# Attributes listed as readonly will be used to create a new record.
|
16
|
+
# Assigning a new value to a readonly attribute on a persisted record raises an error.
|
14
17
|
#
|
15
|
-
#
|
18
|
+
# By setting +config.active_record.raise_on_assign_to_attr_readonly+ to +false+, it will
|
19
|
+
# not raise. The value will change in memory, but will not be persisted on +save+.
|
16
20
|
#
|
17
21
|
# ==== Examples
|
18
22
|
#
|
@@ -21,9 +25,14 @@ module ActiveRecord
|
|
21
25
|
# end
|
22
26
|
#
|
23
27
|
# post = Post.create!(title: "Introducing Ruby on Rails!")
|
24
|
-
# post.
|
28
|
+
# post.title = "a different title" # raises ActiveRecord::ReadonlyAttributeError
|
29
|
+
# post.update(title: "a different title") # raises ActiveRecord::ReadonlyAttributeError
|
25
30
|
def attr_readonly(*attributes)
|
26
|
-
self._attr_readonly
|
31
|
+
self._attr_readonly |= attributes.map(&:to_s)
|
32
|
+
|
33
|
+
if ActiveRecord.raise_on_assign_to_attr_readonly
|
34
|
+
include(HasReadonlyAttributes)
|
35
|
+
end
|
27
36
|
end
|
28
37
|
|
29
38
|
# Returns an array of all the attributes that have been specified as readonly.
|
@@ -35,5 +44,23 @@ module ActiveRecord
|
|
35
44
|
_attr_readonly.include?(name)
|
36
45
|
end
|
37
46
|
end
|
47
|
+
|
48
|
+
module HasReadonlyAttributes # :nodoc:
|
49
|
+
def write_attribute(attr_name, value)
|
50
|
+
if !new_record? && self.class.readonly_attribute?(attr_name.to_s)
|
51
|
+
raise ReadonlyAttributeError.new(attr_name)
|
52
|
+
end
|
53
|
+
|
54
|
+
super
|
55
|
+
end
|
56
|
+
|
57
|
+
def _write_attribute(attr_name, value)
|
58
|
+
if !new_record? && self.class.readonly_attribute?(attr_name.to_s)
|
59
|
+
raise ReadonlyAttributeError.new(attr_name)
|
60
|
+
end
|
61
|
+
|
62
|
+
super
|
63
|
+
end
|
64
|
+
end
|
38
65
|
end
|
39
66
|
end
|
@@ -46,6 +46,8 @@ module ActiveRecord
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
+
# = Active Record Reflection
|
50
|
+
#
|
49
51
|
# \Reflection enables the ability to examine the associations and aggregations of
|
50
52
|
# Active Record classes and objects. This information, for example,
|
51
53
|
# can be used in a form builder that takes an Active Record object
|
@@ -128,6 +130,14 @@ module ActiveRecord
|
|
128
130
|
def clear_reflections_cache # :nodoc:
|
129
131
|
@__reflections = nil
|
130
132
|
end
|
133
|
+
|
134
|
+
private
|
135
|
+
def inherited(subclass)
|
136
|
+
super
|
137
|
+
subclass.class_eval do
|
138
|
+
@__reflections = nil
|
139
|
+
end
|
140
|
+
end
|
131
141
|
end
|
132
142
|
|
133
143
|
# Holds all the methods that are shared between MacroReflection and ThroughReflection.
|
@@ -144,6 +154,14 @@ module ActiveRecord
|
|
144
154
|
# PolymorphicReflection
|
145
155
|
# RuntimeReflection
|
146
156
|
class AbstractReflection # :nodoc:
|
157
|
+
def initialize
|
158
|
+
@class_name = nil
|
159
|
+
@counter_cache_column = nil
|
160
|
+
@inverse_of = nil
|
161
|
+
@inverse_which_updates_counter_cache_defined = false
|
162
|
+
@inverse_which_updates_counter_cache = nil
|
163
|
+
end
|
164
|
+
|
147
165
|
def through_reflection?
|
148
166
|
false
|
149
167
|
end
|
@@ -183,10 +201,14 @@ module ActiveRecord
|
|
183
201
|
|
184
202
|
scope_chain_items.inject(klass_scope, &:merge!)
|
185
203
|
|
186
|
-
|
187
|
-
|
204
|
+
primary_key_column_names = Array(join_primary_key)
|
205
|
+
foreign_key_column_names = Array(join_foreign_key)
|
188
206
|
|
189
|
-
|
207
|
+
primary_foreign_key_pairs = primary_key_column_names.zip(foreign_key_column_names)
|
208
|
+
|
209
|
+
primary_foreign_key_pairs.each do |primary_key_column_name, foreign_key_column_name|
|
210
|
+
klass_scope.where!(table[primary_key_column_name].eq(foreign_table[foreign_key_column_name]))
|
211
|
+
end
|
190
212
|
|
191
213
|
if klass.finder_needs_type_condition?
|
192
214
|
klass_scope.where!(klass.send(:type_condition, table))
|
@@ -231,11 +253,11 @@ module ActiveRecord
|
|
231
253
|
end
|
232
254
|
|
233
255
|
def check_validity_of_inverse!
|
234
|
-
|
235
|
-
if
|
256
|
+
if !polymorphic? && has_inverse?
|
257
|
+
if inverse_of.nil?
|
236
258
|
raise InverseOfAssociationNotFoundError.new(self)
|
237
259
|
end
|
238
|
-
if
|
260
|
+
if inverse_of == self
|
239
261
|
raise InverseOfAssociationRecursiveError.new(self)
|
240
262
|
end
|
241
263
|
end
|
@@ -252,10 +274,16 @@ module ActiveRecord
|
|
252
274
|
#
|
253
275
|
# Hence this method.
|
254
276
|
def inverse_which_updates_counter_cache
|
255
|
-
|
256
|
-
|
257
|
-
|
277
|
+
unless @inverse_which_updates_counter_cache_defined
|
278
|
+
if counter_cache_column
|
279
|
+
inverse_candidates = inverse_of ? [inverse_of] : klass.reflect_on_all_associations(:belongs_to)
|
280
|
+
@inverse_which_updates_counter_cache = inverse_candidates.find do |inverse|
|
281
|
+
inverse.counter_cache_column == counter_cache_column && (inverse.polymorphic? || inverse.klass == active_record)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
@inverse_which_updates_counter_cache_defined = true
|
258
285
|
end
|
286
|
+
@inverse_which_updates_counter_cache
|
259
287
|
end
|
260
288
|
alias inverse_updates_counter_cache? inverse_which_updates_counter_cache
|
261
289
|
|
@@ -297,6 +325,12 @@ module ActiveRecord
|
|
297
325
|
options[:strict_loading]
|
298
326
|
end
|
299
327
|
|
328
|
+
def strict_loading_violation_message(owner)
|
329
|
+
message = +"`#{owner}` is marked for strict_loading."
|
330
|
+
message << " The #{polymorphic? ? "polymorphic association" : "#{klass} association"}"
|
331
|
+
message << " named `:#{name}` cannot be lazily loaded."
|
332
|
+
end
|
333
|
+
|
300
334
|
protected
|
301
335
|
def actual_source_reflection # FIXME: this is a horrible name
|
302
336
|
self
|
@@ -340,6 +374,7 @@ module ActiveRecord
|
|
340
374
|
attr_reader :plural_name # :nodoc:
|
341
375
|
|
342
376
|
def initialize(name, scope, options, active_record)
|
377
|
+
super()
|
343
378
|
@name = name
|
344
379
|
@scope = scope
|
345
380
|
@options = options
|
@@ -347,6 +382,7 @@ module ActiveRecord
|
|
347
382
|
@klass = options[:anonymous_class]
|
348
383
|
@plural_name = active_record.pluralize_table_names ?
|
349
384
|
name.to_s.pluralize : name.to_s
|
385
|
+
validate_reflection!
|
350
386
|
end
|
351
387
|
|
352
388
|
def autosave=(autosave)
|
@@ -398,6 +434,17 @@ module ActiveRecord
|
|
398
434
|
def derive_class_name
|
399
435
|
name.to_s.camelize
|
400
436
|
end
|
437
|
+
|
438
|
+
def validate_reflection!
|
439
|
+
return unless options[:foreign_key].is_a?(Array)
|
440
|
+
|
441
|
+
message = <<~MSG.squish
|
442
|
+
Passing #{options[:foreign_key]} array to :foreign_key option
|
443
|
+
on the #{active_record}##{name} association is not supported.
|
444
|
+
Use the query_constraints: #{options[:foreign_key]} option instead to represent a composite foreign key.
|
445
|
+
MSG
|
446
|
+
raise ArgumentError, message
|
447
|
+
end
|
401
448
|
end
|
402
449
|
|
403
450
|
# Holds all the metadata about an aggregation as it was specified in the
|
@@ -417,23 +464,23 @@ module ActiveRecord
|
|
417
464
|
raise ArgumentError, "Polymorphic associations do not support computing the class."
|
418
465
|
end
|
419
466
|
|
420
|
-
msg = <<-MSG.squish
|
421
|
-
Rails couldn't find a valid model for #{name} association.
|
422
|
-
Please provide the :class_name option on the association declaration.
|
423
|
-
If :class_name is already provided, make sure it's an ActiveRecord::Base subclass.
|
424
|
-
MSG
|
425
|
-
|
426
467
|
begin
|
427
468
|
klass = active_record.send(:compute_type, name)
|
428
|
-
|
429
|
-
|
430
|
-
|
469
|
+
rescue NameError => error
|
470
|
+
if error.name.match?(/(?:\A|::)#{name}\z/)
|
471
|
+
message = "Missing model class #{name} for the #{active_record}##{self.name} association."
|
472
|
+
message += " You can specify a different model class with the :class_name option." unless options[:class_name]
|
473
|
+
raise NameError.new(message, name)
|
474
|
+
else
|
475
|
+
raise
|
431
476
|
end
|
477
|
+
end
|
432
478
|
|
433
|
-
|
434
|
-
|
435
|
-
raise NameError, msg
|
479
|
+
unless klass < ActiveRecord::Base
|
480
|
+
raise ArgumentError, "The #{name} model class for the #{active_record}##{self.name} association is not an ActiveRecord::Base subclass."
|
436
481
|
end
|
482
|
+
|
483
|
+
klass
|
437
484
|
end
|
438
485
|
|
439
486
|
attr_reader :type, :foreign_type
|
@@ -443,6 +490,10 @@ module ActiveRecord
|
|
443
490
|
super
|
444
491
|
@type = -(options[:foreign_type]&.to_s || "#{options[:as]}_type") if options[:as]
|
445
492
|
@foreign_type = -(options[:foreign_type]&.to_s || "#{name}_type") if options[:polymorphic]
|
493
|
+
@join_table = nil
|
494
|
+
@foreign_key = nil
|
495
|
+
@association_foreign_key = nil
|
496
|
+
@association_primary_key = nil
|
446
497
|
|
447
498
|
ensure_option_not_given_as_class!(:class_name)
|
448
499
|
end
|
@@ -459,8 +510,20 @@ module ActiveRecord
|
|
459
510
|
@join_table ||= -(options[:join_table]&.to_s || derive_join_table)
|
460
511
|
end
|
461
512
|
|
462
|
-
def foreign_key
|
463
|
-
@foreign_key ||=
|
513
|
+
def foreign_key(infer_from_inverse_of: true)
|
514
|
+
@foreign_key ||= if options[:query_constraints]
|
515
|
+
options[:query_constraints].map { |fk| fk.to_s.freeze }.freeze
|
516
|
+
elsif options[:foreign_key]
|
517
|
+
options[:foreign_key].to_s
|
518
|
+
else
|
519
|
+
derived_fk = derive_foreign_key(infer_from_inverse_of: infer_from_inverse_of)
|
520
|
+
|
521
|
+
if active_record.has_query_constraints?
|
522
|
+
derived_fk = derive_fk_query_constraints(derived_fk)
|
523
|
+
end
|
524
|
+
|
525
|
+
derived_fk
|
526
|
+
end
|
464
527
|
end
|
465
528
|
|
466
529
|
def association_foreign_key
|
@@ -472,19 +535,46 @@ module ActiveRecord
|
|
472
535
|
end
|
473
536
|
|
474
537
|
def active_record_primary_key
|
475
|
-
|
538
|
+
custom_primary_key = options[:primary_key]
|
539
|
+
@active_record_primary_key ||= if custom_primary_key
|
540
|
+
if custom_primary_key.is_a?(Array)
|
541
|
+
custom_primary_key.map { |pk| pk.to_s.freeze }.freeze
|
542
|
+
else
|
543
|
+
custom_primary_key.to_s.freeze
|
544
|
+
end
|
545
|
+
elsif active_record.has_query_constraints? || options[:query_constraints]
|
546
|
+
active_record.query_constraints_list
|
547
|
+
elsif active_record.composite_primary_key?
|
548
|
+
# If active_record has composite primary key of shape [:<tenant_key>, :id], infer primary_key as :id
|
549
|
+
primary_key = primary_key(active_record)
|
550
|
+
primary_key.include?("id") ? "id" : primary_key.freeze
|
551
|
+
else
|
552
|
+
primary_key(active_record).freeze
|
553
|
+
end
|
476
554
|
end
|
477
555
|
|
478
556
|
def join_primary_key(klass = nil)
|
479
557
|
foreign_key
|
480
558
|
end
|
481
559
|
|
560
|
+
def join_primary_type
|
561
|
+
type
|
562
|
+
end
|
563
|
+
|
482
564
|
def join_foreign_key
|
483
565
|
active_record_primary_key
|
484
566
|
end
|
485
567
|
|
486
568
|
def check_validity!
|
487
569
|
check_validity_of_inverse!
|
570
|
+
|
571
|
+
if !polymorphic? && (klass.composite_primary_key? || active_record.composite_primary_key?)
|
572
|
+
if (has_one? || collection?) && Array(active_record_primary_key).length != Array(foreign_key).length
|
573
|
+
raise CompositePrimaryKeyMismatchError.new(self)
|
574
|
+
elsif belongs_to? && Array(association_primary_key).length != Array(foreign_key).length
|
575
|
+
raise CompositePrimaryKeyMismatchError.new(self)
|
576
|
+
end
|
577
|
+
end
|
488
578
|
end
|
489
579
|
|
490
580
|
def check_eager_loadable!
|
@@ -500,7 +590,7 @@ module ActiveRecord
|
|
500
590
|
end
|
501
591
|
|
502
592
|
def join_id_for(owner) # :nodoc:
|
503
|
-
owner
|
593
|
+
Array(join_foreign_key).map { |key| owner._read_attribute(key) }
|
504
594
|
end
|
505
595
|
|
506
596
|
def through_reflection
|
@@ -582,6 +672,10 @@ module ActiveRecord
|
|
582
672
|
options[:polymorphic]
|
583
673
|
end
|
584
674
|
|
675
|
+
def polymorphic_name
|
676
|
+
active_record.polymorphic_name
|
677
|
+
end
|
678
|
+
|
585
679
|
def add_as_source(seed)
|
586
680
|
seed
|
587
681
|
end
|
@@ -617,7 +711,9 @@ module ActiveRecord
|
|
617
711
|
|
618
712
|
begin
|
619
713
|
reflection = klass._reflect_on_association(inverse_name)
|
620
|
-
rescue NameError
|
714
|
+
rescue NameError => error
|
715
|
+
raise unless error.name.to_s == class_name
|
716
|
+
|
621
717
|
# Give up: we couldn't compute the klass type so we won't be able
|
622
718
|
# to find any associations either.
|
623
719
|
reflection = false
|
@@ -674,16 +770,56 @@ module ActiveRecord
|
|
674
770
|
class_name.camelize
|
675
771
|
end
|
676
772
|
|
677
|
-
def derive_foreign_key
|
773
|
+
def derive_foreign_key(infer_from_inverse_of: true)
|
678
774
|
if belongs_to?
|
679
775
|
"#{name}_id"
|
680
776
|
elsif options[:as]
|
681
777
|
"#{options[:as]}_id"
|
778
|
+
elsif options[:inverse_of] && infer_from_inverse_of
|
779
|
+
inverse_of.foreign_key(infer_from_inverse_of: false)
|
682
780
|
else
|
683
781
|
active_record.model_name.to_s.foreign_key
|
684
782
|
end
|
685
783
|
end
|
686
784
|
|
785
|
+
def derive_fk_query_constraints(foreign_key)
|
786
|
+
primary_query_constraints = active_record.query_constraints_list
|
787
|
+
owner_pk = active_record.primary_key
|
788
|
+
|
789
|
+
if primary_query_constraints.size != 2
|
790
|
+
raise ArgumentError, <<~MSG.squish
|
791
|
+
The query constraints list on the `#{active_record}` model has more than 2
|
792
|
+
attributes. Active Record is unable to derive the query constraints
|
793
|
+
for the association. You need to explicitly define the query constraints
|
794
|
+
for this association.
|
795
|
+
MSG
|
796
|
+
end
|
797
|
+
|
798
|
+
if !primary_query_constraints.include?(owner_pk)
|
799
|
+
raise ArgumentError, <<~MSG.squish
|
800
|
+
The query constraints on the `#{active_record}` model does not include the primary
|
801
|
+
key so Active Record is unable to derive the foreign key constraints for
|
802
|
+
the association. You need to explicitly define the query constraints for this
|
803
|
+
association.
|
804
|
+
MSG
|
805
|
+
end
|
806
|
+
|
807
|
+
first_key, last_key = primary_query_constraints
|
808
|
+
|
809
|
+
if first_key == owner_pk
|
810
|
+
[foreign_key, last_key.to_s]
|
811
|
+
elsif last_key == owner_pk
|
812
|
+
[first_key.to_s, foreign_key]
|
813
|
+
else
|
814
|
+
raise ArgumentError, <<~MSG.squish
|
815
|
+
Active Record couldn't correctly interpret the query constraints
|
816
|
+
for the `#{active_record}` model. The query constraints on `#{active_record}` are
|
817
|
+
`#{primary_query_constraints}` and the foreign key is `#{foreign_key}`.
|
818
|
+
You need to explicitly set the query constraints for this association.
|
819
|
+
MSG
|
820
|
+
end
|
821
|
+
end
|
822
|
+
|
687
823
|
def derive_join_table
|
688
824
|
ModelSchema.derive_join_table_name active_record.table_name, klass.table_name
|
689
825
|
end
|
@@ -734,6 +870,12 @@ module ActiveRecord
|
|
734
870
|
def association_primary_key(klass = nil)
|
735
871
|
if primary_key = options[:primary_key]
|
736
872
|
@association_primary_key ||= -primary_key.to_s
|
873
|
+
elsif !polymorphic? && ((klass || self.klass).has_query_constraints? || options[:query_constraints])
|
874
|
+
(klass || self.klass).composite_query_constraints_list
|
875
|
+
elsif (klass || self.klass).composite_primary_key?
|
876
|
+
# If klass has composite primary key of shape [:<tenant_key>, :id], infer primary_key as :id
|
877
|
+
primary_key = (klass || self.klass).primary_key
|
878
|
+
primary_key.include?("id") ? "id" : primary_key
|
737
879
|
else
|
738
880
|
primary_key(klass || self.klass)
|
739
881
|
end
|
@@ -772,6 +914,7 @@ module ActiveRecord
|
|
772
914
|
:active_record_primary_key, :join_foreign_key, to: :source_reflection
|
773
915
|
|
774
916
|
def initialize(delegate_reflection)
|
917
|
+
super()
|
775
918
|
@delegate_reflection = delegate_reflection
|
776
919
|
@klass = delegate_reflection.options[:anonymous_class]
|
777
920
|
@source_reflection_name = delegate_reflection.options[:source]
|
@@ -905,24 +1048,23 @@ module ActiveRecord
|
|
905
1048
|
end
|
906
1049
|
|
907
1050
|
def source_reflection_name # :nodoc:
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
1051
|
+
@source_reflection_name ||= begin
|
1052
|
+
names = [name.to_s.singularize, name].collect(&:to_sym).uniq
|
1053
|
+
names = names.find_all { |n|
|
1054
|
+
through_reflection.klass._reflect_on_association(n)
|
1055
|
+
}
|
1056
|
+
|
1057
|
+
if names.length > 1
|
1058
|
+
raise AmbiguousSourceReflectionForThroughAssociation.new(
|
1059
|
+
active_record.name,
|
1060
|
+
macro,
|
1061
|
+
name,
|
1062
|
+
options,
|
1063
|
+
source_reflection_names
|
1064
|
+
)
|
1065
|
+
end
|
1066
|
+
names.first
|
923
1067
|
end
|
924
|
-
|
925
|
-
@source_reflection_name = names.first
|
926
1068
|
end
|
927
1069
|
|
928
1070
|
def source_options
|
@@ -1026,12 +1168,13 @@ module ActiveRecord
|
|
1026
1168
|
:name, :scope_for, to: :@reflection
|
1027
1169
|
|
1028
1170
|
def initialize(reflection, previous_reflection)
|
1171
|
+
super()
|
1029
1172
|
@reflection = reflection
|
1030
1173
|
@previous_reflection = previous_reflection
|
1031
1174
|
end
|
1032
1175
|
|
1033
1176
|
def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
|
1034
|
-
scopes = @previous_reflection.join_scopes(table, predicate_builder, record) + super
|
1177
|
+
scopes = @previous_reflection.join_scopes(table, predicate_builder, klass, record) + super
|
1035
1178
|
scopes << build_scope(table, predicate_builder, klass).instance_exec(record, &source_type_scope)
|
1036
1179
|
end
|
1037
1180
|
|
@@ -1051,6 +1194,7 @@ module ActiveRecord
|
|
1051
1194
|
delegate :scope, :type, :constraints, :join_foreign_key, to: :@reflection
|
1052
1195
|
|
1053
1196
|
def initialize(reflection, association)
|
1197
|
+
super()
|
1054
1198
|
@reflection = reflection
|
1055
1199
|
@association = association
|
1056
1200
|
end
|
@@ -5,11 +5,13 @@ module ActiveRecord
|
|
5
5
|
class BatchEnumerator
|
6
6
|
include Enumerable
|
7
7
|
|
8
|
-
def initialize(of: 1000, start: nil, finish: nil, relation:) # :nodoc:
|
8
|
+
def initialize(of: 1000, start: nil, finish: nil, relation:, order: :asc, use_ranges: nil) # :nodoc:
|
9
9
|
@of = of
|
10
10
|
@relation = relation
|
11
11
|
@start = start
|
12
12
|
@finish = finish
|
13
|
+
@order = order
|
14
|
+
@use_ranges = use_ranges
|
13
15
|
end
|
14
16
|
|
15
17
|
# The primary key value from which the BatchEnumerator starts, inclusive of the value.
|
@@ -50,7 +52,7 @@ module ActiveRecord
|
|
50
52
|
def each_record(&block)
|
51
53
|
return to_enum(:each_record) unless block_given?
|
52
54
|
|
53
|
-
@relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: true).each do |relation|
|
55
|
+
@relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: true, order: @order).each do |relation|
|
54
56
|
relation.records.each(&block)
|
55
57
|
end
|
56
58
|
end
|
@@ -90,7 +92,7 @@ module ActiveRecord
|
|
90
92
|
# relation.update_all(awesome: true)
|
91
93
|
# end
|
92
94
|
def each(&block)
|
93
|
-
enum = @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: false)
|
95
|
+
enum = @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: false, order: @order, use_ranges: @use_ranges)
|
94
96
|
return enum.each(&block) if block_given?
|
95
97
|
enum
|
96
98
|
end
|