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.

Files changed (243) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1314 -975
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/aggregations.rb +1 -1
  5. data/lib/active_record/association_relation.rb +0 -10
  6. data/lib/active_record/associations/association.rb +33 -17
  7. data/lib/active_record/associations/association_scope.rb +1 -3
  8. data/lib/active_record/associations/belongs_to_association.rb +15 -4
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  10. data/lib/active_record/associations/builder/association.rb +8 -2
  11. data/lib/active_record/associations/builder/belongs_to.rb +19 -6
  12. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  13. data/lib/active_record/associations/builder/has_many.rb +3 -2
  14. data/lib/active_record/associations/builder/has_one.rb +2 -1
  15. data/lib/active_record/associations/builder/singular_association.rb +2 -2
  16. data/lib/active_record/associations/collection_association.rb +19 -21
  17. data/lib/active_record/associations/collection_proxy.rb +10 -5
  18. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  19. data/lib/active_record/associations/has_many_association.rb +8 -5
  20. data/lib/active_record/associations/has_many_through_association.rb +2 -1
  21. data/lib/active_record/associations/has_one_association.rb +10 -7
  22. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  23. data/lib/active_record/associations/join_dependency.rb +23 -15
  24. data/lib/active_record/associations/preloader/association.rb +186 -52
  25. data/lib/active_record/associations/preloader/batch.rb +48 -0
  26. data/lib/active_record/associations/preloader/branch.rb +147 -0
  27. data/lib/active_record/associations/preloader/through_association.rb +49 -13
  28. data/lib/active_record/associations/preloader.rb +39 -113
  29. data/lib/active_record/associations/singular_association.rb +8 -2
  30. data/lib/active_record/associations/through_association.rb +3 -3
  31. data/lib/active_record/associations.rb +124 -95
  32. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  33. data/lib/active_record/attribute_assignment.rb +1 -1
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  35. data/lib/active_record/attribute_methods/dirty.rb +49 -16
  36. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  37. data/lib/active_record/attribute_methods/query.rb +2 -2
  38. data/lib/active_record/attribute_methods/read.rb +7 -5
  39. data/lib/active_record/attribute_methods/serialization.rb +57 -19
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +8 -3
  41. data/lib/active_record/attribute_methods/write.rb +7 -10
  42. data/lib/active_record/attribute_methods.rb +14 -15
  43. data/lib/active_record/attributes.rb +24 -35
  44. data/lib/active_record/autosave_association.rb +8 -23
  45. data/lib/active_record/base.rb +19 -1
  46. data/lib/active_record/callbacks.rb +2 -2
  47. data/lib/active_record/coders/yaml_column.rb +10 -2
  48. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  50. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -561
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +42 -72
  56. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
  57. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +38 -13
  58. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +80 -24
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +149 -74
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +105 -81
  63. data/lib/active_record/connection_adapters/column.rb +4 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -24
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +37 -21
  66. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -1
  68. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  69. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  70. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -1
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
  72. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  75. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  76. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  79. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  80. data/lib/active_record/connection_adapters/postgresql/quoting.rb +51 -51
  81. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -0
  82. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  83. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
  84. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  85. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +37 -19
  86. data/lib/active_record/connection_adapters/postgresql_adapter.rb +208 -107
  87. data/lib/active_record/connection_adapters/schema_cache.rb +39 -38
  88. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
  89. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +28 -16
  90. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +17 -15
  91. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +96 -32
  92. data/lib/active_record/connection_adapters.rb +6 -5
  93. data/lib/active_record/connection_handling.rb +49 -55
  94. data/lib/active_record/core.rb +124 -134
  95. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  96. data/lib/active_record/database_configurations/database_config.rb +12 -9
  97. data/lib/active_record/database_configurations/hash_config.rb +63 -5
  98. data/lib/active_record/database_configurations/url_config.rb +2 -2
  99. data/lib/active_record/database_configurations.rb +15 -32
  100. data/lib/active_record/delegated_type.rb +53 -12
  101. data/lib/active_record/destroy_association_async_job.rb +1 -1
  102. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  103. data/lib/active_record/dynamic_matchers.rb +1 -1
  104. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  105. data/lib/active_record/encryption/cipher.rb +53 -0
  106. data/lib/active_record/encryption/config.rb +44 -0
  107. data/lib/active_record/encryption/configurable.rb +67 -0
  108. data/lib/active_record/encryption/context.rb +35 -0
  109. data/lib/active_record/encryption/contexts.rb +72 -0
  110. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  111. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  112. data/lib/active_record/encryption/encryptable_record.rb +206 -0
  113. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  114. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  115. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  116. data/lib/active_record/encryption/encryptor.rb +155 -0
  117. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  118. data/lib/active_record/encryption/errors.rb +15 -0
  119. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  120. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  121. data/lib/active_record/encryption/key.rb +28 -0
  122. data/lib/active_record/encryption/key_generator.rb +42 -0
  123. data/lib/active_record/encryption/key_provider.rb +46 -0
  124. data/lib/active_record/encryption/message.rb +33 -0
  125. data/lib/active_record/encryption/message_serializer.rb +90 -0
  126. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  127. data/lib/active_record/encryption/properties.rb +76 -0
  128. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  129. data/lib/active_record/encryption/scheme.rb +99 -0
  130. data/lib/active_record/encryption.rb +55 -0
  131. data/lib/active_record/enum.rb +50 -43
  132. data/lib/active_record/errors.rb +67 -4
  133. data/lib/active_record/explain_registry.rb +11 -6
  134. data/lib/active_record/fixture_set/file.rb +15 -1
  135. data/lib/active_record/fixture_set/table_row.rb +41 -6
  136. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  137. data/lib/active_record/fixtures.rb +20 -23
  138. data/lib/active_record/future_result.rb +139 -0
  139. data/lib/active_record/gem_version.rb +4 -4
  140. data/lib/active_record/inheritance.rb +55 -17
  141. data/lib/active_record/insert_all.rb +80 -14
  142. data/lib/active_record/integration.rb +4 -3
  143. data/lib/active_record/internal_metadata.rb +1 -5
  144. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  145. data/lib/active_record/locking/optimistic.rb +10 -9
  146. data/lib/active_record/locking/pessimistic.rb +10 -4
  147. data/lib/active_record/log_subscriber.rb +23 -7
  148. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  149. data/lib/active_record/middleware/database_selector.rb +18 -6
  150. data/lib/active_record/middleware/shard_selector.rb +60 -0
  151. data/lib/active_record/migration/command_recorder.rb +7 -7
  152. data/lib/active_record/migration/compatibility.rb +84 -2
  153. data/lib/active_record/migration/join_table.rb +1 -1
  154. data/lib/active_record/migration.rb +114 -83
  155. data/lib/active_record/model_schema.rb +58 -59
  156. data/lib/active_record/nested_attributes.rb +13 -12
  157. data/lib/active_record/no_touching.rb +3 -3
  158. data/lib/active_record/null_relation.rb +2 -6
  159. data/lib/active_record/persistence.rb +228 -60
  160. data/lib/active_record/query_cache.rb +2 -2
  161. data/lib/active_record/query_logs.rb +138 -0
  162. data/lib/active_record/querying.rb +16 -6
  163. data/lib/active_record/railtie.rb +136 -22
  164. data/lib/active_record/railties/controller_runtime.rb +1 -1
  165. data/lib/active_record/railties/databases.rake +78 -136
  166. data/lib/active_record/readonly_attributes.rb +11 -0
  167. data/lib/active_record/reflection.rb +73 -50
  168. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  169. data/lib/active_record/relation/batches.rb +6 -6
  170. data/lib/active_record/relation/calculations.rb +43 -38
  171. data/lib/active_record/relation/delegation.rb +7 -7
  172. data/lib/active_record/relation/finder_methods.rb +31 -35
  173. data/lib/active_record/relation/merger.rb +20 -13
  174. data/lib/active_record/relation/predicate_builder.rb +1 -6
  175. data/lib/active_record/relation/query_attribute.rb +5 -11
  176. data/lib/active_record/relation/query_methods.rb +276 -67
  177. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  178. data/lib/active_record/relation/spawn_methods.rb +2 -2
  179. data/lib/active_record/relation/where_clause.rb +10 -19
  180. data/lib/active_record/relation.rb +189 -88
  181. data/lib/active_record/result.rb +17 -7
  182. data/lib/active_record/runtime_registry.rb +9 -13
  183. data/lib/active_record/sanitization.rb +17 -12
  184. data/lib/active_record/schema.rb +38 -23
  185. data/lib/active_record/schema_dumper.rb +25 -19
  186. data/lib/active_record/schema_migration.rb +4 -4
  187. data/lib/active_record/scoping/default.rb +60 -13
  188. data/lib/active_record/scoping/named.rb +3 -11
  189. data/lib/active_record/scoping.rb +64 -34
  190. data/lib/active_record/serialization.rb +6 -1
  191. data/lib/active_record/signed_id.rb +3 -3
  192. data/lib/active_record/store.rb +7 -2
  193. data/lib/active_record/suppressor.rb +11 -15
  194. data/lib/active_record/tasks/database_tasks.rb +127 -60
  195. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  196. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -13
  197. data/lib/active_record/test_databases.rb +1 -1
  198. data/lib/active_record/test_fixtures.rb +16 -9
  199. data/lib/active_record/timestamp.rb +3 -4
  200. data/lib/active_record/transactions.rb +9 -14
  201. data/lib/active_record/translation.rb +3 -3
  202. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  203. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  204. data/lib/active_record/type/internal/timezone.rb +2 -2
  205. data/lib/active_record/type/serialized.rb +1 -1
  206. data/lib/active_record/type/type_map.rb +17 -20
  207. data/lib/active_record/type.rb +1 -2
  208. data/lib/active_record/validations/associated.rb +4 -4
  209. data/lib/active_record/validations/presence.rb +2 -2
  210. data/lib/active_record/validations/uniqueness.rb +4 -4
  211. data/lib/active_record/version.rb +1 -1
  212. data/lib/active_record.rb +217 -27
  213. data/lib/arel/attributes/attribute.rb +0 -8
  214. data/lib/arel/crud.rb +28 -22
  215. data/lib/arel/delete_manager.rb +18 -4
  216. data/lib/arel/filter_predications.rb +9 -0
  217. data/lib/arel/insert_manager.rb +2 -3
  218. data/lib/arel/nodes/casted.rb +1 -1
  219. data/lib/arel/nodes/delete_statement.rb +12 -13
  220. data/lib/arel/nodes/filter.rb +10 -0
  221. data/lib/arel/nodes/function.rb +1 -0
  222. data/lib/arel/nodes/insert_statement.rb +2 -2
  223. data/lib/arel/nodes/select_core.rb +2 -2
  224. data/lib/arel/nodes/select_statement.rb +2 -2
  225. data/lib/arel/nodes/update_statement.rb +8 -3
  226. data/lib/arel/nodes.rb +1 -0
  227. data/lib/arel/predications.rb +11 -3
  228. data/lib/arel/select_manager.rb +10 -4
  229. data/lib/arel/table.rb +0 -1
  230. data/lib/arel/tree_manager.rb +0 -12
  231. data/lib/arel/update_manager.rb +18 -4
  232. data/lib/arel/visitors/dot.rb +80 -90
  233. data/lib/arel/visitors/mysql.rb +8 -2
  234. data/lib/arel/visitors/postgresql.rb +0 -10
  235. data/lib/arel/visitors/to_sql.rb +58 -2
  236. data/lib/arel.rb +2 -1
  237. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  238. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  239. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  240. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  241. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  242. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  243. 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) #:nodoc:
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
- # This shit is nasty. We need to avoid the following situation:
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 #:nodoc:
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 #:nodoc:
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
- active_record.send(:compute_type, name)
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
- if options[:class_name] && options[:class_name].class == Class
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 check_preloadable!
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). Preloading instance dependent scopes is
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
- VALID_AUTOMATIC_INVERSE_MACROS.include?(reflection.macro) &&
643
- !INVALID_AUTOMATIC_INVERSE_OPTIONS.any? { |opt| reflection.options[opt] } &&
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.name.foreign_key
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 #:nodoc:
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(nil, &source_type_scope)
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:) #:nodoc:
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 { |record| yield record }
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 { |relation| yield relation } if block_given?
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 :asc or :desc). Defaults to :asc.
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 { |record| yield record }
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 :asc or :desc). Defaults to :asc.
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 :asc or :desc). Defaults to :asc.
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? ? klass.error_on_ignored_order : error_on_ignore)
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
- # ["published", "business"]=>0, ["published", "technology"]=>2}
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(column_name = nil)
86
+ def sum(identity_or_column = nil, &block)
87
87
  if block_given?
88
- unless column_name.nil?
89
- raise ArgumentError, "Column name argument is not supported when a block is passed."
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
- super()
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, column_name)
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 a bunch of records just to grab the attributes you want.
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.new([], [])
207
+ ActiveRecord::Result.empty
199
208
  else
200
- klass.connection.select_all(relation.arel, nil)
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) #:nodoc:
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
- type_cast_calculated_value(result.cast_values.first, operation) do |value|
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) #:nodoc:
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, nil) }
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
- type = nil
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) do |value|
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
- yield value || 0
453
+ type.deserialize(value || 0)
454
454
  when "average"
455
- value&.respond_to?(:to_d) ? value.to_d : value
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
- yield value
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}(#{definition})
67
- scoping { klass.#{method}(#{definition}) }
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) if respond_to?(:ruby2_keywords, true)
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) if respond_to?(:ruby2_keywords, true)
113
+ ruby2_keywords(:method_missing)
114
114
  end
115
115
 
116
116
  module ClassMethods # :nodoc: