activerecord 6.1.4 → 7.0.0.rc1
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 +1049 -977
- data/MIT-LICENSE +1 -1
- 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 +34 -27
- data/lib/active_record/associations/collection_proxy.rb +8 -3
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- data/lib/active_record/associations/has_many_association.rb +1 -1
- 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/preloader/association.rb +187 -55
- 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 +90 -82
- 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 +66 -12
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
- data/lib/active_record/attribute_methods/write.rb +7 -10
- data/lib/active_record/attribute_methods.rb +13 -14
- data/lib/active_record/attributes.rb +24 -35
- data/lib/active_record/autosave_association.rb +6 -21
- data/lib/active_record/base.rb +19 -1
- data/lib/active_record/callbacks.rb +2 -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 +34 -9
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +69 -18
- 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 +97 -81
- data/lib/active_record/connection_adapters/column.rb +4 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +35 -23
- data/lib/active_record/connection_adapters/mysql/quoting.rb +35 -21
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +4 -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 +28 -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 +50 -50
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -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 +27 -16
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +205 -105
- data/lib/active_record/connection_adapters/schema_cache.rb +29 -4
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +15 -16
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -30
- data/lib/active_record/connection_adapters.rb +6 -5
- data/lib/active_record/connection_handling.rb +47 -53
- data/lib/active_record/core.rb +122 -132
- 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 +16 -32
- data/lib/active_record/delegated_type.rb +52 -11
- 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 +61 -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 +208 -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 +49 -42
- 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 +17 -20
- 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 +3 -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 +9 -3
- data/lib/active_record/log_subscriber.rb +14 -3
- data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
- data/lib/active_record/middleware/database_selector.rb +8 -3
- data/lib/active_record/middleware/shard_selector.rb +60 -0
- data/lib/active_record/migration/command_recorder.rb +4 -4
- data/lib/active_record/migration/compatibility.rb +83 -1
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +109 -79
- data/lib/active_record/model_schema.rb +45 -58
- 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 +219 -52
- data/lib/active_record/query_cache.rb +2 -2
- data/lib/active_record/query_logs.rb +138 -0
- data/lib/active_record/querying.rb +15 -5
- data/lib/active_record/railtie.rb +127 -17
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +66 -129
- data/lib/active_record/readonly_attributes.rb +11 -0
- data/lib/active_record/reflection.rb +67 -50
- data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
- data/lib/active_record/relation/batches.rb +3 -3
- data/lib/active_record/relation/calculations.rb +40 -36
- data/lib/active_record/relation/delegation.rb +6 -6
- 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 +235 -61
- 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 +171 -84
- data/lib/active_record/result.rb +17 -7
- data/lib/active_record/runtime_registry.rb +9 -13
- data/lib/active_record/sanitization.rb +11 -7
- data/lib/active_record/schema_dumper.rb +10 -3
- data/lib/active_record/schema_migration.rb +0 -4
- data/lib/active_record/scoping/default.rb +61 -12
- data/lib/active_record/scoping/named.rb +3 -11
- data/lib/active_record/scoping.rb +64 -34
- data/lib/active_record/serialization.rb +1 -1
- data/lib/active_record/signed_id.rb +1 -1
- data/lib/active_record/suppressor.rb +11 -15
- data/lib/active_record/tasks/database_tasks.rb +116 -58
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -12
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +4 -4
- data/lib/active_record/timestamp.rb +3 -4
- data/lib/active_record/transactions.rb +9 -14
- data/lib/active_record/translation.rb +2 -2
- 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 +1 -1
- data/lib/active_record/validations/uniqueness.rb +1 -1
- data/lib/active_record.rb +204 -28
- 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
- metadata +56 -13
@@ -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
|
@@ -306,6 +310,12 @@ module ActiveRecord
|
|
306
310
|
def primary_key(klass)
|
307
311
|
klass.primary_key || raise(UnknownPrimaryKey.new(klass))
|
308
312
|
end
|
313
|
+
|
314
|
+
def ensure_option_not_given_as_class!(option_name)
|
315
|
+
if options[option_name] && options[option_name].class == Class
|
316
|
+
raise ArgumentError, "A class was passed to `:#{option_name}` but we are expecting a string."
|
317
|
+
end
|
318
|
+
end
|
309
319
|
end
|
310
320
|
|
311
321
|
# Base class for AggregateReflection and AssociationReflection. Objects of
|
@@ -392,7 +402,7 @@ module ActiveRecord
|
|
392
402
|
|
393
403
|
# Holds all the metadata about an aggregation as it was specified in the
|
394
404
|
# Active Record class.
|
395
|
-
class AggregateReflection < MacroReflection
|
405
|
+
class AggregateReflection < MacroReflection # :nodoc:
|
396
406
|
def mapping
|
397
407
|
mapping = options[:mapping] || [name, name]
|
398
408
|
mapping.first.is_a?(Array) ? mapping : [mapping]
|
@@ -401,12 +411,29 @@ module ActiveRecord
|
|
401
411
|
|
402
412
|
# Holds all the metadata about an association as it was specified in the
|
403
413
|
# Active Record class.
|
404
|
-
class AssociationReflection < MacroReflection
|
414
|
+
class AssociationReflection < MacroReflection # :nodoc:
|
405
415
|
def compute_class(name)
|
406
416
|
if polymorphic?
|
407
417
|
raise ArgumentError, "Polymorphic associations do not support computing the class."
|
408
418
|
end
|
409
|
-
|
419
|
+
|
420
|
+
msg = <<-MSG.squish
|
421
|
+
Rails couldn't find a valid model for #{name} association.
|
422
|
+
Please provide the :class_name option on the association declaration.
|
423
|
+
If :class_name is already provided, make sure it's an ActiveRecord::Base subclass.
|
424
|
+
MSG
|
425
|
+
|
426
|
+
begin
|
427
|
+
klass = active_record.send(:compute_type, name)
|
428
|
+
|
429
|
+
unless klass < ActiveRecord::Base
|
430
|
+
raise ArgumentError, msg
|
431
|
+
end
|
432
|
+
|
433
|
+
klass
|
434
|
+
rescue NameError
|
435
|
+
raise NameError, msg
|
436
|
+
end
|
410
437
|
end
|
411
438
|
|
412
439
|
attr_reader :type, :foreign_type
|
@@ -416,11 +443,8 @@ module ActiveRecord
|
|
416
443
|
super
|
417
444
|
@type = -(options[:foreign_type]&.to_s || "#{options[:as]}_type") if options[:as]
|
418
445
|
@foreign_type = -(options[:foreign_type]&.to_s || "#{name}_type") if options[:polymorphic]
|
419
|
-
@constructable = calculate_constructable(macro, options)
|
420
446
|
|
421
|
-
|
422
|
-
raise ArgumentError, "A class was passed to `:class_name` but we are expecting a string."
|
423
|
-
end
|
447
|
+
ensure_option_not_given_as_class!(:class_name)
|
424
448
|
end
|
425
449
|
|
426
450
|
def association_scope_cache(klass, owner, &block)
|
@@ -431,10 +455,6 @@ module ActiveRecord
|
|
431
455
|
klass.cached_find_by_statement(key, &block)
|
432
456
|
end
|
433
457
|
|
434
|
-
def constructable? # :nodoc:
|
435
|
-
@constructable
|
436
|
-
end
|
437
|
-
|
438
458
|
def join_table
|
439
459
|
@join_table ||= -(options[:join_table]&.to_s || derive_join_table)
|
440
460
|
end
|
@@ -467,18 +487,17 @@ module ActiveRecord
|
|
467
487
|
check_validity_of_inverse!
|
468
488
|
end
|
469
489
|
|
470
|
-
def
|
490
|
+
def check_eager_loadable!
|
471
491
|
return unless scope
|
472
492
|
|
473
493
|
unless scope.arity == 0
|
474
494
|
raise ArgumentError, <<-MSG.squish
|
475
495
|
The association scope '#{name}' is instance dependent (the scope
|
476
|
-
block takes an argument).
|
477
|
-
not supported.
|
496
|
+
block takes an argument). Eager loading instance dependent scopes
|
497
|
+
is not supported.
|
478
498
|
MSG
|
479
499
|
end
|
480
500
|
end
|
481
|
-
alias :check_eager_loadable! :check_preloadable!
|
482
501
|
|
483
502
|
def join_id_for(owner) # :nodoc:
|
484
503
|
owner[join_foreign_key]
|
@@ -563,9 +582,6 @@ module ActiveRecord
|
|
563
582
|
options[:polymorphic]
|
564
583
|
end
|
565
584
|
|
566
|
-
VALID_AUTOMATIC_INVERSE_MACROS = [:has_many, :has_one, :belongs_to]
|
567
|
-
INVALID_AUTOMATIC_INVERSE_OPTIONS = [:through, :foreign_key]
|
568
|
-
|
569
585
|
def add_as_source(seed)
|
570
586
|
seed
|
571
587
|
end
|
@@ -583,10 +599,6 @@ module ActiveRecord
|
|
583
599
|
end
|
584
600
|
|
585
601
|
private
|
586
|
-
def calculate_constructable(macro, options)
|
587
|
-
true
|
588
|
-
end
|
589
|
-
|
590
602
|
# Attempts to find the inverse association name automatically.
|
591
603
|
# If it cannot find a suitable inverse association name, it returns
|
592
604
|
# +nil+.
|
@@ -623,9 +635,10 @@ module ActiveRecord
|
|
623
635
|
# with the current reflection's klass name.
|
624
636
|
def valid_inverse_reflection?(reflection)
|
625
637
|
reflection &&
|
638
|
+
reflection != self &&
|
626
639
|
foreign_key == reflection.foreign_key &&
|
627
640
|
klass <= reflection.active_record &&
|
628
|
-
can_find_inverse_of_automatically?(reflection)
|
641
|
+
can_find_inverse_of_automatically?(reflection, true)
|
629
642
|
end
|
630
643
|
|
631
644
|
# Checks to see if the reflection doesn't have any options that prevent
|
@@ -634,14 +647,25 @@ module ActiveRecord
|
|
634
647
|
# have <tt>has_many</tt>, <tt>has_one</tt>, <tt>belongs_to</tt> associations.
|
635
648
|
# Third, we must not have options such as <tt>:foreign_key</tt>
|
636
649
|
# 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)
|
650
|
+
def can_find_inverse_of_automatically?(reflection, inverse_reflection = false)
|
641
651
|
reflection.options[:inverse_of] != false &&
|
642
|
-
|
643
|
-
!
|
652
|
+
!reflection.options[:through] &&
|
653
|
+
!reflection.options[:foreign_key] &&
|
654
|
+
scope_allows_automatic_inverse_of?(reflection, inverse_reflection)
|
655
|
+
end
|
656
|
+
|
657
|
+
# Scopes on the potential inverse reflection prevent automatic
|
658
|
+
# <tt>inverse_of</tt>, since the scope could exclude the owner record
|
659
|
+
# we would inverse from. Scopes on the reflection itself allow for
|
660
|
+
# automatic <tt>inverse_of</tt> as long as
|
661
|
+
# <tt>config.active_record.automatic_scope_inversing<tt> is set to
|
662
|
+
# +true+ (the default for new applications).
|
663
|
+
def scope_allows_automatic_inverse_of?(reflection, inverse_reflection)
|
664
|
+
if inverse_reflection
|
644
665
|
!reflection.scope
|
666
|
+
else
|
667
|
+
!reflection.scope || reflection.klass.automatic_scope_inversing
|
668
|
+
end
|
645
669
|
end
|
646
670
|
|
647
671
|
def derive_class_name
|
@@ -656,7 +680,7 @@ module ActiveRecord
|
|
656
680
|
elsif options[:as]
|
657
681
|
"#{options[:as]}_id"
|
658
682
|
else
|
659
|
-
active_record.
|
683
|
+
active_record.model_name.to_s.foreign_key
|
660
684
|
end
|
661
685
|
end
|
662
686
|
|
@@ -691,11 +715,6 @@ module ActiveRecord
|
|
691
715
|
Associations::HasOneAssociation
|
692
716
|
end
|
693
717
|
end
|
694
|
-
|
695
|
-
private
|
696
|
-
def calculate_constructable(macro, options)
|
697
|
-
!options[:through]
|
698
|
-
end
|
699
718
|
end
|
700
719
|
|
701
720
|
class BelongsToReflection < AssociationReflection # :nodoc:
|
@@ -733,13 +752,9 @@ module ActiveRecord
|
|
733
752
|
end
|
734
753
|
|
735
754
|
private
|
736
|
-
def can_find_inverse_of_automatically?(
|
755
|
+
def can_find_inverse_of_automatically?(*)
|
737
756
|
!polymorphic? && super
|
738
757
|
end
|
739
|
-
|
740
|
-
def calculate_constructable(macro, options)
|
741
|
-
!polymorphic?
|
742
|
-
end
|
743
758
|
end
|
744
759
|
|
745
760
|
class HasAndBelongsToManyReflection < AssociationReflection # :nodoc:
|
@@ -752,7 +767,7 @@ module ActiveRecord
|
|
752
767
|
|
753
768
|
# Holds all the metadata about a :through association as it was specified
|
754
769
|
# in the Active Record class.
|
755
|
-
class ThroughReflection < AbstractReflection
|
770
|
+
class ThroughReflection < AbstractReflection # :nodoc:
|
756
771
|
delegate :foreign_key, :foreign_type, :association_foreign_key, :join_id_for, :type,
|
757
772
|
:active_record_primary_key, :join_foreign_key, to: :source_reflection
|
758
773
|
|
@@ -760,6 +775,8 @@ module ActiveRecord
|
|
760
775
|
@delegate_reflection = delegate_reflection
|
761
776
|
@klass = delegate_reflection.options[:anonymous_class]
|
762
777
|
@source_reflection_name = delegate_reflection.options[:source]
|
778
|
+
|
779
|
+
ensure_option_not_given_as_class!(:source_type)
|
763
780
|
end
|
764
781
|
|
765
782
|
def through_reflection?
|
@@ -840,8 +857,8 @@ module ActiveRecord
|
|
840
857
|
source_reflection.scopes + super
|
841
858
|
end
|
842
859
|
|
843
|
-
def join_scopes(table, predicate_builder, klass = self.klass) # :nodoc:
|
844
|
-
source_reflection.join_scopes(table, predicate_builder, klass) + super
|
860
|
+
def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
|
861
|
+
source_reflection.join_scopes(table, predicate_builder, klass, record) + super
|
845
862
|
end
|
846
863
|
|
847
864
|
def has_scope?
|
@@ -1013,9 +1030,9 @@ module ActiveRecord
|
|
1013
1030
|
@previous_reflection = previous_reflection
|
1014
1031
|
end
|
1015
1032
|
|
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(
|
1033
|
+
def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
|
1034
|
+
scopes = @previous_reflection.join_scopes(table, predicate_builder, record) + super
|
1035
|
+
scopes << build_scope(table, predicate_builder, klass).instance_exec(record, &source_type_scope)
|
1019
1036
|
end
|
1020
1037
|
|
1021
1038
|
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
|
@@ -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
|
@@ -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
|
|
@@ -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 6.2.
|
325
|
-
To migrate to Rails 6.2'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
|
@@ -361,7 +362,7 @@ module ActiveRecord
|
|
361
362
|
relation.group_values = group_fields
|
362
363
|
relation.select_values = select_values
|
363
364
|
|
364
|
-
calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel,
|
365
|
+
calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, "#{@klass.name} #{operation.capitalize}") }
|
365
366
|
|
366
367
|
if association
|
367
368
|
key_ids = calculated_data.collect { |row| row[group_aliases.first] }
|
@@ -381,20 +382,18 @@ module ActiveRecord
|
|
381
382
|
end
|
382
383
|
end
|
383
384
|
|
384
|
-
|
385
|
+
if operation != "count"
|
386
|
+
type = column.try(:type_caster) ||
|
387
|
+
lookup_cast_type_from_join_dependencies(column_name.to_s) || Type.default_value
|
388
|
+
type = type.subtype if Enum::EnumType === type
|
389
|
+
end
|
390
|
+
|
385
391
|
hash_rows.each_with_object({}) do |row, result|
|
386
392
|
key = group_aliases.map { |aliaz| row[aliaz] }
|
387
393
|
key = key.first if key.size == 1
|
388
394
|
key = key_records[key] if associated
|
389
395
|
|
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
|
396
|
+
result[key] = type_cast_calculated_value(row[column_alias], operation, type)
|
398
397
|
end
|
399
398
|
end
|
400
399
|
|
@@ -445,16 +444,21 @@ module ActiveRecord
|
|
445
444
|
result.cast_values(cast_types)
|
446
445
|
end
|
447
446
|
|
448
|
-
def type_cast_calculated_value(value, operation)
|
447
|
+
def type_cast_calculated_value(value, operation, type)
|
449
448
|
case operation
|
450
449
|
when "count"
|
451
450
|
value.to_i
|
452
451
|
when "sum"
|
453
|
-
|
452
|
+
type.deserialize(value || 0)
|
454
453
|
when "average"
|
455
|
-
|
454
|
+
case type.type
|
455
|
+
when :integer, :decimal
|
456
|
+
value&.to_d
|
457
|
+
else
|
458
|
+
type.deserialize(value)
|
459
|
+
end
|
456
460
|
else # "minimum", "maximum"
|
457
|
-
|
461
|
+
type.deserialize(value)
|
458
462
|
end
|
459
463
|
end
|
460
464
|
|
@@ -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
|
@@ -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:
|
@@ -104,6 +104,32 @@ module ActiveRecord
|
|
104
104
|
take || raise_record_not_found_exception!
|
105
105
|
end
|
106
106
|
|
107
|
+
# Finds the sole matching record. Raises ActiveRecord::RecordNotFound if no
|
108
|
+
# record is found. Raises ActiveRecord::SoleRecordExceeded if more than one
|
109
|
+
# record is found.
|
110
|
+
#
|
111
|
+
# Product.where(["price = %?", price]).sole
|
112
|
+
def sole
|
113
|
+
found, undesired = first(2)
|
114
|
+
|
115
|
+
if found.nil?
|
116
|
+
raise_record_not_found_exception!
|
117
|
+
elsif undesired.present?
|
118
|
+
raise ActiveRecord::SoleRecordExceeded.new(self)
|
119
|
+
else
|
120
|
+
found
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Finds the sole matching record. Raises ActiveRecord::RecordNotFound if no
|
125
|
+
# record is found. Raises ActiveRecord::SoleRecordExceeded if more than one
|
126
|
+
# record is found.
|
127
|
+
#
|
128
|
+
# Product.find_sole_by(["price = %?", price])
|
129
|
+
def find_sole_by(arg, *args)
|
130
|
+
where(arg, *args).sole
|
131
|
+
end
|
132
|
+
|
107
133
|
# Find the first record (or first N records if a parameter is supplied).
|
108
134
|
# If no order is defined it will order by primary key.
|
109
135
|
#
|
@@ -114,8 +140,6 @@ module ActiveRecord
|
|
114
140
|
# Person.first(3) # returns the first three objects fetched by SELECT * FROM people ORDER BY people.id LIMIT 3
|
115
141
|
#
|
116
142
|
def first(limit = nil)
|
117
|
-
check_reorder_deprecation unless loaded?
|
118
|
-
|
119
143
|
if limit
|
120
144
|
find_nth_with_limit(0, limit)
|
121
145
|
else
|
@@ -364,17 +388,6 @@ module ActiveRecord
|
|
364
388
|
end
|
365
389
|
|
366
390
|
private
|
367
|
-
def check_reorder_deprecation
|
368
|
-
if !order_values.empty? && order_values.all?(&:blank?)
|
369
|
-
blank_value = order_values.first
|
370
|
-
ActiveSupport::Deprecation.warn(<<~MSG.squish)
|
371
|
-
`.reorder(#{blank_value.inspect})` with `.first` / `.first!` no longer
|
372
|
-
takes non-deterministic result in Rails 6.2.
|
373
|
-
To continue taking non-deterministic result, use `.take` / `.take!` instead.
|
374
|
-
MSG
|
375
|
-
end
|
376
|
-
end
|
377
|
-
|
378
391
|
def construct_relation_for_exists(conditions)
|
379
392
|
conditions = sanitize_forbidden_attributes(conditions)
|
380
393
|
|
@@ -400,7 +413,7 @@ module ActiveRecord
|
|
400
413
|
)
|
401
414
|
relation = except(:includes, :eager_load, :preload).joins!(join_dependency)
|
402
415
|
|
403
|
-
if eager_loading && !(
|
416
|
+
if eager_loading && has_limit_or_offset? && !(
|
404
417
|
using_limitable_reflections?(join_dependency.reflections) &&
|
405
418
|
using_limitable_reflections?(
|
406
419
|
construct_join_dependency(
|
@@ -409,12 +422,10 @@ module ActiveRecord
|
|
409
422
|
), nil
|
410
423
|
).reflections
|
411
424
|
)
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
|
425
|
+
)
|
426
|
+
relation = skip_query_cache_if_necessary do
|
427
|
+
klass.connection.distinct_relation_for_primary_key(relation)
|
416
428
|
end
|
417
|
-
relation.limit_value = relation.offset_value = nil
|
418
429
|
end
|
419
430
|
|
420
431
|
if block_given?
|
@@ -424,18 +435,6 @@ module ActiveRecord
|
|
424
435
|
end
|
425
436
|
end
|
426
437
|
|
427
|
-
def limited_ids_for(relation)
|
428
|
-
values = @klass.connection.columns_for_distinct(
|
429
|
-
connection.visitor.compile(table[primary_key]),
|
430
|
-
relation.order_values
|
431
|
-
)
|
432
|
-
|
433
|
-
relation = relation.except(:select).select(values).distinct!
|
434
|
-
|
435
|
-
id_rows = skip_query_cache_if_necessary { @klass.connection.select_rows(relation.arel, "SQL") }
|
436
|
-
id_rows.map(&:last)
|
437
|
-
end
|
438
|
-
|
439
438
|
def using_limitable_reflections?(reflections)
|
440
439
|
reflections.none?(&:collection?)
|
441
440
|
end
|
@@ -508,10 +507,7 @@ module ActiveRecord
|
|
508
507
|
result = except(:limit, :offset).where(primary_key => ids).records
|
509
508
|
|
510
509
|
if result.size == ids.size
|
511
|
-
|
512
|
-
|
513
|
-
records_by_id = result.index_by(&:id)
|
514
|
-
ids.map { |id| records_by_id.fetch(pk_type.cast(id)) }
|
510
|
+
result.in_order_of(:id, ids.map { |id| @klass.type_for_attribute(primary_key).cast(id) })
|
515
511
|
else
|
516
512
|
raise_record_not_found_exception!(ids, result.size, ids.size)
|
517
513
|
end
|