activerecord 7.0.8.7 → 7.1.0.beta1
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 +1339 -1572
- 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 +17 -9
- data/lib/active_record/associations/collection_proxy.rb +16 -11
- 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 -3
- 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 +12 -9
- 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 +193 -97
- 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 +55 -9
- 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 +109 -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 -122
- data/lib/active_record/connection_adapters/abstract/transaction.rb +280 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +502 -91
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +200 -108
- 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 +22 -143
- data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -12
- 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 +17 -12
- 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 +1 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +76 -29
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +9 -6
- 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 +42 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +351 -54
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +336 -168
- 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 +42 -36
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +4 -3
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +1 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -7
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +162 -77
- 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 +128 -138
- 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 +2 -2
- 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 +89 -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 +4 -4
- 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 +118 -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 +100 -4
- data/lib/active_record/migration/compatibility.rb +131 -5
- 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 +213 -109
- data/lib/active_record/model_schema.rb +47 -27
- data/lib/active_record/nested_attributes.rb +28 -3
- data/lib/active_record/normalization.rb +158 -0
- data/lib/active_record/persistence.rb +183 -33
- 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 +107 -45
- data/lib/active_record/railties/controller_runtime.rb +10 -5
- 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 +169 -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 +152 -63
- data/lib/active_record/relation/delegation.rb +22 -8
- data/lib/active_record/relation/finder_methods.rb +85 -15
- 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/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +26 -14
- data/lib/active_record/relation/query_attribute.rb +2 -1
- data/lib/active_record/relation/query_methods.rb +351 -62
- 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 +41 -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 +10 -1
- 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 +14 -7
- data/lib/active_record/test_fixtures.rb +113 -96
- 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 +36 -10
- 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/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/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 +52 -17
- 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)
|
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
|
|
@@ -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(active_record, 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,62 @@ 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(klass, foreign_key)
|
774
|
+
primary_query_constraints = klass.query_constraints_list
|
775
|
+
owner_pk = klass.primary_key
|
776
|
+
|
777
|
+
if primary_query_constraints.size != 2
|
778
|
+
raise ArgumentError, <<~MSG.squish
|
779
|
+
The query constraints list on the `#{klass}` 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 `#{klass}` 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
|
+
# The primary key and foreign key are both already in the query constraints
|
796
|
+
# so we don't want to derive the key. In this case we want a single key.
|
797
|
+
if primary_query_constraints.include?(owner_pk) && primary_query_constraints.include?(foreign_key)
|
798
|
+
return foreign_key
|
799
|
+
end
|
800
|
+
|
801
|
+
first_key, last_key = primary_query_constraints
|
802
|
+
|
803
|
+
if first_key == owner_pk
|
804
|
+
[foreign_key, last_key.to_s]
|
805
|
+
elsif last_key == owner_pk
|
806
|
+
[first_key.to_s, foreign_key]
|
807
|
+
else
|
808
|
+
raise ArgumentError, <<~MSG.squish
|
809
|
+
Active Record couldn't correctly interpret the query constraints
|
810
|
+
for the `#{klass}` model. The query constraints on `#{klass}` are
|
811
|
+
`#{primary_query_constraints}` and the foreign key is `#{foreign_key}`.
|
812
|
+
You need to explicitly set the query constraints for this association.
|
813
|
+
MSG
|
814
|
+
end
|
815
|
+
end
|
816
|
+
|
701
817
|
def derive_join_table
|
702
818
|
ModelSchema.derive_join_table_name active_record.table_name, klass.table_name
|
703
819
|
end
|
@@ -746,8 +862,14 @@ module ActiveRecord
|
|
746
862
|
|
747
863
|
# klass option is necessary to support loading polymorphic associations
|
748
864
|
def association_primary_key(klass = nil)
|
749
|
-
if
|
865
|
+
if !polymorphic? && ((klass || self.klass).has_query_constraints? || options[:query_constraints])
|
866
|
+
(klass || self.klass).composite_query_constraints_list
|
867
|
+
elsif primary_key = options[:primary_key]
|
750
868
|
@association_primary_key ||= -primary_key.to_s
|
869
|
+
elsif (klass || self.klass).composite_primary_key?
|
870
|
+
# If klass has composite primary key of shape [:<tenant_key>, :id], infer primary_key as :id
|
871
|
+
primary_key = (klass || self.klass).primary_key
|
872
|
+
primary_key.include?("id") ? "id" : primary_key
|
751
873
|
else
|
752
874
|
primary_key(klass || self.klass)
|
753
875
|
end
|
@@ -786,6 +908,7 @@ module ActiveRecord
|
|
786
908
|
:active_record_primary_key, :join_foreign_key, to: :source_reflection
|
787
909
|
|
788
910
|
def initialize(delegate_reflection)
|
911
|
+
super()
|
789
912
|
@delegate_reflection = delegate_reflection
|
790
913
|
@klass = delegate_reflection.options[:anonymous_class]
|
791
914
|
@source_reflection_name = delegate_reflection.options[:source]
|
@@ -919,24 +1042,23 @@ module ActiveRecord
|
|
919
1042
|
end
|
920
1043
|
|
921
1044
|
def source_reflection_name # :nodoc:
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
1045
|
+
@source_reflection_name ||= begin
|
1046
|
+
names = [name.to_s.singularize, name].collect(&:to_sym).uniq
|
1047
|
+
names = names.find_all { |n|
|
1048
|
+
through_reflection.klass._reflect_on_association(n)
|
1049
|
+
}
|
1050
|
+
|
1051
|
+
if names.length > 1
|
1052
|
+
raise AmbiguousSourceReflectionForThroughAssociation.new(
|
1053
|
+
active_record.name,
|
1054
|
+
macro,
|
1055
|
+
name,
|
1056
|
+
options,
|
1057
|
+
source_reflection_names
|
1058
|
+
)
|
1059
|
+
end
|
1060
|
+
names.first
|
937
1061
|
end
|
938
|
-
|
939
|
-
@source_reflection_name = names.first
|
940
1062
|
end
|
941
1063
|
|
942
1064
|
def source_options
|
@@ -1040,6 +1162,7 @@ module ActiveRecord
|
|
1040
1162
|
:name, :scope_for, to: :@reflection
|
1041
1163
|
|
1042
1164
|
def initialize(reflection, previous_reflection)
|
1165
|
+
super()
|
1043
1166
|
@reflection = reflection
|
1044
1167
|
@previous_reflection = previous_reflection
|
1045
1168
|
end
|
@@ -1065,6 +1188,7 @@ module ActiveRecord
|
|
1065
1188
|
delegate :scope, :type, :constraints, :join_foreign_key, to: :@reflection
|
1066
1189
|
|
1067
1190
|
def initialize(reflection, association)
|
1191
|
+
super()
|
1068
1192
|
@reflection = reflection
|
1069
1193
|
@association = association
|
1070
1194
|
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
|