activerecord 7.0.0 → 7.1.0
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 +1607 -1040
- data/MIT-LICENSE +1 -1
- data/README.rdoc +17 -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 +345 -219
- 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 +40 -26
- 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 +128 -32
- 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 -129
- data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +504 -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 +148 -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 +3 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +72 -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/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +3 -1
- 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 +358 -57
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +343 -181
- 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 +45 -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 +98 -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 +136 -148
- 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 -71
- 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 +114 -27
- 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 +2 -2
- data/lib/active_record/inheritance.rb +30 -16
- data/lib/active_record/insert_all.rb +55 -8
- data/lib/active_record/integration.rb +10 -10
- data/lib/active_record/internal_metadata.rb +118 -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 +104 -9
- 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.rb +271 -117
- data/lib/active_record/model_schema.rb +82 -50
- data/lib/active_record/nested_attributes.rb +23 -3
- data/lib/active_record/normalization.rb +159 -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 +127 -61
- data/lib/active_record/railties/controller_runtime.rb +12 -8
- data/lib/active_record/railties/databases.rake +142 -143
- 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 +177 -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 +200 -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 +429 -76
- 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 +26 -14
- 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 +0 -8
- 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 +50 -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)
|
206
|
+
|
207
|
+
primary_foreign_key_pairs = primary_key_column_names.zip(foreign_key_column_names)
|
188
208
|
|
189
|
-
|
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
|
@@ -417,23 +452,23 @@ module ActiveRecord
|
|
417
452
|
raise ArgumentError, "Polymorphic associations do not support computing the class."
|
418
453
|
end
|
419
454
|
|
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
455
|
begin
|
427
456
|
klass = active_record.send(:compute_type, name)
|
428
|
-
|
429
|
-
|
430
|
-
|
457
|
+
rescue NameError => error
|
458
|
+
if error.name.match?(/(?:\A|::)#{name}\z/)
|
459
|
+
message = "Missing model class #{name} for the #{active_record}##{self.name} association."
|
460
|
+
message += " You can specify a different model class with the :class_name option." unless options[:class_name]
|
461
|
+
raise NameError.new(message, name)
|
462
|
+
else
|
463
|
+
raise
|
431
464
|
end
|
465
|
+
end
|
432
466
|
|
433
|
-
|
434
|
-
|
435
|
-
raise NameError, msg
|
467
|
+
unless klass < ActiveRecord::Base
|
468
|
+
raise ArgumentError, "The #{name} model class for the #{active_record}##{self.name} association is not an ActiveRecord::Base subclass."
|
436
469
|
end
|
470
|
+
|
471
|
+
klass
|
437
472
|
end
|
438
473
|
|
439
474
|
attr_reader :type, :foreign_type
|
@@ -443,6 +478,10 @@ module ActiveRecord
|
|
443
478
|
super
|
444
479
|
@type = -(options[:foreign_type]&.to_s || "#{options[:as]}_type") if options[:as]
|
445
480
|
@foreign_type = -(options[:foreign_type]&.to_s || "#{name}_type") if options[:polymorphic]
|
481
|
+
@join_table = nil
|
482
|
+
@foreign_key = nil
|
483
|
+
@association_foreign_key = nil
|
484
|
+
@association_primary_key = nil
|
446
485
|
|
447
486
|
ensure_option_not_given_as_class!(:class_name)
|
448
487
|
end
|
@@ -459,8 +498,20 @@ module ActiveRecord
|
|
459
498
|
@join_table ||= -(options[:join_table]&.to_s || derive_join_table)
|
460
499
|
end
|
461
500
|
|
462
|
-
def foreign_key
|
463
|
-
@foreign_key ||=
|
501
|
+
def foreign_key(infer_from_inverse_of: true)
|
502
|
+
@foreign_key ||= if options[:query_constraints]
|
503
|
+
options[:query_constraints].map { |fk| fk.to_s.freeze }.freeze
|
504
|
+
elsif options[:foreign_key]
|
505
|
+
options[:foreign_key].to_s
|
506
|
+
else
|
507
|
+
derived_fk = derive_foreign_key(infer_from_inverse_of: infer_from_inverse_of)
|
508
|
+
|
509
|
+
if active_record.has_query_constraints?
|
510
|
+
derived_fk = derive_fk_query_constraints(derived_fk)
|
511
|
+
end
|
512
|
+
|
513
|
+
derived_fk
|
514
|
+
end
|
464
515
|
end
|
465
516
|
|
466
517
|
def association_foreign_key
|
@@ -472,19 +523,46 @@ module ActiveRecord
|
|
472
523
|
end
|
473
524
|
|
474
525
|
def active_record_primary_key
|
475
|
-
|
526
|
+
custom_primary_key = options[:primary_key]
|
527
|
+
@active_record_primary_key ||= if custom_primary_key
|
528
|
+
if custom_primary_key.is_a?(Array)
|
529
|
+
custom_primary_key.map { |pk| pk.to_s.freeze }.freeze
|
530
|
+
else
|
531
|
+
custom_primary_key.to_s.freeze
|
532
|
+
end
|
533
|
+
elsif active_record.has_query_constraints? || options[:query_constraints]
|
534
|
+
active_record.query_constraints_list
|
535
|
+
elsif active_record.composite_primary_key?
|
536
|
+
# If active_record has composite primary key of shape [:<tenant_key>, :id], infer primary_key as :id
|
537
|
+
primary_key = primary_key(active_record)
|
538
|
+
primary_key.include?("id") ? "id" : primary_key.freeze
|
539
|
+
else
|
540
|
+
primary_key(active_record).freeze
|
541
|
+
end
|
476
542
|
end
|
477
543
|
|
478
544
|
def join_primary_key(klass = nil)
|
479
545
|
foreign_key
|
480
546
|
end
|
481
547
|
|
548
|
+
def join_primary_type
|
549
|
+
type
|
550
|
+
end
|
551
|
+
|
482
552
|
def join_foreign_key
|
483
553
|
active_record_primary_key
|
484
554
|
end
|
485
555
|
|
486
556
|
def check_validity!
|
487
557
|
check_validity_of_inverse!
|
558
|
+
|
559
|
+
if !polymorphic? && (klass.composite_primary_key? || active_record.composite_primary_key?)
|
560
|
+
if (has_one? || collection?) && Array(active_record_primary_key).length != Array(foreign_key).length
|
561
|
+
raise CompositePrimaryKeyMismatchError.new(self)
|
562
|
+
elsif belongs_to? && Array(association_primary_key).length != Array(foreign_key).length
|
563
|
+
raise CompositePrimaryKeyMismatchError.new(self)
|
564
|
+
end
|
565
|
+
end
|
488
566
|
end
|
489
567
|
|
490
568
|
def check_eager_loadable!
|
@@ -500,7 +578,7 @@ module ActiveRecord
|
|
500
578
|
end
|
501
579
|
|
502
580
|
def join_id_for(owner) # :nodoc:
|
503
|
-
owner
|
581
|
+
Array(join_foreign_key).map { |key| owner._read_attribute(key) }
|
504
582
|
end
|
505
583
|
|
506
584
|
def through_reflection
|
@@ -582,6 +660,10 @@ module ActiveRecord
|
|
582
660
|
options[:polymorphic]
|
583
661
|
end
|
584
662
|
|
663
|
+
def polymorphic_name
|
664
|
+
active_record.polymorphic_name
|
665
|
+
end
|
666
|
+
|
585
667
|
def add_as_source(seed)
|
586
668
|
seed
|
587
669
|
end
|
@@ -617,7 +699,9 @@ module ActiveRecord
|
|
617
699
|
|
618
700
|
begin
|
619
701
|
reflection = klass._reflect_on_association(inverse_name)
|
620
|
-
rescue NameError
|
702
|
+
rescue NameError => error
|
703
|
+
raise unless error.name.to_s == class_name
|
704
|
+
|
621
705
|
# Give up: we couldn't compute the klass type so we won't be able
|
622
706
|
# to find any associations either.
|
623
707
|
reflection = false
|
@@ -674,16 +758,56 @@ module ActiveRecord
|
|
674
758
|
class_name.camelize
|
675
759
|
end
|
676
760
|
|
677
|
-
def derive_foreign_key
|
761
|
+
def derive_foreign_key(infer_from_inverse_of: true)
|
678
762
|
if belongs_to?
|
679
763
|
"#{name}_id"
|
680
764
|
elsif options[:as]
|
681
765
|
"#{options[:as]}_id"
|
766
|
+
elsif options[:inverse_of] && infer_from_inverse_of
|
767
|
+
inverse_of.foreign_key(infer_from_inverse_of: false)
|
682
768
|
else
|
683
769
|
active_record.model_name.to_s.foreign_key
|
684
770
|
end
|
685
771
|
end
|
686
772
|
|
773
|
+
def derive_fk_query_constraints(foreign_key)
|
774
|
+
primary_query_constraints = active_record.query_constraints_list
|
775
|
+
owner_pk = active_record.primary_key
|
776
|
+
|
777
|
+
if primary_query_constraints.size != 2
|
778
|
+
raise ArgumentError, <<~MSG.squish
|
779
|
+
The query constraints list on the `#{active_record}` model has more than 2
|
780
|
+
attributes. Active Record is unable to derive the query constraints
|
781
|
+
for the association. You need to explicitly define the query constraints
|
782
|
+
for this association.
|
783
|
+
MSG
|
784
|
+
end
|
785
|
+
|
786
|
+
if !primary_query_constraints.include?(owner_pk)
|
787
|
+
raise ArgumentError, <<~MSG.squish
|
788
|
+
The query constraints on the `#{active_record}` model does not include the primary
|
789
|
+
key so Active Record is unable to derive the foreign key constraints for
|
790
|
+
the association. You need to explicitly define the query constraints for this
|
791
|
+
association.
|
792
|
+
MSG
|
793
|
+
end
|
794
|
+
|
795
|
+
first_key, last_key = primary_query_constraints
|
796
|
+
|
797
|
+
if first_key == owner_pk
|
798
|
+
[foreign_key, last_key.to_s]
|
799
|
+
elsif last_key == owner_pk
|
800
|
+
[first_key.to_s, foreign_key]
|
801
|
+
else
|
802
|
+
raise ArgumentError, <<~MSG.squish
|
803
|
+
Active Record couldn't correctly interpret the query constraints
|
804
|
+
for the `#{active_record}` model. The query constraints on `#{active_record}` are
|
805
|
+
`#{primary_query_constraints}` and the foreign key is `#{foreign_key}`.
|
806
|
+
You need to explicitly set the query constraints for this association.
|
807
|
+
MSG
|
808
|
+
end
|
809
|
+
end
|
810
|
+
|
687
811
|
def derive_join_table
|
688
812
|
ModelSchema.derive_join_table_name active_record.table_name, klass.table_name
|
689
813
|
end
|
@@ -734,6 +858,12 @@ module ActiveRecord
|
|
734
858
|
def association_primary_key(klass = nil)
|
735
859
|
if primary_key = options[:primary_key]
|
736
860
|
@association_primary_key ||= -primary_key.to_s
|
861
|
+
elsif !polymorphic? && ((klass || self.klass).has_query_constraints? || options[:query_constraints])
|
862
|
+
(klass || self.klass).composite_query_constraints_list
|
863
|
+
elsif (klass || self.klass).composite_primary_key?
|
864
|
+
# If klass has composite primary key of shape [:<tenant_key>, :id], infer primary_key as :id
|
865
|
+
primary_key = (klass || self.klass).primary_key
|
866
|
+
primary_key.include?("id") ? "id" : primary_key
|
737
867
|
else
|
738
868
|
primary_key(klass || self.klass)
|
739
869
|
end
|
@@ -772,6 +902,7 @@ module ActiveRecord
|
|
772
902
|
:active_record_primary_key, :join_foreign_key, to: :source_reflection
|
773
903
|
|
774
904
|
def initialize(delegate_reflection)
|
905
|
+
super()
|
775
906
|
@delegate_reflection = delegate_reflection
|
776
907
|
@klass = delegate_reflection.options[:anonymous_class]
|
777
908
|
@source_reflection_name = delegate_reflection.options[:source]
|
@@ -905,24 +1036,23 @@ module ActiveRecord
|
|
905
1036
|
end
|
906
1037
|
|
907
1038
|
def source_reflection_name # :nodoc:
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
1039
|
+
@source_reflection_name ||= begin
|
1040
|
+
names = [name.to_s.singularize, name].collect(&:to_sym).uniq
|
1041
|
+
names = names.find_all { |n|
|
1042
|
+
through_reflection.klass._reflect_on_association(n)
|
1043
|
+
}
|
1044
|
+
|
1045
|
+
if names.length > 1
|
1046
|
+
raise AmbiguousSourceReflectionForThroughAssociation.new(
|
1047
|
+
active_record.name,
|
1048
|
+
macro,
|
1049
|
+
name,
|
1050
|
+
options,
|
1051
|
+
source_reflection_names
|
1052
|
+
)
|
1053
|
+
end
|
1054
|
+
names.first
|
923
1055
|
end
|
924
|
-
|
925
|
-
@source_reflection_name = names.first
|
926
1056
|
end
|
927
1057
|
|
928
1058
|
def source_options
|
@@ -1026,12 +1156,13 @@ module ActiveRecord
|
|
1026
1156
|
:name, :scope_for, to: :@reflection
|
1027
1157
|
|
1028
1158
|
def initialize(reflection, previous_reflection)
|
1159
|
+
super()
|
1029
1160
|
@reflection = reflection
|
1030
1161
|
@previous_reflection = previous_reflection
|
1031
1162
|
end
|
1032
1163
|
|
1033
1164
|
def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
|
1034
|
-
scopes = @previous_reflection.join_scopes(table, predicate_builder, record) + super
|
1165
|
+
scopes = @previous_reflection.join_scopes(table, predicate_builder, klass, record) + super
|
1035
1166
|
scopes << build_scope(table, predicate_builder, klass).instance_exec(record, &source_type_scope)
|
1036
1167
|
end
|
1037
1168
|
|
@@ -1051,6 +1182,7 @@ module ActiveRecord
|
|
1051
1182
|
delegate :scope, :type, :constraints, :join_foreign_key, to: :@reflection
|
1052
1183
|
|
1053
1184
|
def initialize(reflection, association)
|
1185
|
+
super()
|
1054
1186
|
@reflection = reflection
|
1055
1187
|
@association = association
|
1056
1188
|
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
|