activerecord 7.0.6 → 7.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1424 -1390
- data/MIT-LICENSE +1 -1
- data/README.rdoc +15 -16
- 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 +16 -10
- data/lib/active_record/associations/collection_proxy.rb +20 -10
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +20 -13
- data/lib/active_record/associations/has_many_through_association.rb +10 -6
- data/lib/active_record/associations/has_one_association.rb +10 -7
- data/lib/active_record/associations/join_dependency.rb +10 -8
- data/lib/active_record/associations/preloader/association.rb +27 -6
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +6 -8
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +295 -199
- 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 +150 -31
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +105 -21
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +60 -18
- data/lib/active_record/base.rb +7 -2
- data/lib/active_record/callbacks.rb +10 -24
- 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 -42
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
- 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 +41 -6
- 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 +137 -11
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +289 -124
- data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +496 -102
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +214 -113
- 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 +21 -14
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +18 -13
- 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 +14 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +71 -40
- 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 +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +15 -8
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
- 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 +349 -55
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +338 -176
- 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 +9 -5
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -9
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +210 -83
- 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 +71 -94
- data/lib/active_record/core.rb +136 -148
- data/lib/active_record/counter_cache.rb +46 -25
- 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 +86 -33
- data/lib/active_record/delegated_type.rb +8 -3
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +2 -0
- 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 +12 -19
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +5 -1
- data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
- data/lib/active_record/encryption/encryptable_record.rb +36 -18
- data/lib/active_record/encryption/encrypted_attribute_type.rb +17 -6
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -54
- 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_serializer.rb +2 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/scheme.rb +19 -22
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +113 -26
- data/lib/active_record/errors.rb +108 -15
- data/lib/active_record/explain.rb +23 -3
- 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 +119 -71
- 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 +55 -8
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/internal_metadata.rb +120 -30
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +29 -12
- 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 +5 -7
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +104 -5
- data/lib/active_record/migration/compatibility.rb +142 -58
- 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 +265 -112
- data/lib/active_record/model_schema.rb +60 -40
- data/lib/active_record/nested_attributes.rb +21 -3
- data/lib/active_record/normalization.rb +159 -0
- data/lib/active_record/persistence.rb +187 -35
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- data/lib/active_record/query_logs.rb +77 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +15 -2
- data/lib/active_record/railtie.rb +109 -47
- data/lib/active_record/railties/controller_runtime.rb +12 -8
- data/lib/active_record/railties/databases.rake +139 -145
- 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 +162 -44
- 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 +160 -63
- data/lib/active_record/relation/delegation.rb +22 -8
- 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 +11 -2
- 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 +27 -16
- data/lib/active_record/relation/query_attribute.rb +25 -1
- data/lib/active_record/relation/query_methods.rb +378 -70
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +76 -35
- data/lib/active_record/result.rb +19 -5
- data/lib/active_record/runtime_registry.rb +10 -1
- data/lib/active_record/sanitization.rb +51 -11
- data/lib/active_record/schema.rb +2 -3
- data/lib/active_record/schema_dumper.rb +46 -7
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +15 -5
- 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/signed_id.rb +7 -5
- data/lib/active_record/store.rb +8 -8
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +11 -2
- data/lib/active_record/tasks/database_tasks.rb +127 -105
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
- data/lib/active_record/test_fixtures.rb +113 -96
- 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/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +4 -0
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/validations/absence.rb +1 -1
- 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 +47 -2
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +121 -16
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- 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/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
@@ -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
|
|
@@ -346,6 +374,7 @@ module ActiveRecord
|
|
346
374
|
attr_reader :plural_name # :nodoc:
|
347
375
|
|
348
376
|
def initialize(name, scope, options, active_record)
|
377
|
+
super()
|
349
378
|
@name = name
|
350
379
|
@scope = scope
|
351
380
|
@options = options
|
@@ -423,23 +452,23 @@ module ActiveRecord
|
|
423
452
|
raise ArgumentError, "Polymorphic associations do not support computing the class."
|
424
453
|
end
|
425
454
|
|
426
|
-
msg = <<-MSG.squish
|
427
|
-
Rails couldn't find a valid model for #{name} association.
|
428
|
-
Please provide the :class_name option on the association declaration.
|
429
|
-
If :class_name is already provided, make sure it's an ActiveRecord::Base subclass.
|
430
|
-
MSG
|
431
|
-
|
432
455
|
begin
|
433
456
|
klass = active_record.send(:compute_type, name)
|
434
|
-
|
435
|
-
|
436
|
-
|
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
|
437
464
|
end
|
465
|
+
end
|
438
466
|
|
439
|
-
|
440
|
-
|
441
|
-
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."
|
442
469
|
end
|
470
|
+
|
471
|
+
klass
|
443
472
|
end
|
444
473
|
|
445
474
|
attr_reader :type, :foreign_type
|
@@ -449,6 +478,10 @@ module ActiveRecord
|
|
449
478
|
super
|
450
479
|
@type = -(options[:foreign_type]&.to_s || "#{options[:as]}_type") if options[:as]
|
451
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
|
452
485
|
|
453
486
|
ensure_option_not_given_as_class!(:class_name)
|
454
487
|
end
|
@@ -465,8 +498,20 @@ module ActiveRecord
|
|
465
498
|
@join_table ||= -(options[:join_table]&.to_s || derive_join_table)
|
466
499
|
end
|
467
500
|
|
468
|
-
def foreign_key
|
469
|
-
@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
|
470
515
|
end
|
471
516
|
|
472
517
|
def association_foreign_key
|
@@ -478,7 +523,22 @@ module ActiveRecord
|
|
478
523
|
end
|
479
524
|
|
480
525
|
def active_record_primary_key
|
481
|
-
|
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
|
482
542
|
end
|
483
543
|
|
484
544
|
def join_primary_key(klass = nil)
|
@@ -495,6 +555,14 @@ module ActiveRecord
|
|
495
555
|
|
496
556
|
def check_validity!
|
497
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
|
498
566
|
end
|
499
567
|
|
500
568
|
def check_eager_loadable!
|
@@ -510,7 +578,7 @@ module ActiveRecord
|
|
510
578
|
end
|
511
579
|
|
512
580
|
def join_id_for(owner) # :nodoc:
|
513
|
-
owner
|
581
|
+
Array(join_foreign_key).map { |key| owner._read_attribute(key) }
|
514
582
|
end
|
515
583
|
|
516
584
|
def through_reflection
|
@@ -631,7 +699,9 @@ module ActiveRecord
|
|
631
699
|
|
632
700
|
begin
|
633
701
|
reflection = klass._reflect_on_association(inverse_name)
|
634
|
-
rescue NameError
|
702
|
+
rescue NameError => error
|
703
|
+
raise unless error.name.to_s == class_name
|
704
|
+
|
635
705
|
# Give up: we couldn't compute the klass type so we won't be able
|
636
706
|
# to find any associations either.
|
637
707
|
reflection = false
|
@@ -688,16 +758,56 @@ module ActiveRecord
|
|
688
758
|
class_name.camelize
|
689
759
|
end
|
690
760
|
|
691
|
-
def derive_foreign_key
|
761
|
+
def derive_foreign_key(infer_from_inverse_of: true)
|
692
762
|
if belongs_to?
|
693
763
|
"#{name}_id"
|
694
764
|
elsif options[:as]
|
695
765
|
"#{options[:as]}_id"
|
766
|
+
elsif options[:inverse_of] && infer_from_inverse_of
|
767
|
+
inverse_of.foreign_key(infer_from_inverse_of: false)
|
696
768
|
else
|
697
769
|
active_record.model_name.to_s.foreign_key
|
698
770
|
end
|
699
771
|
end
|
700
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
|
+
|
701
811
|
def derive_join_table
|
702
812
|
ModelSchema.derive_join_table_name active_record.table_name, klass.table_name
|
703
813
|
end
|
@@ -748,6 +858,12 @@ module ActiveRecord
|
|
748
858
|
def association_primary_key(klass = nil)
|
749
859
|
if primary_key = options[:primary_key]
|
750
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
|
751
867
|
else
|
752
868
|
primary_key(klass || self.klass)
|
753
869
|
end
|
@@ -786,6 +902,7 @@ module ActiveRecord
|
|
786
902
|
:active_record_primary_key, :join_foreign_key, to: :source_reflection
|
787
903
|
|
788
904
|
def initialize(delegate_reflection)
|
905
|
+
super()
|
789
906
|
@delegate_reflection = delegate_reflection
|
790
907
|
@klass = delegate_reflection.options[:anonymous_class]
|
791
908
|
@source_reflection_name = delegate_reflection.options[:source]
|
@@ -919,24 +1036,23 @@ module ActiveRecord
|
|
919
1036
|
end
|
920
1037
|
|
921
1038
|
def source_reflection_name # :nodoc:
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
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
|
937
1055
|
end
|
938
|
-
|
939
|
-
@source_reflection_name = names.first
|
940
1056
|
end
|
941
1057
|
|
942
1058
|
def source_options
|
@@ -1040,6 +1156,7 @@ module ActiveRecord
|
|
1040
1156
|
:name, :scope_for, to: :@reflection
|
1041
1157
|
|
1042
1158
|
def initialize(reflection, previous_reflection)
|
1159
|
+
super()
|
1043
1160
|
@reflection = reflection
|
1044
1161
|
@previous_reflection = previous_reflection
|
1045
1162
|
end
|
@@ -1065,6 +1182,7 @@ module ActiveRecord
|
|
1065
1182
|
delegate :scope, :type, :constraints, :join_foreign_key, to: :@reflection
|
1066
1183
|
|
1067
1184
|
def initialize(reflection, association)
|
1185
|
+
super()
|
1068
1186
|
@reflection = reflection
|
1069
1187
|
@association = association
|
1070
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
|