activerecord 7.2.4 → 8.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1013 -878
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/association_relation.rb +2 -1
  5. data/lib/active_record/associations/association.rb +69 -27
  6. data/lib/active_record/associations/association_scope.rb +1 -1
  7. data/lib/active_record/associations/belongs_to_association.rb +2 -2
  8. data/lib/active_record/associations/builder/association.rb +23 -11
  9. data/lib/active_record/associations/builder/belongs_to.rb +36 -8
  10. data/lib/active_record/associations/builder/collection_association.rb +7 -3
  11. data/lib/active_record/associations/builder/has_one.rb +1 -1
  12. data/lib/active_record/associations/builder/singular_association.rb +33 -5
  13. data/lib/active_record/associations/collection_association.rb +7 -3
  14. data/lib/active_record/associations/collection_proxy.rb +26 -4
  15. data/lib/active_record/associations/deprecation.rb +88 -0
  16. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  17. data/lib/active_record/associations/errors.rb +3 -0
  18. data/lib/active_record/associations/foreign_association.rb +3 -1
  19. data/lib/active_record/associations/has_many_through_association.rb +3 -2
  20. data/lib/active_record/associations/has_one_association.rb +1 -0
  21. data/lib/active_record/associations/join_dependency.rb +3 -2
  22. data/lib/active_record/associations/preloader/association.rb +3 -3
  23. data/lib/active_record/associations/preloader/batch.rb +7 -1
  24. data/lib/active_record/associations/preloader/branch.rb +1 -0
  25. data/lib/active_record/associations/singular_association.rb +8 -3
  26. data/lib/active_record/associations.rb +192 -24
  27. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  28. data/lib/active_record/attribute_methods/primary_key.rb +4 -8
  29. data/lib/active_record/attribute_methods/query.rb +34 -0
  30. data/lib/active_record/attribute_methods/serialization.rb +18 -5
  31. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -14
  32. data/lib/active_record/attributes.rb +3 -0
  33. data/lib/active_record/autosave_association.rb +69 -27
  34. data/lib/active_record/base.rb +1 -2
  35. data/lib/active_record/callbacks.rb +1 -2
  36. data/lib/active_record/coders/column_serializer.rb +1 -1
  37. data/lib/active_record/coders/json.rb +16 -6
  38. data/lib/active_record/coders/yaml_column.rb +2 -2
  39. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +35 -28
  40. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
  41. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +68 -13
  42. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +429 -93
  43. data/lib/active_record/connection_adapters/abstract/database_statements.rb +148 -74
  44. data/lib/active_record/connection_adapters/abstract/query_cache.rb +27 -5
  45. data/lib/active_record/connection_adapters/abstract/quoting.rb +16 -25
  46. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
  47. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +32 -35
  48. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
  49. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +124 -34
  50. data/lib/active_record/connection_adapters/abstract/transaction.rb +40 -8
  51. data/lib/active_record/connection_adapters/abstract_adapter.rb +129 -63
  52. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +132 -65
  53. data/lib/active_record/connection_adapters/column.rb +14 -4
  54. data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
  55. data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
  56. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
  57. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
  58. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +79 -66
  59. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +89 -94
  60. data/lib/active_record/connection_adapters/mysql2_adapter.rb +2 -10
  61. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  62. data/lib/active_record/connection_adapters/postgresql/column.rb +8 -2
  63. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +79 -61
  64. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
  65. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  66. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +46 -3
  67. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
  68. data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
  69. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  70. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
  71. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +14 -33
  72. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +71 -32
  73. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +156 -68
  74. data/lib/active_record/connection_adapters/postgresql_adapter.rb +78 -105
  75. data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
  76. data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
  77. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +90 -98
  78. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -8
  79. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
  80. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +27 -2
  81. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +15 -16
  82. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +104 -37
  83. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  84. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +41 -66
  85. data/lib/active_record/connection_adapters/trilogy_adapter.rb +15 -18
  86. data/lib/active_record/connection_adapters.rb +1 -56
  87. data/lib/active_record/connection_handling.rb +25 -2
  88. data/lib/active_record/core.rb +33 -17
  89. data/lib/active_record/counter_cache.rb +35 -8
  90. data/lib/active_record/database_configurations/database_config.rb +9 -1
  91. data/lib/active_record/database_configurations/hash_config.rb +74 -9
  92. data/lib/active_record/database_configurations/url_config.rb +15 -3
  93. data/lib/active_record/database_configurations.rb +7 -3
  94. data/lib/active_record/delegated_type.rb +1 -1
  95. data/lib/active_record/disable_joins_association_relation.rb +5 -1
  96. data/lib/active_record/dynamic_matchers.rb +54 -69
  97. data/lib/active_record/encryption/config.rb +3 -1
  98. data/lib/active_record/encryption/encryptable_record.rb +8 -8
  99. data/lib/active_record/encryption/encrypted_attribute_type.rb +11 -2
  100. data/lib/active_record/encryption/encrypted_fixtures.rb +12 -7
  101. data/lib/active_record/encryption/encryptor.rb +28 -8
  102. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  103. data/lib/active_record/encryption/scheme.rb +9 -2
  104. data/lib/active_record/enum.rb +33 -30
  105. data/lib/active_record/errors.rb +33 -9
  106. data/lib/active_record/explain.rb +1 -1
  107. data/lib/active_record/explain_registry.rb +51 -2
  108. data/lib/active_record/filter_attribute_handler.rb +76 -0
  109. data/lib/active_record/fixtures.rb +2 -4
  110. data/lib/active_record/future_result.rb +15 -9
  111. data/lib/active_record/gem_version.rb +2 -2
  112. data/lib/active_record/inheritance.rb +9 -4
  113. data/lib/active_record/insert_all.rb +14 -9
  114. data/lib/active_record/locking/optimistic.rb +8 -1
  115. data/lib/active_record/locking/pessimistic.rb +5 -0
  116. data/lib/active_record/log_subscriber.rb +31 -29
  117. data/lib/active_record/message_pack.rb +13 -0
  118. data/lib/active_record/middleware/shard_selector.rb +34 -17
  119. data/lib/active_record/migration/command_recorder.rb +56 -13
  120. data/lib/active_record/migration/compatibility.rb +37 -24
  121. data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
  122. data/lib/active_record/migration.rb +48 -42
  123. data/lib/active_record/model_schema.rb +40 -15
  124. data/lib/active_record/nested_attributes.rb +14 -12
  125. data/lib/active_record/persistence.rb +163 -133
  126. data/lib/active_record/query_cache.rb +22 -15
  127. data/lib/active_record/query_logs.rb +100 -52
  128. data/lib/active_record/query_logs_formatter.rb +17 -28
  129. data/lib/active_record/querying.rb +21 -9
  130. data/lib/active_record/railtie.rb +35 -30
  131. data/lib/active_record/railties/controller_runtime.rb +11 -6
  132. data/lib/active_record/railties/databases.rake +26 -38
  133. data/lib/active_record/railties/job_checkpoints.rb +15 -0
  134. data/lib/active_record/railties/job_runtime.rb +10 -11
  135. data/lib/active_record/reflection.rb +54 -22
  136. data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
  137. data/lib/active_record/relation/batches.rb +147 -73
  138. data/lib/active_record/relation/calculations.rb +60 -43
  139. data/lib/active_record/relation/delegation.rb +25 -15
  140. data/lib/active_record/relation/finder_methods.rb +73 -43
  141. data/lib/active_record/relation/merger.rb +12 -10
  142. data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
  143. data/lib/active_record/relation/predicate_builder/association_query_value.rb +9 -9
  144. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
  145. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  146. data/lib/active_record/relation/predicate_builder.rb +23 -8
  147. data/lib/active_record/relation/query_attribute.rb +3 -1
  148. data/lib/active_record/relation/query_methods.rb +200 -96
  149. data/lib/active_record/relation/spawn_methods.rb +7 -7
  150. data/lib/active_record/relation/where_clause.rb +2 -9
  151. data/lib/active_record/relation.rb +127 -87
  152. data/lib/active_record/result.rb +109 -24
  153. data/lib/active_record/runtime_registry.rb +42 -58
  154. data/lib/active_record/sanitization.rb +9 -6
  155. data/lib/active_record/schema.rb +1 -1
  156. data/lib/active_record/schema_dumper.rb +17 -10
  157. data/lib/active_record/schema_migration.rb +2 -1
  158. data/lib/active_record/scoping/named.rb +5 -2
  159. data/lib/active_record/scoping.rb +0 -1
  160. data/lib/active_record/signed_id.rb +44 -16
  161. data/lib/active_record/statement_cache.rb +24 -20
  162. data/lib/active_record/store.rb +53 -22
  163. data/lib/active_record/structured_event_subscriber.rb +85 -0
  164. data/lib/active_record/table_metadata.rb +6 -23
  165. data/lib/active_record/tasks/abstract_tasks.rb +83 -0
  166. data/lib/active_record/tasks/database_tasks.rb +85 -85
  167. data/lib/active_record/tasks/mysql_database_tasks.rb +4 -58
  168. data/lib/active_record/tasks/postgresql_database_tasks.rb +7 -40
  169. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -28
  170. data/lib/active_record/test_databases.rb +18 -4
  171. data/lib/active_record/test_fixtures.rb +52 -6
  172. data/lib/active_record/testing/query_assertions.rb +8 -2
  173. data/lib/active_record/timestamp.rb +4 -2
  174. data/lib/active_record/token_for.rb +1 -1
  175. data/lib/active_record/transaction.rb +2 -5
  176. data/lib/active_record/transactions.rb +56 -32
  177. data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
  178. data/lib/active_record/type/internal/timezone.rb +7 -0
  179. data/lib/active_record/type/json.rb +13 -2
  180. data/lib/active_record/type/serialized.rb +16 -4
  181. data/lib/active_record/type/type_map.rb +1 -1
  182. data/lib/active_record/type_caster/connection.rb +2 -1
  183. data/lib/active_record/validations/associated.rb +1 -1
  184. data/lib/active_record/validations/uniqueness.rb +8 -8
  185. data/lib/active_record.rb +85 -50
  186. data/lib/arel/alias_predication.rb +2 -0
  187. data/lib/arel/collectors/bind.rb +2 -2
  188. data/lib/arel/collectors/sql_string.rb +1 -1
  189. data/lib/arel/collectors/substitute_binds.rb +2 -2
  190. data/lib/arel/crud.rb +6 -11
  191. data/lib/arel/nodes/binary.rb +1 -1
  192. data/lib/arel/nodes/count.rb +2 -2
  193. data/lib/arel/nodes/function.rb +4 -10
  194. data/lib/arel/nodes/named_function.rb +2 -2
  195. data/lib/arel/nodes/node.rb +2 -2
  196. data/lib/arel/nodes/sql_literal.rb +1 -1
  197. data/lib/arel/nodes.rb +0 -2
  198. data/lib/arel/predications.rb +1 -3
  199. data/lib/arel/select_manager.rb +7 -2
  200. data/lib/arel/table.rb +3 -7
  201. data/lib/arel/visitors/dot.rb +0 -3
  202. data/lib/arel/visitors/postgresql.rb +55 -0
  203. data/lib/arel/visitors/sqlite.rb +55 -8
  204. data/lib/arel/visitors/to_sql.rb +4 -22
  205. data/lib/arel.rb +3 -1
  206. data/lib/rails/generators/active_record/application_record/USAGE +1 -1
  207. metadata +15 -12
  208. data/lib/active_record/explain_subscriber.rb +0 -34
  209. data/lib/active_record/normalization.rb +0 -163
  210. data/lib/active_record/relation/record_fetch_warning.rb +0 -52
@@ -60,7 +60,7 @@ module ActiveRecord
60
60
  :reverse_order, :distinct, :create_with, :skip_query_cache]
61
61
 
62
62
  CLAUSE_METHODS = [:where, :having, :from]
63
- INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :with, :with_recursive]
63
+ INVALID_METHODS_FOR_UPDATE_AND_DELETE_ALL = [:distinct, :with, :with_recursive]
64
64
 
65
65
  VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS + CLAUSE_METHODS
66
66
 
@@ -68,19 +68,26 @@ module ActiveRecord
68
68
  include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
69
69
  include SignedId::RelationMethods, TokenFor::RelationMethods
70
70
 
71
- attr_reader :table, :klass, :loaded, :predicate_builder
71
+ attr_reader :table, :model, :loaded, :predicate_builder
72
72
  attr_accessor :skip_preloading_value
73
- alias :model :klass
73
+ alias :klass :model
74
74
  alias :loaded? :loaded
75
75
  alias :locked? :lock_value
76
76
 
77
- def initialize(klass, table: klass.arel_table, predicate_builder: klass.predicate_builder, values: {})
78
- @klass = klass
77
+ def initialize(model, table: nil, predicate_builder: nil, values: {})
78
+ if table
79
+ predicate_builder ||= model.predicate_builder.with(TableMetadata.new(model, table))
80
+ else
81
+ table = model.arel_table
82
+ predicate_builder ||= model.predicate_builder
83
+ end
84
+
85
+ @model = model
79
86
  @table = table
80
87
  @values = values
81
88
  @loaded = false
82
89
  @predicate_builder = predicate_builder
83
- @delegate_to_klass = false
90
+ @delegate_to_model = false
84
91
  @future_result = nil
85
92
  @records = nil
86
93
  @async = false
@@ -93,7 +100,7 @@ module ActiveRecord
93
100
  end
94
101
 
95
102
  def bind_attribute(name, value) # :nodoc:
96
- if reflection = klass._reflect_on_association(name)
103
+ if reflection = model._reflect_on_association(name)
97
104
  name = reflection.foreign_key
98
105
  value = value.read_attribute(reflection.association_primary_key) unless value.nil?
99
106
  end
@@ -273,7 +280,7 @@ module ActiveRecord
273
280
  record
274
281
  rescue ActiveRecord::RecordNotUnique
275
282
  if connection.transaction_open?
276
- where(attributes).lock.find_by!(attributes)
283
+ rewhere(attributes).lock(connection.create_or_find_by_lock).take!
277
284
  else
278
285
  find_by!(attributes)
279
286
  end
@@ -293,7 +300,7 @@ module ActiveRecord
293
300
  record
294
301
  rescue ActiveRecord::RecordNotUnique
295
302
  if connection.transaction_open?
296
- where(attributes).lock.find_by!(attributes)
303
+ rewhere(attributes).lock(connection.create_or_find_by_lock).take!
297
304
  else
298
305
  find_by!(attributes)
299
306
  end
@@ -374,10 +381,11 @@ module ActiveRecord
374
381
 
375
382
  # Returns true if there are no records.
376
383
  #
377
- # When a pattern argument is given, this method checks whether elements in
378
- # the Enumerable match the pattern via the case-equality operator (<tt>===</tt>).
384
+ # When an argument is given, returns true if no records match the argument
385
+ # via the case-equality operator (<tt>===</tt>).
379
386
  #
380
- # posts.none?(Comment) # => true or false
387
+ # posts.none?(Post) # => true if posts is empty
388
+ # posts.none?(Comment) # => true
381
389
  def none?(*args)
382
390
  return true if @none
383
391
 
@@ -387,10 +395,12 @@ module ActiveRecord
387
395
 
388
396
  # Returns true if there are any records.
389
397
  #
390
- # When a pattern argument is given, this method checks whether elements in
391
- # the Enumerable match the pattern via the case-equality operator (<tt>===</tt>).
392
398
  #
393
- # posts.any?(Post) # => true or false
399
+ # When an argument is given, returns true if at least one records matches
400
+ # the argument via the case-equality operator (<tt>===</tt>).
401
+ #
402
+ # posts.any?(Post) # => true if at least one record
403
+ # posts.any?(Comment) # => false
394
404
  def any?(*args)
395
405
  return false if @none
396
406
 
@@ -400,10 +410,11 @@ module ActiveRecord
400
410
 
401
411
  # Returns true if there is exactly one record.
402
412
  #
403
- # When a pattern argument is given, this method checks whether elements in
404
- # the Enumerable match the pattern via the case-equality operator (<tt>===</tt>).
413
+ # When an argument is given, returns true if exactly one records matches the
414
+ # argument via the case-equality operator (<tt>===</tt>).
405
415
  #
406
- # posts.one?(Post) # => true or false
416
+ # posts.one?(Post) # => true if exactly one record
417
+ # posts.any?(Comment) # => false
407
418
  def one?(*args)
408
419
  return false if @none
409
420
 
@@ -440,14 +451,14 @@ module ActiveRecord
440
451
  # Product.where("name like ?", "%Game%").cache_key(:last_reviewed_at)
441
452
  def cache_key(timestamp_column = "updated_at")
442
453
  @cache_keys ||= {}
443
- @cache_keys[timestamp_column] ||= klass.collection_cache_key(self, timestamp_column)
454
+ @cache_keys[timestamp_column] ||= model.collection_cache_key(self, timestamp_column)
444
455
  end
445
456
 
446
457
  def compute_cache_key(timestamp_column = :updated_at) # :nodoc:
447
458
  query_signature = ActiveSupport::Digest.hexdigest(to_sql)
448
- key = "#{klass.model_name.cache_key}/query-#{query_signature}"
459
+ key = "#{model.model_name.cache_key}/query-#{query_signature}"
449
460
 
450
- if collection_cache_versioning
461
+ if model.collection_cache_versioning
451
462
  key
452
463
  else
453
464
  "#{key}-#{compute_cache_version(timestamp_column)}"
@@ -466,7 +477,7 @@ module ActiveRecord
466
477
  #
467
478
  # SELECT COUNT(*), MAX("products"."updated_at") FROM "products" WHERE (name like '%Cosmic Encounter%')
468
479
  def cache_version(timestamp_column = :updated_at)
469
- if collection_cache_versioning
480
+ if model.collection_cache_versioning
470
481
  @cache_versions ||= {}
471
482
  @cache_versions[timestamp_column] ||= compute_cache_version(timestamp_column)
472
483
  end
@@ -478,14 +489,14 @@ module ActiveRecord
478
489
  if loaded?
479
490
  size = records.size
480
491
  if size > 0
481
- timestamp = records.map { |record| record.read_attribute(timestamp_column) }.max
492
+ timestamp = records.filter_map { |record| record.read_attribute(timestamp_column) }.max
482
493
  end
483
494
  else
484
495
  collection = eager_loading? ? apply_join_dependency : self
485
496
 
486
497
  with_connection do |c|
487
498
  column = c.visitor.compile(table[timestamp_column])
488
- select_values = "COUNT(*) AS #{adapter_class.quote_column_name("size")}, MAX(%s) AS timestamp"
499
+ select_values = "COUNT(*) AS #{model.adapter_class.quote_column_name("size")}, MAX(%s) AS timestamp"
489
500
 
490
501
  if collection.has_limit_or_offset?
491
502
  query = collection.select("#{column} AS collection_cache_key_timestamp")
@@ -502,7 +513,7 @@ module ActiveRecord
502
513
  size, timestamp = c.select_rows(arel, nil).first
503
514
 
504
515
  if size
505
- column_type = klass.type_for_attribute(timestamp_column)
516
+ column_type = model.type_for_attribute(timestamp_column)
506
517
  timestamp = column_type.deserialize(timestamp)
507
518
  else
508
519
  size = 0
@@ -511,7 +522,7 @@ module ActiveRecord
511
522
  end
512
523
 
513
524
  if timestamp
514
- "#{size}-#{timestamp.utc.to_fs(cache_timestamp_format)}"
525
+ "#{size}-#{timestamp.utc.to_fs(model.cache_timestamp_format)}"
515
526
  else
516
527
  "#{size}"
517
528
  end
@@ -542,7 +553,7 @@ module ActiveRecord
542
553
  # Please check unscoped if you want to remove all previous scopes (including
543
554
  # the default_scope) during the execution of a block.
544
555
  def scoping(all_queries: nil, &block)
545
- registry = klass.scope_registry
556
+ registry = model.scope_registry
546
557
  if global_scope?(registry) && all_queries == false
547
558
  raise ArgumentError, "Scoping is set to apply to all queries and cannot be unset in a nested block."
548
559
  elsif already_in_scope?(registry)
@@ -553,11 +564,11 @@ module ActiveRecord
553
564
  end
554
565
 
555
566
  def _exec_scope(...) # :nodoc:
556
- @delegate_to_klass = true
557
- registry = klass.scope_registry
567
+ @delegate_to_model = true
568
+ registry = model.scope_registry
558
569
  _scoping(nil, registry) { instance_exec(...) || self }
559
570
  ensure
560
- @delegate_to_klass = false
571
+ @delegate_to_model = false
561
572
  end
562
573
 
563
574
  # Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
@@ -593,31 +604,41 @@ module ActiveRecord
593
604
 
594
605
  return 0 if @none
595
606
 
607
+ invalid_methods = INVALID_METHODS_FOR_UPDATE_AND_DELETE_ALL.select do |method|
608
+ value = @values[method]
609
+ method == :distinct ? value : value&.any?
610
+ end
611
+ if invalid_methods.any?
612
+ ActiveRecord.deprecator.warn <<~MESSAGE
613
+ `#{invalid_methods.join(', ')}` is not supported by `update_all` and was never included in the generated query.
614
+
615
+ Calling `#{invalid_methods.join(', ')}` with `update_all` will raise an error in Rails 8.2.
616
+ MESSAGE
617
+ end
618
+
596
619
  if updates.is_a?(Hash)
597
- if klass.locking_enabled? &&
598
- !updates.key?(klass.locking_column) &&
599
- !updates.key?(klass.locking_column.to_sym)
600
- attr = table[klass.locking_column]
620
+ if model.locking_enabled? &&
621
+ !updates.key?(model.locking_column) &&
622
+ !updates.key?(model.locking_column.to_sym)
623
+ attr = table[model.locking_column]
601
624
  updates[attr.name] = _increment_attribute(attr)
602
625
  end
603
626
  values = _substitute_values(updates)
604
627
  else
605
- values = Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
628
+ values = Arel.sql(model.sanitize_sql_for_assignment(updates, table.name))
606
629
  end
607
630
 
608
- klass.with_connection do |c|
609
- arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
631
+ model.with_connection do |c|
632
+ arel = eager_loading? ? apply_join_dependency.arel : arel()
610
633
  arel.source.left = table
611
634
 
612
- group_values_arel_columns = arel_columns(group_values.uniq)
613
- having_clause_ast = having_clause.ast unless having_clause.empty?
614
- key = if klass.composite_primary_key?
635
+ key = if model.composite_primary_key?
615
636
  primary_key.map { |pk| table[pk] }
616
637
  else
617
638
  table[primary_key]
618
639
  end
619
- stmt = arel.compile_update(values, key, having_clause_ast, group_values_arel_columns)
620
- c.update(stmt, "#{klass} Update All").tap { reset }
640
+ stmt = arel.compile_update(values, key)
641
+ c.update(stmt, "#{model} Update All").tap { reset }
621
642
  end
622
643
  end
623
644
 
@@ -625,7 +646,7 @@ module ActiveRecord
625
646
  if id == :all
626
647
  each { |record| record.update(attributes) }
627
648
  else
628
- klass.update(id, attributes)
649
+ model.update(id, attributes)
629
650
  end
630
651
  end
631
652
 
@@ -633,7 +654,7 @@ module ActiveRecord
633
654
  if id == :all
634
655
  each { |record| record.update!(attributes) }
635
656
  else
636
- klass.update!(id, attributes)
657
+ model.update!(id, attributes)
637
658
  end
638
659
  end
639
660
 
@@ -852,7 +873,9 @@ module ActiveRecord
852
873
  # Active Record's schema_cache.
853
874
  #
854
875
  # [:on_duplicate]
855
- # Configure the SQL update sentence that will be used in case of conflict.
876
+ # Configure the behavior that will be used in case of conflict. Use `:skip`
877
+ # to ignore any conflicts or provide a safe SQL fragment wrapped with
878
+ # `Arel.sql`.
856
879
  #
857
880
  # NOTE: If you use this option you must provide all the columns you want to update
858
881
  # by yourself.
@@ -939,7 +962,7 @@ module ActiveRecord
939
962
  names = touch if touch != true
940
963
  names = Array.wrap(names)
941
964
  options = names.extract_options!
942
- touch_updates = klass.touch_attributes_with_time(*names, **options)
965
+ touch_updates = model.touch_attributes_with_time(*names, **options)
943
966
  updates.merge!(touch_updates) unless touch_updates.empty?
944
967
  end
945
968
 
@@ -970,7 +993,7 @@ module ActiveRecord
970
993
  # Person.where(name: 'David').touch_all
971
994
  # # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670' WHERE \"people\".\"name\" = 'David'"
972
995
  def touch_all(*names, time: nil)
973
- update_all klass.touch_attributes_with_time(*names, time: time)
996
+ update_all model.touch_attributes_with_time(*names, time: time)
974
997
  end
975
998
 
976
999
  # Destroys the records by instantiating each
@@ -1003,7 +1026,7 @@ module ActiveRecord
1003
1026
  #
1004
1027
  # Post.where(person_id: 5).where(category: ['Something', 'Else']).delete_all
1005
1028
  #
1006
- # Both calls delete the affected posts all at once with a single DELETE statement.
1029
+ # This call deletes the affected posts all at once with a single DELETE statement.
1007
1030
  # If you need to destroy dependent associations or call your <tt>before_*</tt> or
1008
1031
  # +after_destroy+ callbacks, use the #destroy_all method instead.
1009
1032
  #
@@ -1014,7 +1037,7 @@ module ActiveRecord
1014
1037
  def delete_all
1015
1038
  return 0 if @none
1016
1039
 
1017
- invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select do |method|
1040
+ invalid_methods = INVALID_METHODS_FOR_UPDATE_AND_DELETE_ALL.select do |method|
1018
1041
  value = @values[method]
1019
1042
  method == :distinct ? value : value&.any?
1020
1043
  end
@@ -1022,20 +1045,18 @@ module ActiveRecord
1022
1045
  raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
1023
1046
  end
1024
1047
 
1025
- klass.with_connection do |c|
1026
- arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
1048
+ model.with_connection do |c|
1049
+ arel = eager_loading? ? apply_join_dependency.arel : arel()
1027
1050
  arel.source.left = table
1028
1051
 
1029
- group_values_arel_columns = arel_columns(group_values.uniq)
1030
- having_clause_ast = having_clause.ast unless having_clause.empty?
1031
- key = if klass.composite_primary_key?
1052
+ key = if model.composite_primary_key?
1032
1053
  primary_key.map { |pk| table[pk] }
1033
1054
  else
1034
1055
  table[primary_key]
1035
1056
  end
1036
- stmt = arel.compile_delete(key, having_clause_ast, group_values_arel_columns)
1057
+ stmt = arel.compile_delete(key)
1037
1058
 
1038
- c.delete(stmt, "#{klass} Delete All").tap { reset }
1059
+ c.delete(stmt, "#{model} Delete All").tap { reset }
1039
1060
  end
1040
1061
  end
1041
1062
 
@@ -1060,6 +1081,10 @@ module ActiveRecord
1060
1081
  def delete(id_or_array)
1061
1082
  return 0 if id_or_array.nil? || (id_or_array.is_a?(Array) && id_or_array.empty?)
1062
1083
 
1084
+ if model.composite_primary_key? && !id_or_array.first.is_a?(Array)
1085
+ id_or_array = [id_or_array]
1086
+ end
1087
+
1063
1088
  where(model.primary_key => id_or_array).delete_all
1064
1089
  end
1065
1090
 
@@ -1134,9 +1159,6 @@ module ActiveRecord
1134
1159
  # for queries to actually be executed concurrently. Otherwise it defaults to
1135
1160
  # executing them in the foreground.
1136
1161
  #
1137
- # +load_async+ will also fall back to executing in the foreground in the test environment when transactional
1138
- # fixtures are enabled.
1139
- #
1140
1162
  # If the query was actually executed in the background, the Active Record logs will show
1141
1163
  # it by prefixing the log line with <tt>ASYNC</tt>:
1142
1164
  #
@@ -1146,7 +1168,7 @@ module ActiveRecord
1146
1168
  return load if !c.async_enabled?
1147
1169
 
1148
1170
  unless loaded?
1149
- result = exec_main_query(async: c.current_transaction.closed?)
1171
+ result = exec_main_query(async: !c.current_transaction.joinable?)
1150
1172
 
1151
1173
  if result.is_a?(Array)
1152
1174
  @records = result
@@ -1160,6 +1182,16 @@ module ActiveRecord
1160
1182
  self
1161
1183
  end
1162
1184
 
1185
+ def then(&block) # :nodoc:
1186
+ if @future_result
1187
+ @future_result.then do
1188
+ yield self
1189
+ end
1190
+ else
1191
+ super
1192
+ end
1193
+ end
1194
+
1163
1195
  # Returns <tt>true</tt> if the relation was scheduled on the background
1164
1196
  # thread pool.
1165
1197
  def scheduled?
@@ -1190,7 +1222,7 @@ module ActiveRecord
1190
1222
  def reset
1191
1223
  @future_result&.cancel
1192
1224
  @future_result = nil
1193
- @delegate_to_klass = false
1225
+ @delegate_to_model = false
1194
1226
  @to_sql = @arel = @loaded = @should_eager_load = nil
1195
1227
  @offsets = @take = nil
1196
1228
  @cache_keys = nil
@@ -1210,7 +1242,7 @@ module ActiveRecord
1210
1242
  relation.to_sql
1211
1243
  end
1212
1244
  else
1213
- klass.with_connection do |conn|
1245
+ model.with_connection do |conn|
1214
1246
  conn.unprepared_statement { conn.to_sql(arel) }
1215
1247
  end
1216
1248
  end
@@ -1220,12 +1252,12 @@ module ActiveRecord
1220
1252
  #
1221
1253
  # User.where(name: 'Oscar').where_values_hash
1222
1254
  # # => {name: "Oscar"}
1223
- def where_values_hash(relation_table_name = klass.table_name) # :nodoc:
1255
+ def where_values_hash(relation_table_name = model.table_name) # :nodoc:
1224
1256
  where_clause.to_h(relation_table_name)
1225
1257
  end
1226
1258
 
1227
1259
  def scope_for_create
1228
- hash = where_clause.to_h(klass.table_name, equality_only: true)
1260
+ hash = where_clause.to_h(model.table_name, equality_only: true)
1229
1261
  create_with_value.each { |k, v| hash[k.to_s] = v } unless create_with_value.empty?
1230
1262
  hash
1231
1263
  end
@@ -1271,6 +1303,10 @@ module ActiveRecord
1271
1303
  records.blank?
1272
1304
  end
1273
1305
 
1306
+ def readonly?
1307
+ readonly_value
1308
+ end
1309
+
1274
1310
  def values
1275
1311
  @values.dup
1276
1312
  end
@@ -1289,7 +1325,7 @@ module ActiveRecord
1289
1325
  end
1290
1326
 
1291
1327
  def empty_scope? # :nodoc:
1292
- @values == klass.unscoped.values
1328
+ @values == model.unscoped.values
1293
1329
  end
1294
1330
 
1295
1331
  def has_limit_or_offset? # :nodoc:
@@ -1297,7 +1333,7 @@ module ActiveRecord
1297
1333
  end
1298
1334
 
1299
1335
  def alias_tracker(joins = [], aliases = nil) # :nodoc:
1300
- ActiveRecord::Associations::AliasTracker.create(connection_pool, table.name, joins, aliases)
1336
+ ActiveRecord::Associations::AliasTracker.create(model.connection_pool, table.name, joins, aliases)
1301
1337
  end
1302
1338
 
1303
1339
  class StrictLoadingScope # :nodoc:
@@ -1327,46 +1363,46 @@ module ActiveRecord
1327
1363
 
1328
1364
  private
1329
1365
  def already_in_scope?(registry)
1330
- @delegate_to_klass && registry.current_scope(klass, true)
1366
+ @delegate_to_model && registry.current_scope(model, true)
1331
1367
  end
1332
1368
 
1333
1369
  def global_scope?(registry)
1334
- registry.global_current_scope(klass, true)
1370
+ registry.global_current_scope(model, true)
1335
1371
  end
1336
1372
 
1337
1373
  def current_scope_restoring_block(&block)
1338
- current_scope = klass.current_scope(true)
1374
+ current_scope = model.current_scope(true)
1339
1375
  -> record do
1340
- klass.current_scope = current_scope
1376
+ model.current_scope = current_scope
1341
1377
  yield record if block_given?
1342
1378
  end
1343
1379
  end
1344
1380
 
1345
1381
  def _new(attributes, &block)
1346
- klass.new(attributes, &block)
1382
+ model.new(attributes, &block)
1347
1383
  end
1348
1384
 
1349
1385
  def _create(attributes, &block)
1350
- klass.create(attributes, &block)
1386
+ model.create(attributes, &block)
1351
1387
  end
1352
1388
 
1353
1389
  def _create!(attributes, &block)
1354
- klass.create!(attributes, &block)
1390
+ model.create!(attributes, &block)
1355
1391
  end
1356
1392
 
1357
1393
  def _scoping(scope, registry, all_queries = false)
1358
- previous = registry.current_scope(klass, true)
1359
- registry.set_current_scope(klass, scope)
1394
+ previous = registry.current_scope(model, true)
1395
+ registry.set_current_scope(model, scope)
1360
1396
 
1361
1397
  if all_queries
1362
- previous_global = registry.global_current_scope(klass, true)
1363
- registry.set_global_current_scope(klass, scope)
1398
+ previous_global = registry.global_current_scope(model, true)
1399
+ registry.set_global_current_scope(model, scope)
1364
1400
  end
1365
1401
  yield
1366
1402
  ensure
1367
- registry.set_current_scope(klass, previous)
1403
+ registry.set_current_scope(model, previous)
1368
1404
  if all_queries
1369
- registry.set_global_current_scope(klass, previous_global)
1405
+ registry.set_global_current_scope(model, previous_global)
1370
1406
  end
1371
1407
  end
1372
1408
 
@@ -1378,7 +1414,7 @@ module ActiveRecord
1378
1414
  value = Arel::Nodes::Grouping.new(value)
1379
1415
  end
1380
1416
  else
1381
- type = klass.type_for_attribute(attr.name)
1417
+ type = model.type_for_attribute(attr.name)
1382
1418
  value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
1383
1419
  end
1384
1420
  [attr, value]
@@ -1387,12 +1423,16 @@ module ActiveRecord
1387
1423
 
1388
1424
  def _increment_attribute(attribute, value = 1)
1389
1425
  bind = predicate_builder.build_bind_attribute(attribute.name, value.abs)
1390
- expr = table.coalesce(Arel::Nodes::UnqualifiedColumn.new(attribute), 0)
1426
+ expr = table.coalesce(attribute, 0)
1391
1427
  expr = value < 0 ? expr - bind : expr + bind
1392
1428
  expr.expr
1393
1429
  end
1394
1430
 
1395
1431
  def exec_queries(&block)
1432
+ if lock_value && model.current_preventing_writes
1433
+ raise ActiveRecord::ReadOnlyError, "Lock query attempted while in readonly mode"
1434
+ end
1435
+
1396
1436
  skip_query_cache_if_necessary do
1397
1437
  rows = if scheduled?
1398
1438
  future = @future_result
@@ -1425,20 +1465,20 @@ module ActiveRecord
1425
1465
  if where_clause.contradiction?
1426
1466
  [].freeze
1427
1467
  elsif eager_loading?
1428
- klass.with_connection do |c|
1468
+ model.with_connection do |c|
1429
1469
  apply_join_dependency do |relation, join_dependency|
1430
1470
  if relation.null_relation?
1431
1471
  [].freeze
1432
1472
  else
1433
1473
  relation = join_dependency.apply_column_aliases(relation)
1434
1474
  @_join_dependency = join_dependency
1435
- c.select_all(relation.arel, "SQL", async: async)
1475
+ c.select_all(relation.arel, "#{model.name} Eager Load", async: async)
1436
1476
  end
1437
1477
  end
1438
1478
  end
1439
1479
  else
1440
- klass.with_connection do |c|
1441
- klass._query_by_sql(c, arel, async: async)
1480
+ model.with_connection do |c|
1481
+ model._query_by_sql(c, arel, async: async)
1442
1482
  end
1443
1483
  end
1444
1484
  end
@@ -1451,13 +1491,13 @@ module ActiveRecord
1451
1491
  @_join_dependency = nil
1452
1492
  records
1453
1493
  else
1454
- klass._load_from_sql(rows, &block).freeze
1494
+ model._load_from_sql(rows, &block).freeze
1455
1495
  end
1456
1496
  end
1457
1497
 
1458
1498
  def skip_query_cache_if_necessary(&block)
1459
1499
  if skip_query_cache_value
1460
- uncached(&block)
1500
+ model.uncached(&block)
1461
1501
  else
1462
1502
  yield
1463
1503
  end