activerecord 6.1.6 → 7.0.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1314 -975
- data/README.rdoc +1 -1
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/association_relation.rb +0 -10
- data/lib/active_record/associations/association.rb +33 -17
- data/lib/active_record/associations/association_scope.rb +1 -3
- data/lib/active_record/associations/belongs_to_association.rb +15 -4
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +8 -2
- data/lib/active_record/associations/builder/belongs_to.rb +19 -6
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_many.rb +3 -2
- data/lib/active_record/associations/builder/has_one.rb +2 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -2
- data/lib/active_record/associations/collection_association.rb +19 -21
- data/lib/active_record/associations/collection_proxy.rb +10 -5
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/has_many_association.rb +8 -5
- data/lib/active_record/associations/has_many_through_association.rb +2 -1
- data/lib/active_record/associations/has_one_association.rb +10 -7
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +23 -15
- data/lib/active_record/associations/preloader/association.rb +186 -52
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +49 -13
- data/lib/active_record/associations/preloader.rb +39 -113
- data/lib/active_record/associations/singular_association.rb +8 -2
- data/lib/active_record/associations/through_association.rb +3 -3
- data/lib/active_record/associations.rb +124 -95
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +1 -1
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +49 -16
- data/lib/active_record/attribute_methods/primary_key.rb +2 -2
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +7 -5
- data/lib/active_record/attribute_methods/serialization.rb +57 -19
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +8 -3
- data/lib/active_record/attribute_methods/write.rb +7 -10
- data/lib/active_record/attribute_methods.rb +14 -15
- data/lib/active_record/attributes.rb +24 -35
- data/lib/active_record/autosave_association.rb +8 -23
- data/lib/active_record/base.rb +19 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/coders/yaml_column.rb +10 -2
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -561
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
- data/lib/active_record/connection_adapters/abstract/quoting.rb +42 -72
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +38 -13
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +80 -24
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
- data/lib/active_record/connection_adapters/abstract_adapter.rb +149 -74
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +105 -81
- data/lib/active_record/connection_adapters/column.rb +4 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -24
- data/lib/active_record/connection_adapters/mysql/quoting.rb +37 -21
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +51 -51
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +37 -19
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +208 -107
- data/lib/active_record/connection_adapters/schema_cache.rb +39 -38
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +28 -16
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +17 -15
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +96 -32
- data/lib/active_record/connection_adapters.rb +6 -5
- data/lib/active_record/connection_handling.rb +49 -55
- data/lib/active_record/core.rb +124 -134
- data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
- data/lib/active_record/database_configurations/database_config.rb +12 -9
- data/lib/active_record/database_configurations/hash_config.rb +63 -5
- data/lib/active_record/database_configurations/url_config.rb +2 -2
- data/lib/active_record/database_configurations.rb +15 -32
- data/lib/active_record/delegated_type.rb +53 -12
- data/lib/active_record/destroy_association_async_job.rb +1 -1
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +44 -0
- data/lib/active_record/encryption/configurable.rb +67 -0
- data/lib/active_record/encryption/context.rb +35 -0
- data/lib/active_record/encryption/contexts.rb +72 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +206 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +155 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +42 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_serializer.rb +90 -0
- data/lib/active_record/encryption/null_encryptor.rb +21 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
- data/lib/active_record/encryption/scheme.rb +99 -0
- data/lib/active_record/encryption.rb +55 -0
- data/lib/active_record/enum.rb +50 -43
- data/lib/active_record/errors.rb +67 -4
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/fixture_set/file.rb +15 -1
- data/lib/active_record/fixture_set/table_row.rb +41 -6
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +20 -23
- data/lib/active_record/future_result.rb +139 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +55 -17
- data/lib/active_record/insert_all.rb +80 -14
- data/lib/active_record/integration.rb +4 -3
- data/lib/active_record/internal_metadata.rb +1 -5
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +10 -9
- data/lib/active_record/locking/pessimistic.rb +10 -4
- data/lib/active_record/log_subscriber.rb +23 -7
- data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
- data/lib/active_record/middleware/database_selector.rb +18 -6
- data/lib/active_record/middleware/shard_selector.rb +60 -0
- data/lib/active_record/migration/command_recorder.rb +7 -7
- data/lib/active_record/migration/compatibility.rb +84 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +114 -83
- data/lib/active_record/model_schema.rb +58 -59
- data/lib/active_record/nested_attributes.rb +13 -12
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/null_relation.rb +2 -6
- data/lib/active_record/persistence.rb +228 -60
- data/lib/active_record/query_cache.rb +2 -2
- data/lib/active_record/query_logs.rb +138 -0
- data/lib/active_record/querying.rb +16 -6
- data/lib/active_record/railtie.rb +136 -22
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +78 -136
- data/lib/active_record/readonly_attributes.rb +11 -0
- data/lib/active_record/reflection.rb +73 -50
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +6 -6
- data/lib/active_record/relation/calculations.rb +43 -38
- data/lib/active_record/relation/delegation.rb +7 -7
- data/lib/active_record/relation/finder_methods.rb +31 -35
- data/lib/active_record/relation/merger.rb +20 -13
- data/lib/active_record/relation/predicate_builder.rb +1 -6
- data/lib/active_record/relation/query_attribute.rb +5 -11
- data/lib/active_record/relation/query_methods.rb +276 -67
- data/lib/active_record/relation/record_fetch_warning.rb +7 -9
- data/lib/active_record/relation/spawn_methods.rb +2 -2
- data/lib/active_record/relation/where_clause.rb +10 -19
- data/lib/active_record/relation.rb +189 -88
- data/lib/active_record/result.rb +17 -7
- data/lib/active_record/runtime_registry.rb +9 -13
- data/lib/active_record/sanitization.rb +17 -12
- data/lib/active_record/schema.rb +38 -23
- data/lib/active_record/schema_dumper.rb +25 -19
- data/lib/active_record/schema_migration.rb +4 -4
- data/lib/active_record/scoping/default.rb +60 -13
- data/lib/active_record/scoping/named.rb +3 -11
- data/lib/active_record/scoping.rb +64 -34
- data/lib/active_record/serialization.rb +6 -1
- data/lib/active_record/signed_id.rb +3 -3
- data/lib/active_record/store.rb +7 -2
- data/lib/active_record/suppressor.rb +11 -15
- data/lib/active_record/tasks/database_tasks.rb +127 -60
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -13
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +16 -9
- data/lib/active_record/timestamp.rb +3 -4
- data/lib/active_record/transactions.rb +9 -14
- data/lib/active_record/translation.rb +3 -3
- data/lib/active_record/type/adapter_specific_registry.rb +32 -7
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +2 -2
- data/lib/active_record/type/serialized.rb +1 -1
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/validations/associated.rb +4 -4
- data/lib/active_record/validations/presence.rb +2 -2
- data/lib/active_record/validations/uniqueness.rb +4 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +217 -27
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -4
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- data/lib/arel/nodes/casted.rb +1 -1
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +1 -0
- data/lib/arel/predications.rb +11 -3
- data/lib/arel/select_manager.rb +10 -4
- data/lib/arel/table.rb +0 -1
- data/lib/arel/tree_manager.rb +0 -12
- data/lib/arel/update_manager.rb +18 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +8 -2
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +58 -2
- data/lib/arel.rb +2 -1
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +55 -11
@@ -10,6 +10,7 @@ module ActiveRecord
|
|
10
10
|
included do
|
11
11
|
class_attribute :_reflections, instance_writer: false, default: {}
|
12
12
|
class_attribute :aggregate_reflections, instance_writer: false, default: {}
|
13
|
+
class_attribute :automatic_scope_inversing, instance_writer: false, default: false
|
13
14
|
end
|
14
15
|
|
15
16
|
class << self
|
@@ -115,7 +116,7 @@ module ActiveRecord
|
|
115
116
|
reflections[association.to_s]
|
116
117
|
end
|
117
118
|
|
118
|
-
def _reflect_on_association(association)
|
119
|
+
def _reflect_on_association(association) # :nodoc:
|
119
120
|
_reflections[association.to_s]
|
120
121
|
end
|
121
122
|
|
@@ -194,9 +195,9 @@ module ActiveRecord
|
|
194
195
|
klass_scope
|
195
196
|
end
|
196
197
|
|
197
|
-
def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
|
198
|
+
def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
|
198
199
|
if scope
|
199
|
-
[scope_for(build_scope(table, predicate_builder, klass))]
|
200
|
+
[scope_for(build_scope(table, predicate_builder, klass), record)]
|
200
201
|
else
|
201
202
|
[]
|
202
203
|
end
|
@@ -234,10 +235,13 @@ module ActiveRecord
|
|
234
235
|
if has_inverse? && inverse_of.nil?
|
235
236
|
raise InverseOfAssociationNotFoundError.new(self)
|
236
237
|
end
|
238
|
+
if has_inverse? && inverse_of == self
|
239
|
+
raise InverseOfAssociationRecursiveError.new(self)
|
240
|
+
end
|
237
241
|
end
|
238
242
|
end
|
239
243
|
|
240
|
-
#
|
244
|
+
# We need to avoid the following situation:
|
241
245
|
#
|
242
246
|
# * An associated record is deleted via record.destroy
|
243
247
|
# * Hence the callbacks run, and they find a belongs_to on the record with a
|
@@ -293,6 +297,12 @@ module ActiveRecord
|
|
293
297
|
options[:strict_loading]
|
294
298
|
end
|
295
299
|
|
300
|
+
def strict_loading_violation_message(owner)
|
301
|
+
message = +"`#{owner}` is marked for strict_loading."
|
302
|
+
message << " The #{polymorphic? ? "polymorphic association" : "#{klass} association"}"
|
303
|
+
message << " named `:#{name}` cannot be lazily loaded."
|
304
|
+
end
|
305
|
+
|
296
306
|
protected
|
297
307
|
def actual_source_reflection # FIXME: this is a horrible name
|
298
308
|
self
|
@@ -306,6 +316,12 @@ module ActiveRecord
|
|
306
316
|
def primary_key(klass)
|
307
317
|
klass.primary_key || raise(UnknownPrimaryKey.new(klass))
|
308
318
|
end
|
319
|
+
|
320
|
+
def ensure_option_not_given_as_class!(option_name)
|
321
|
+
if options[option_name] && options[option_name].class == Class
|
322
|
+
raise ArgumentError, "A class was passed to `:#{option_name}` but we are expecting a string."
|
323
|
+
end
|
324
|
+
end
|
309
325
|
end
|
310
326
|
|
311
327
|
# Base class for AggregateReflection and AssociationReflection. Objects of
|
@@ -392,7 +408,7 @@ module ActiveRecord
|
|
392
408
|
|
393
409
|
# Holds all the metadata about an aggregation as it was specified in the
|
394
410
|
# Active Record class.
|
395
|
-
class AggregateReflection < MacroReflection
|
411
|
+
class AggregateReflection < MacroReflection # :nodoc:
|
396
412
|
def mapping
|
397
413
|
mapping = options[:mapping] || [name, name]
|
398
414
|
mapping.first.is_a?(Array) ? mapping : [mapping]
|
@@ -401,12 +417,29 @@ module ActiveRecord
|
|
401
417
|
|
402
418
|
# Holds all the metadata about an association as it was specified in the
|
403
419
|
# Active Record class.
|
404
|
-
class AssociationReflection < MacroReflection
|
420
|
+
class AssociationReflection < MacroReflection # :nodoc:
|
405
421
|
def compute_class(name)
|
406
422
|
if polymorphic?
|
407
423
|
raise ArgumentError, "Polymorphic associations do not support computing the class."
|
408
424
|
end
|
409
|
-
|
425
|
+
|
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
|
+
begin
|
433
|
+
klass = active_record.send(:compute_type, name)
|
434
|
+
|
435
|
+
unless klass < ActiveRecord::Base
|
436
|
+
raise ArgumentError, msg
|
437
|
+
end
|
438
|
+
|
439
|
+
klass
|
440
|
+
rescue NameError
|
441
|
+
raise NameError, msg
|
442
|
+
end
|
410
443
|
end
|
411
444
|
|
412
445
|
attr_reader :type, :foreign_type
|
@@ -416,11 +449,8 @@ module ActiveRecord
|
|
416
449
|
super
|
417
450
|
@type = -(options[:foreign_type]&.to_s || "#{options[:as]}_type") if options[:as]
|
418
451
|
@foreign_type = -(options[:foreign_type]&.to_s || "#{name}_type") if options[:polymorphic]
|
419
|
-
@constructable = calculate_constructable(macro, options)
|
420
452
|
|
421
|
-
|
422
|
-
raise ArgumentError, "A class was passed to `:class_name` but we are expecting a string."
|
423
|
-
end
|
453
|
+
ensure_option_not_given_as_class!(:class_name)
|
424
454
|
end
|
425
455
|
|
426
456
|
def association_scope_cache(klass, owner, &block)
|
@@ -431,10 +461,6 @@ module ActiveRecord
|
|
431
461
|
klass.cached_find_by_statement(key, &block)
|
432
462
|
end
|
433
463
|
|
434
|
-
def constructable? # :nodoc:
|
435
|
-
@constructable
|
436
|
-
end
|
437
|
-
|
438
464
|
def join_table
|
439
465
|
@join_table ||= -(options[:join_table]&.to_s || derive_join_table)
|
440
466
|
end
|
@@ -467,18 +493,17 @@ module ActiveRecord
|
|
467
493
|
check_validity_of_inverse!
|
468
494
|
end
|
469
495
|
|
470
|
-
def
|
496
|
+
def check_eager_loadable!
|
471
497
|
return unless scope
|
472
498
|
|
473
499
|
unless scope.arity == 0
|
474
500
|
raise ArgumentError, <<-MSG.squish
|
475
501
|
The association scope '#{name}' is instance dependent (the scope
|
476
|
-
block takes an argument).
|
477
|
-
not supported.
|
502
|
+
block takes an argument). Eager loading instance dependent scopes
|
503
|
+
is not supported.
|
478
504
|
MSG
|
479
505
|
end
|
480
506
|
end
|
481
|
-
alias :check_eager_loadable! :check_preloadable!
|
482
507
|
|
483
508
|
def join_id_for(owner) # :nodoc:
|
484
509
|
owner[join_foreign_key]
|
@@ -563,9 +588,6 @@ module ActiveRecord
|
|
563
588
|
options[:polymorphic]
|
564
589
|
end
|
565
590
|
|
566
|
-
VALID_AUTOMATIC_INVERSE_MACROS = [:has_many, :has_one, :belongs_to]
|
567
|
-
INVALID_AUTOMATIC_INVERSE_OPTIONS = [:through, :foreign_key]
|
568
|
-
|
569
591
|
def add_as_source(seed)
|
570
592
|
seed
|
571
593
|
end
|
@@ -583,10 +605,6 @@ module ActiveRecord
|
|
583
605
|
end
|
584
606
|
|
585
607
|
private
|
586
|
-
def calculate_constructable(macro, options)
|
587
|
-
true
|
588
|
-
end
|
589
|
-
|
590
608
|
# Attempts to find the inverse association name automatically.
|
591
609
|
# If it cannot find a suitable inverse association name, it returns
|
592
610
|
# +nil+.
|
@@ -623,9 +641,10 @@ module ActiveRecord
|
|
623
641
|
# with the current reflection's klass name.
|
624
642
|
def valid_inverse_reflection?(reflection)
|
625
643
|
reflection &&
|
644
|
+
reflection != self &&
|
626
645
|
foreign_key == reflection.foreign_key &&
|
627
646
|
klass <= reflection.active_record &&
|
628
|
-
can_find_inverse_of_automatically?(reflection)
|
647
|
+
can_find_inverse_of_automatically?(reflection, true)
|
629
648
|
end
|
630
649
|
|
631
650
|
# Checks to see if the reflection doesn't have any options that prevent
|
@@ -634,14 +653,25 @@ module ActiveRecord
|
|
634
653
|
# have <tt>has_many</tt>, <tt>has_one</tt>, <tt>belongs_to</tt> associations.
|
635
654
|
# Third, we must not have options such as <tt>:foreign_key</tt>
|
636
655
|
# which prevent us from correctly guessing the inverse association.
|
637
|
-
|
638
|
-
# Anything with a scope can additionally ruin our attempt at finding an
|
639
|
-
# inverse, so we exclude reflections with scopes.
|
640
|
-
def can_find_inverse_of_automatically?(reflection)
|
656
|
+
def can_find_inverse_of_automatically?(reflection, inverse_reflection = false)
|
641
657
|
reflection.options[:inverse_of] != false &&
|
642
|
-
|
643
|
-
!
|
658
|
+
!reflection.options[:through] &&
|
659
|
+
!reflection.options[:foreign_key] &&
|
660
|
+
scope_allows_automatic_inverse_of?(reflection, inverse_reflection)
|
661
|
+
end
|
662
|
+
|
663
|
+
# Scopes on the potential inverse reflection prevent automatic
|
664
|
+
# <tt>inverse_of</tt>, since the scope could exclude the owner record
|
665
|
+
# we would inverse from. Scopes on the reflection itself allow for
|
666
|
+
# automatic <tt>inverse_of</tt> as long as
|
667
|
+
# <tt>config.active_record.automatic_scope_inversing<tt> is set to
|
668
|
+
# +true+ (the default for new applications).
|
669
|
+
def scope_allows_automatic_inverse_of?(reflection, inverse_reflection)
|
670
|
+
if inverse_reflection
|
644
671
|
!reflection.scope
|
672
|
+
else
|
673
|
+
!reflection.scope || reflection.klass.automatic_scope_inversing
|
674
|
+
end
|
645
675
|
end
|
646
676
|
|
647
677
|
def derive_class_name
|
@@ -656,7 +686,7 @@ module ActiveRecord
|
|
656
686
|
elsif options[:as]
|
657
687
|
"#{options[:as]}_id"
|
658
688
|
else
|
659
|
-
active_record.
|
689
|
+
active_record.model_name.to_s.foreign_key
|
660
690
|
end
|
661
691
|
end
|
662
692
|
|
@@ -691,11 +721,6 @@ module ActiveRecord
|
|
691
721
|
Associations::HasOneAssociation
|
692
722
|
end
|
693
723
|
end
|
694
|
-
|
695
|
-
private
|
696
|
-
def calculate_constructable(macro, options)
|
697
|
-
!options[:through]
|
698
|
-
end
|
699
724
|
end
|
700
725
|
|
701
726
|
class BelongsToReflection < AssociationReflection # :nodoc:
|
@@ -733,13 +758,9 @@ module ActiveRecord
|
|
733
758
|
end
|
734
759
|
|
735
760
|
private
|
736
|
-
def can_find_inverse_of_automatically?(
|
761
|
+
def can_find_inverse_of_automatically?(*)
|
737
762
|
!polymorphic? && super
|
738
763
|
end
|
739
|
-
|
740
|
-
def calculate_constructable(macro, options)
|
741
|
-
!polymorphic?
|
742
|
-
end
|
743
764
|
end
|
744
765
|
|
745
766
|
class HasAndBelongsToManyReflection < AssociationReflection # :nodoc:
|
@@ -752,7 +773,7 @@ module ActiveRecord
|
|
752
773
|
|
753
774
|
# Holds all the metadata about a :through association as it was specified
|
754
775
|
# in the Active Record class.
|
755
|
-
class ThroughReflection < AbstractReflection
|
776
|
+
class ThroughReflection < AbstractReflection # :nodoc:
|
756
777
|
delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for, :type,
|
757
778
|
:active_record_primary_key, :join_foreign_key, to: :source_reflection
|
758
779
|
|
@@ -760,6 +781,8 @@ module ActiveRecord
|
|
760
781
|
@delegate_reflection = delegate_reflection
|
761
782
|
@klass = delegate_reflection.options[:anonymous_class]
|
762
783
|
@source_reflection_name = delegate_reflection.options[:source]
|
784
|
+
|
785
|
+
ensure_option_not_given_as_class!(:source_type)
|
763
786
|
end
|
764
787
|
|
765
788
|
def through_reflection?
|
@@ -840,8 +863,8 @@ module ActiveRecord
|
|
840
863
|
source_reflection.scopes + super
|
841
864
|
end
|
842
865
|
|
843
|
-
def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
|
844
|
-
source_reflection.join_scopes(table, predicate_builder, klass) + super
|
866
|
+
def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
|
867
|
+
source_reflection.join_scopes(table, predicate_builder, klass, record) + super
|
845
868
|
end
|
846
869
|
|
847
870
|
def has_scope?
|
@@ -1013,9 +1036,9 @@ module ActiveRecord
|
|
1013
1036
|
@previous_reflection = previous_reflection
|
1014
1037
|
end
|
1015
1038
|
|
1016
|
-
def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
|
1017
|
-
scopes = @previous_reflection.join_scopes(table, predicate_builder) + super
|
1018
|
-
scopes << build_scope(table, predicate_builder, klass).instance_exec(
|
1039
|
+
def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
|
1040
|
+
scopes = @previous_reflection.join_scopes(table, predicate_builder, klass, record) + super
|
1041
|
+
scopes << build_scope(table, predicate_builder, klass).instance_exec(record, &source_type_scope)
|
1019
1042
|
end
|
1020
1043
|
|
1021
1044
|
def constraints
|
@@ -5,13 +5,27 @@ module ActiveRecord
|
|
5
5
|
class BatchEnumerator
|
6
6
|
include Enumerable
|
7
7
|
|
8
|
-
def initialize(of: 1000, start: nil, finish: nil, relation:)
|
8
|
+
def initialize(of: 1000, start: nil, finish: nil, relation:) # :nodoc:
|
9
9
|
@of = of
|
10
10
|
@relation = relation
|
11
11
|
@start = start
|
12
12
|
@finish = finish
|
13
13
|
end
|
14
14
|
|
15
|
+
# The primary key value from which the BatchEnumerator starts, inclusive of the value.
|
16
|
+
attr_reader :start
|
17
|
+
|
18
|
+
# The primary key value at which the BatchEnumerator ends, inclusive of the value.
|
19
|
+
attr_reader :finish
|
20
|
+
|
21
|
+
# The relation from which the BatchEnumerator yields batches.
|
22
|
+
attr_reader :relation
|
23
|
+
|
24
|
+
# The size of the batches yielded by the BatchEnumerator.
|
25
|
+
def batch_size
|
26
|
+
@of
|
27
|
+
end
|
28
|
+
|
15
29
|
# Looping through a collection of records from the database (using the
|
16
30
|
# +all+ method, for example) is very inefficient since it will try to
|
17
31
|
# instantiate all the objects at once.
|
@@ -33,11 +47,11 @@ module ActiveRecord
|
|
33
47
|
# Person.in_batches.each_record.with_index do |person, index|
|
34
48
|
# person.award_trophy(index + 1)
|
35
49
|
# end
|
36
|
-
def each_record
|
50
|
+
def each_record(&block)
|
37
51
|
return to_enum(:each_record) unless block_given?
|
38
52
|
|
39
53
|
@relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: true).each do |relation|
|
40
|
-
relation.records.each
|
54
|
+
relation.records.each(&block)
|
41
55
|
end
|
42
56
|
end
|
43
57
|
|
@@ -75,9 +89,9 @@ module ActiveRecord
|
|
75
89
|
# Person.in_batches.each do |relation|
|
76
90
|
# relation.update_all(awesome: true)
|
77
91
|
# end
|
78
|
-
def each
|
92
|
+
def each(&block)
|
79
93
|
enum = @relation.to_enum(:in_batches, of: @of, start: @start, finish: @finish, load: false)
|
80
|
-
return enum.each
|
94
|
+
return enum.each(&block) if block_given?
|
81
95
|
enum
|
82
96
|
end
|
83
97
|
end
|
@@ -37,7 +37,7 @@ module ActiveRecord
|
|
37
37
|
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
|
38
38
|
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
|
39
39
|
# an order is present in the relation.
|
40
|
-
# * <tt>:order</tt> - Specifies the primary key order (can be
|
40
|
+
# * <tt>:order</tt> - Specifies the primary key order (can be +:asc+ or +:desc+). Defaults to +:asc+.
|
41
41
|
#
|
42
42
|
# Limits are honored, and if present there is no requirement for the batch
|
43
43
|
# size: it can be less than, equal to, or greater than the limit.
|
@@ -65,10 +65,10 @@ module ActiveRecord
|
|
65
65
|
#
|
66
66
|
# NOTE: By its nature, batch processing is subject to race conditions if
|
67
67
|
# other processes are modifying the database.
|
68
|
-
def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, order: :asc)
|
68
|
+
def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, order: :asc, &block)
|
69
69
|
if block_given?
|
70
70
|
find_in_batches(start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, order: order) do |records|
|
71
|
-
records.each
|
71
|
+
records.each(&block)
|
72
72
|
end
|
73
73
|
else
|
74
74
|
enum_for(:find_each, start: start, finish: finish, batch_size: batch_size, error_on_ignore: error_on_ignore, order: order) do
|
@@ -102,7 +102,7 @@ module ActiveRecord
|
|
102
102
|
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
|
103
103
|
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
|
104
104
|
# an order is present in the relation.
|
105
|
-
# * <tt>:order</tt> - Specifies the primary key order (can be
|
105
|
+
# * <tt>:order</tt> - Specifies the primary key order (can be +:asc+ or +:desc+). Defaults to +:asc+.
|
106
106
|
#
|
107
107
|
# Limits are honored, and if present there is no requirement for the batch
|
108
108
|
# size: it can be less than, equal to, or greater than the limit.
|
@@ -167,7 +167,7 @@ module ActiveRecord
|
|
167
167
|
# * <tt>:finish</tt> - Specifies the primary key value to end at, inclusive of the value.
|
168
168
|
# * <tt>:error_on_ignore</tt> - Overrides the application config to specify if an error should be raised when
|
169
169
|
# an order is present in the relation.
|
170
|
-
# * <tt>:order</tt> - Specifies the primary key order (can be
|
170
|
+
# * <tt>:order</tt> - Specifies the primary key order (can be +:asc+ or +:desc+). Defaults to +:asc+.
|
171
171
|
#
|
172
172
|
# Limits are honored, and if present there is no requirement for the batch
|
173
173
|
# size, it can be less than, equal, or greater than the limit.
|
@@ -284,7 +284,7 @@ module ActiveRecord
|
|
284
284
|
end
|
285
285
|
|
286
286
|
def act_on_ignored_order(error_on_ignore)
|
287
|
-
raise_error = (error_on_ignore.nil? ?
|
287
|
+
raise_error = (error_on_ignore.nil? ? ActiveRecord.error_on_ignored_order : error_on_ignore)
|
288
288
|
|
289
289
|
if raise_error
|
290
290
|
raise ArgumentError.new(ORDER_IGNORE_MESSAGE)
|
@@ -31,7 +31,7 @@ module ActiveRecord
|
|
31
31
|
#
|
32
32
|
# Article.group(:status, :category).count
|
33
33
|
# # => {["draft", "business"]=>10, ["draft", "technology"]=>4,
|
34
|
-
#
|
34
|
+
# # ["published", "business"]=>0, ["published", "technology"]=>2}
|
35
35
|
#
|
36
36
|
# If #count is used with {Relation#select}[rdoc-ref:QueryMethods#select], it will count the selected columns:
|
37
37
|
#
|
@@ -83,15 +83,24 @@ module ActiveRecord
|
|
83
83
|
# #calculate for examples with options.
|
84
84
|
#
|
85
85
|
# Person.sum(:age) # => 4562
|
86
|
-
def sum(
|
86
|
+
def sum(identity_or_column = nil, &block)
|
87
87
|
if block_given?
|
88
|
-
|
89
|
-
|
88
|
+
values = map(&block)
|
89
|
+
if identity_or_column.nil? && (values.first.is_a?(Numeric) || values.first(1) == [])
|
90
|
+
identity_or_column = 0
|
90
91
|
end
|
91
92
|
|
92
|
-
|
93
|
+
if identity_or_column.nil?
|
94
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
95
|
+
Rails 7.0 has deprecated Enumerable.sum in favor of Ruby's native implementation available since 2.4.
|
96
|
+
Sum of non-numeric elements requires an initial argument.
|
97
|
+
MSG
|
98
|
+
values.inject(:+) || 0
|
99
|
+
else
|
100
|
+
values.sum(identity_or_column)
|
101
|
+
end
|
93
102
|
else
|
94
|
-
calculate(:sum,
|
103
|
+
calculate(:sum, identity_or_column)
|
95
104
|
end
|
96
105
|
end
|
97
106
|
|
@@ -146,7 +155,7 @@ module ActiveRecord
|
|
146
155
|
end
|
147
156
|
|
148
157
|
# Use #pluck as a shortcut to select one or more attributes without
|
149
|
-
# loading
|
158
|
+
# loading an entire record object per row.
|
150
159
|
#
|
151
160
|
# Person.pluck(:name)
|
152
161
|
#
|
@@ -195,9 +204,9 @@ module ActiveRecord
|
|
195
204
|
relation.select_values = columns
|
196
205
|
result = skip_query_cache_if_necessary do
|
197
206
|
if where_clause.contradiction?
|
198
|
-
ActiveRecord::Result.
|
207
|
+
ActiveRecord::Result.empty
|
199
208
|
else
|
200
|
-
klass.connection.select_all(relation.arel,
|
209
|
+
klass.connection.select_all(relation.arel, "#{klass.name} Pluck")
|
201
210
|
end
|
202
211
|
end
|
203
212
|
type_cast_pluck_values(result, columns)
|
@@ -286,7 +295,7 @@ module ActiveRecord
|
|
286
295
|
operation == "count" ? column.count(distinct) : column.public_send(operation)
|
287
296
|
end
|
288
297
|
|
289
|
-
def execute_simple_calculation(operation, column_name, distinct)
|
298
|
+
def execute_simple_calculation(operation, column_name, distinct) # :nodoc:
|
290
299
|
if operation == "count" && (column_name == :all && distinct || has_limit_or_offset?)
|
291
300
|
# Shortcut when limit is zero.
|
292
301
|
return 0 if limit_value == 0
|
@@ -305,29 +314,21 @@ module ActiveRecord
|
|
305
314
|
query_builder = relation.arel
|
306
315
|
end
|
307
316
|
|
308
|
-
result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder) }
|
317
|
+
result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder, "#{@klass.name} #{operation.capitalize}") }
|
309
318
|
|
310
|
-
|
319
|
+
if operation != "count"
|
311
320
|
type = column.try(:type_caster) ||
|
312
321
|
lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
|
313
322
|
type = type.subtype if Enum::EnumType === type
|
314
|
-
type.deserialize(value)
|
315
323
|
end
|
324
|
+
|
325
|
+
type_cast_calculated_value(result.cast_values.first, operation, type)
|
316
326
|
end
|
317
327
|
|
318
|
-
def execute_grouped_calculation(operation, column_name, distinct)
|
328
|
+
def execute_grouped_calculation(operation, column_name, distinct) # :nodoc:
|
319
329
|
group_fields = group_values
|
320
330
|
group_fields = group_fields.uniq if group_fields.size > 1
|
321
331
|
|
322
|
-
unless group_fields == group_values
|
323
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
324
|
-
`#{operation}` with group by duplicated fields does no longer affect to result in Rails 7.0.
|
325
|
-
To migrate to Rails 7.0's behavior, use `uniq!(:group)` to deduplicate group fields
|
326
|
-
(`#{klass.name&.tableize || klass.table_name}.uniq!(:group).#{operation}(#{column_name.inspect})`).
|
327
|
-
MSG
|
328
|
-
group_fields = group_values
|
329
|
-
end
|
330
|
-
|
331
332
|
if group_fields.size == 1 && group_fields.first.respond_to?(:to_sym)
|
332
333
|
association = klass._reflect_on_association(group_fields.first)
|
333
334
|
associated = association && association.belongs_to? # only count belongs_to associations
|
@@ -344,12 +345,13 @@ module ActiveRecord
|
|
344
345
|
column = aggregate_column(column_name)
|
345
346
|
column_alias = column_alias_for("#{operation} #{column_name.to_s.downcase}")
|
346
347
|
select_value = operation_over_aggregate_column(column, operation, distinct)
|
347
|
-
select_value.as(column_alias)
|
348
|
+
select_value.as(connection.quote_column_name(column_alias))
|
348
349
|
|
349
350
|
select_values = [select_value]
|
350
351
|
select_values += self.select_values unless having_clause.empty?
|
351
352
|
|
352
353
|
select_values.concat group_columns.map { |aliaz, field|
|
354
|
+
aliaz = connection.quote_column_name(aliaz)
|
353
355
|
if field.respond_to?(:as)
|
354
356
|
field.as(aliaz)
|
355
357
|
else
|
@@ -361,7 +363,7 @@ module ActiveRecord
|
|
361
363
|
relation.group_values = group_fields
|
362
364
|
relation.select_values = select_values
|
363
365
|
|
364
|
-
calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel,
|
366
|
+
calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, "#{@klass.name} #{operation.capitalize}") }
|
365
367
|
|
366
368
|
if association
|
367
369
|
key_ids = calculated_data.collect { |row| row[group_aliases.first] }
|
@@ -381,20 +383,18 @@ module ActiveRecord
|
|
381
383
|
end
|
382
384
|
end
|
383
385
|
|
384
|
-
|
386
|
+
if operation != "count"
|
387
|
+
type = column.try(:type_caster) ||
|
388
|
+
lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
|
389
|
+
type = type.subtype if Enum::EnumType === type
|
390
|
+
end
|
391
|
+
|
385
392
|
hash_rows.each_with_object({}) do |row, result|
|
386
393
|
key = group_aliases.map { |aliaz| row[aliaz] }
|
387
394
|
key = key.first if key.size == 1
|
388
395
|
key = key_records[key] if associated
|
389
396
|
|
390
|
-
result[key] = type_cast_calculated_value(row[column_alias], operation)
|
391
|
-
unless type
|
392
|
-
type = column.try(:type_caster) ||
|
393
|
-
lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
|
394
|
-
type = type.subtype if Enum::EnumType === type
|
395
|
-
end
|
396
|
-
type.deserialize(value)
|
397
|
-
end
|
397
|
+
result[key] = type_cast_calculated_value(row[column_alias], operation, type)
|
398
398
|
end
|
399
399
|
end
|
400
400
|
|
@@ -445,16 +445,21 @@ module ActiveRecord
|
|
445
445
|
result.cast_values(cast_types)
|
446
446
|
end
|
447
447
|
|
448
|
-
def type_cast_calculated_value(value, operation)
|
448
|
+
def type_cast_calculated_value(value, operation, type)
|
449
449
|
case operation
|
450
450
|
when "count"
|
451
451
|
value.to_i
|
452
452
|
when "sum"
|
453
|
-
|
453
|
+
type.deserialize(value || 0)
|
454
454
|
when "average"
|
455
|
-
|
455
|
+
case type.type
|
456
|
+
when :integer, :decimal
|
457
|
+
value&.to_d
|
458
|
+
else
|
459
|
+
type.deserialize(value)
|
460
|
+
end
|
456
461
|
else # "minimum", "maximum"
|
457
|
-
|
462
|
+
type.deserialize(value)
|
458
463
|
end
|
459
464
|
end
|
460
465
|
|
@@ -15,7 +15,8 @@ module ActiveRecord
|
|
15
15
|
[
|
16
16
|
ActiveRecord::Relation,
|
17
17
|
ActiveRecord::Associations::CollectionProxy,
|
18
|
-
ActiveRecord::AssociationRelation
|
18
|
+
ActiveRecord::AssociationRelation,
|
19
|
+
ActiveRecord::DisableJoinsAssociationRelation
|
19
20
|
].each do |klass|
|
20
21
|
delegate = Class.new(klass) {
|
21
22
|
include ClassSpecificRelation
|
@@ -61,17 +62,16 @@ module ActiveRecord
|
|
61
62
|
return if method_defined?(method)
|
62
63
|
|
63
64
|
if /\A[a-zA-Z_]\w*[!?]?\z/.match?(method) && !DELEGATION_RESERVED_METHOD_NAMES.include?(method.to_s)
|
64
|
-
definition = RUBY_VERSION >= "2.7" ? "..." : "*args, &block"
|
65
65
|
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
66
|
-
def #{method}(
|
67
|
-
scoping { klass.#{method}(
|
66
|
+
def #{method}(...)
|
67
|
+
scoping { klass.#{method}(...) }
|
68
68
|
end
|
69
69
|
RUBY
|
70
70
|
else
|
71
71
|
define_method(method) do |*args, &block|
|
72
72
|
scoping { klass.public_send(method, *args, &block) }
|
73
73
|
end
|
74
|
-
ruby2_keywords(method)
|
74
|
+
ruby2_keywords(method)
|
75
75
|
end
|
76
76
|
end
|
77
77
|
end
|
@@ -87,7 +87,7 @@ module ActiveRecord
|
|
87
87
|
|
88
88
|
delegate :to_xml, :encode_with, :length, :each, :join,
|
89
89
|
:[], :&, :|, :+, :-, :sample, :reverse, :rotate, :compact, :in_groups, :in_groups_of,
|
90
|
-
:to_sentence, :to_formatted_s, :as_json,
|
90
|
+
:to_sentence, :to_fs, :to_formatted_s, :as_json,
|
91
91
|
:shuffle, :split, :slice, :index, :rindex, to: :records
|
92
92
|
|
93
93
|
delegate :primary_key, :connection, to: :klass
|
@@ -110,7 +110,7 @@ module ActiveRecord
|
|
110
110
|
super
|
111
111
|
end
|
112
112
|
end
|
113
|
-
ruby2_keywords(:method_missing)
|
113
|
+
ruby2_keywords(:method_missing)
|
114
114
|
end
|
115
115
|
|
116
116
|
module ClassMethods # :nodoc:
|