activerecord 7.1.5.1 → 8.0.2

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 (206) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +369 -2484
  3. data/README.rdoc +15 -15
  4. data/examples/performance.rb +2 -2
  5. data/lib/active_record/association_relation.rb +2 -1
  6. data/lib/active_record/associations/alias_tracker.rb +31 -23
  7. data/lib/active_record/associations/association.rb +43 -12
  8. data/lib/active_record/associations/belongs_to_association.rb +21 -8
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  10. data/lib/active_record/associations/builder/association.rb +7 -6
  11. data/lib/active_record/associations/builder/belongs_to.rb +1 -0
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
  13. data/lib/active_record/associations/builder/has_many.rb +3 -4
  14. data/lib/active_record/associations/builder/has_one.rb +3 -4
  15. data/lib/active_record/associations/collection_association.rb +17 -9
  16. data/lib/active_record/associations/collection_proxy.rb +14 -1
  17. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  18. data/lib/active_record/associations/errors.rb +265 -0
  19. data/lib/active_record/associations/has_many_association.rb +1 -1
  20. data/lib/active_record/associations/has_many_through_association.rb +10 -3
  21. data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
  22. data/lib/active_record/associations/nested_error.rb +47 -0
  23. data/lib/active_record/associations/preloader/association.rb +4 -3
  24. data/lib/active_record/associations/preloader/branch.rb +7 -1
  25. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  26. data/lib/active_record/associations/singular_association.rb +14 -3
  27. data/lib/active_record/associations/through_association.rb +1 -1
  28. data/lib/active_record/associations.rb +92 -295
  29. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  30. data/lib/active_record/attribute_assignment.rb +0 -2
  31. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  32. data/lib/active_record/attribute_methods/primary_key.rb +25 -61
  33. data/lib/active_record/attribute_methods/read.rb +1 -13
  34. data/lib/active_record/attribute_methods/serialization.rb +4 -24
  35. data/lib/active_record/attribute_methods/time_zone_conversion.rb +9 -18
  36. data/lib/active_record/attribute_methods.rb +71 -75
  37. data/lib/active_record/attributes.rb +63 -49
  38. data/lib/active_record/autosave_association.rb +92 -57
  39. data/lib/active_record/base.rb +2 -3
  40. data/lib/active_record/callbacks.rb +1 -1
  41. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +48 -122
  42. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
  43. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -1
  44. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +286 -77
  45. data/lib/active_record/connection_adapters/abstract/database_statements.rb +119 -55
  46. data/lib/active_record/connection_adapters/abstract/query_cache.rb +197 -76
  47. data/lib/active_record/connection_adapters/abstract/quoting.rb +66 -92
  48. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
  49. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +12 -3
  50. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +48 -12
  51. data/lib/active_record/connection_adapters/abstract/transaction.rb +140 -67
  52. data/lib/active_record/connection_adapters/abstract_adapter.rb +85 -90
  53. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +71 -52
  54. data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
  55. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
  56. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
  57. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +56 -45
  58. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +92 -101
  59. data/lib/active_record/connection_adapters/mysql2_adapter.rb +13 -31
  60. data/lib/active_record/connection_adapters/pool_config.rb +14 -13
  61. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +86 -41
  62. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  63. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  64. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  65. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  66. data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
  67. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  68. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
  69. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +36 -20
  70. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +3 -2
  71. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +75 -28
  72. data/lib/active_record/connection_adapters/postgresql_adapter.rb +73 -113
  73. data/lib/active_record/connection_adapters/schema_cache.rb +124 -131
  74. data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
  75. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +81 -97
  76. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -46
  77. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
  78. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
  79. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +29 -0
  80. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +35 -3
  81. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +183 -87
  82. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  83. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +39 -69
  84. data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
  85. data/lib/active_record/connection_adapters.rb +65 -0
  86. data/lib/active_record/connection_handling.rb +74 -37
  87. data/lib/active_record/core.rb +132 -51
  88. data/lib/active_record/counter_cache.rb +19 -10
  89. data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
  90. data/lib/active_record/database_configurations/database_config.rb +23 -4
  91. data/lib/active_record/database_configurations/hash_config.rb +46 -34
  92. data/lib/active_record/database_configurations/url_config.rb +20 -1
  93. data/lib/active_record/database_configurations.rb +1 -1
  94. data/lib/active_record/delegated_type.rb +41 -17
  95. data/lib/active_record/dynamic_matchers.rb +2 -2
  96. data/lib/active_record/encryption/config.rb +3 -1
  97. data/lib/active_record/encryption/encryptable_record.rb +7 -7
  98. data/lib/active_record/encryption/encrypted_attribute_type.rb +33 -4
  99. data/lib/active_record/encryption/encryptor.rb +28 -6
  100. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  101. data/lib/active_record/encryption/key_provider.rb +1 -1
  102. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  103. data/lib/active_record/encryption/message_serializer.rb +4 -0
  104. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  105. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  106. data/lib/active_record/encryption/scheme.rb +8 -1
  107. data/lib/active_record/enum.rb +20 -16
  108. data/lib/active_record/errors.rb +54 -20
  109. data/lib/active_record/explain.rb +13 -24
  110. data/lib/active_record/fixtures.rb +37 -33
  111. data/lib/active_record/future_result.rb +21 -13
  112. data/lib/active_record/gem_version.rb +4 -4
  113. data/lib/active_record/inheritance.rb +4 -2
  114. data/lib/active_record/insert_all.rb +19 -16
  115. data/lib/active_record/integration.rb +4 -1
  116. data/lib/active_record/internal_metadata.rb +48 -34
  117. data/lib/active_record/locking/optimistic.rb +8 -7
  118. data/lib/active_record/log_subscriber.rb +5 -32
  119. data/lib/active_record/message_pack.rb +1 -1
  120. data/lib/active_record/migration/command_recorder.rb +33 -14
  121. data/lib/active_record/migration/compatibility.rb +8 -3
  122. data/lib/active_record/migration/default_strategy.rb +4 -5
  123. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  124. data/lib/active_record/migration.rb +104 -98
  125. data/lib/active_record/model_schema.rb +32 -70
  126. data/lib/active_record/nested_attributes.rb +15 -9
  127. data/lib/active_record/normalization.rb +3 -7
  128. data/lib/active_record/persistence.rb +127 -451
  129. data/lib/active_record/query_cache.rb +19 -8
  130. data/lib/active_record/query_logs.rb +104 -37
  131. data/lib/active_record/query_logs_formatter.rb +17 -28
  132. data/lib/active_record/querying.rb +24 -12
  133. data/lib/active_record/railtie.rb +26 -68
  134. data/lib/active_record/railties/controller_runtime.rb +13 -4
  135. data/lib/active_record/railties/databases.rake +43 -61
  136. data/lib/active_record/reflection.rb +112 -53
  137. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  138. data/lib/active_record/relation/batches.rb +138 -72
  139. data/lib/active_record/relation/calculations.rb +122 -82
  140. data/lib/active_record/relation/delegation.rb +30 -22
  141. data/lib/active_record/relation/finder_methods.rb +32 -18
  142. data/lib/active_record/relation/merger.rb +12 -14
  143. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  144. data/lib/active_record/relation/predicate_builder/association_query_value.rb +10 -2
  145. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
  146. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  147. data/lib/active_record/relation/predicate_builder.rb +16 -3
  148. data/lib/active_record/relation/query_attribute.rb +1 -1
  149. data/lib/active_record/relation/query_methods.rb +317 -101
  150. data/lib/active_record/relation/spawn_methods.rb +3 -19
  151. data/lib/active_record/relation/where_clause.rb +7 -19
  152. data/lib/active_record/relation.rb +561 -119
  153. data/lib/active_record/result.rb +95 -46
  154. data/lib/active_record/runtime_registry.rb +39 -0
  155. data/lib/active_record/sanitization.rb +31 -25
  156. data/lib/active_record/schema.rb +8 -6
  157. data/lib/active_record/schema_dumper.rb +53 -20
  158. data/lib/active_record/schema_migration.rb +31 -14
  159. data/lib/active_record/scoping/named.rb +6 -2
  160. data/lib/active_record/signed_id.rb +24 -4
  161. data/lib/active_record/statement_cache.rb +19 -19
  162. data/lib/active_record/store.rb +7 -3
  163. data/lib/active_record/table_metadata.rb +2 -13
  164. data/lib/active_record/tasks/database_tasks.rb +87 -58
  165. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -3
  166. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  167. data/lib/active_record/tasks/sqlite_database_tasks.rb +4 -3
  168. data/lib/active_record/test_fixtures.rb +98 -89
  169. data/lib/active_record/testing/query_assertions.rb +121 -0
  170. data/lib/active_record/timestamp.rb +2 -2
  171. data/lib/active_record/token_for.rb +22 -12
  172. data/lib/active_record/touch_later.rb +1 -1
  173. data/lib/active_record/transaction.rb +132 -0
  174. data/lib/active_record/transactions.rb +72 -17
  175. data/lib/active_record/translation.rb +0 -2
  176. data/lib/active_record/type/serialized.rb +1 -3
  177. data/lib/active_record/type_caster/connection.rb +4 -4
  178. data/lib/active_record/validations/associated.rb +9 -3
  179. data/lib/active_record/validations/uniqueness.rb +23 -18
  180. data/lib/active_record/validations.rb +4 -1
  181. data/lib/active_record.rb +138 -57
  182. data/lib/arel/alias_predication.rb +1 -1
  183. data/lib/arel/collectors/bind.rb +4 -2
  184. data/lib/arel/collectors/composite.rb +7 -0
  185. data/lib/arel/collectors/sql_string.rb +2 -2
  186. data/lib/arel/collectors/substitute_binds.rb +3 -3
  187. data/lib/arel/nodes/binary.rb +1 -7
  188. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  189. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  190. data/lib/arel/nodes/node.rb +5 -4
  191. data/lib/arel/nodes/sql_literal.rb +8 -1
  192. data/lib/arel/nodes.rb +2 -2
  193. data/lib/arel/predications.rb +1 -1
  194. data/lib/arel/select_manager.rb +1 -1
  195. data/lib/arel/table.rb +3 -7
  196. data/lib/arel/tree_manager.rb +3 -2
  197. data/lib/arel/update_manager.rb +2 -1
  198. data/lib/arel/visitors/dot.rb +1 -0
  199. data/lib/arel/visitors/mysql.rb +9 -4
  200. data/lib/arel/visitors/postgresql.rb +1 -12
  201. data/lib/arel/visitors/sqlite.rb +25 -0
  202. data/lib/arel/visitors/to_sql.rb +29 -16
  203. data/lib/arel.rb +7 -3
  204. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  205. metadata +18 -16
  206. data/lib/active_record/relation/record_fetch_warning.rb +0 -49
@@ -188,6 +188,27 @@ module ActiveRecord
188
188
  # #after_commit is a good spot to put in a hook to clearing a cache since clearing it from
189
189
  # within a transaction could trigger the cache to be regenerated before the database is updated.
190
190
  #
191
+ # ==== NOTE: Callbacks are deduplicated per callback by filter.
192
+ #
193
+ # Trying to define multiple callbacks with the same filter will result in a single callback being run.
194
+ #
195
+ # For example:
196
+ #
197
+ # after_commit :do_something
198
+ # after_commit :do_something # only the last one will be called
199
+ #
200
+ # This applies to all variations of <tt>after_*_commit</tt> callbacks as well.
201
+ #
202
+ # after_commit :do_something
203
+ # after_create_commit :do_something
204
+ # after_save_commit :do_something
205
+ #
206
+ # It is recommended to use the +on:+ option to specify when the callback should be run.
207
+ #
208
+ # after_commit :do_something, on: [:create, :update]
209
+ #
210
+ # This is equivalent to using +after_create_commit+ and +after_update_commit+, but will not be deduplicated.
211
+ #
191
212
  # === Caveats
192
213
  #
193
214
  # If you're on MySQL, then do not use Data Definition Language (DDL) operations in nested
@@ -198,18 +219,30 @@ module ActiveRecord
198
219
  # database error will occur because the savepoint has already been
199
220
  # automatically released. The following example demonstrates the problem:
200
221
  #
201
- # Model.connection.transaction do # BEGIN
202
- # Model.connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
203
- # Model.connection.create_table(...) # active_record_1 now automatically released
204
- # end # RELEASE SAVEPOINT active_record_1
205
- # # ^^^^ BOOM! database error!
206
- # end
222
+ # Model.transaction do # BEGIN
223
+ # Model.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
224
+ # Model.lease_connection.create_table(...) # active_record_1 now automatically released
225
+ # end # RELEASE SAVEPOINT active_record_1
226
+ # end # ^^^^ BOOM! database error!
207
227
  #
208
228
  # Note that "TRUNCATE" is also a MySQL DDL statement!
209
229
  module ClassMethods
210
230
  # See the ConnectionAdapters::DatabaseStatements#transaction API docs.
211
231
  def transaction(**options, &block)
212
- connection.transaction(**options, &block)
232
+ with_connection do |connection|
233
+ connection.transaction(**options, &block)
234
+ end
235
+ end
236
+
237
+ # Returns a representation of the current transaction state,
238
+ # which can be a top level transaction, a savepoint, or the absence of a transaction.
239
+ #
240
+ # An object is always returned, whether or not a transaction is currently active.
241
+ # To check if a transaction was opened, use <tt>current_transaction.open?</tt>.
242
+ #
243
+ # See the ActiveRecord::Transaction documentation for detailed behavior.
244
+ def current_transaction
245
+ connection_pool.active_connection&.current_transaction&.user_transaction || Transaction::NULL_TRANSACTION
213
246
  end
214
247
 
215
248
  def before_commit(*args, &block) # :nodoc:
@@ -266,6 +299,25 @@ module ActiveRecord
266
299
  set_callback(:rollback, :after, *args, &block)
267
300
  end
268
301
 
302
+ # Similar to ActiveSupport::Callbacks::ClassMethods#set_callback, but with
303
+ # support for options available on #after_commit and #after_rollback callbacks.
304
+ def set_callback(name, *filter_list, &block)
305
+ options = filter_list.extract_options!
306
+ filter_list << options
307
+
308
+ if name.in?([:commit, :rollback]) && options[:on]
309
+ fire_on = Array(options[:on])
310
+ assert_valid_transaction_action(fire_on)
311
+ options[:if] = [
312
+ -> { transaction_include_any_action?(fire_on) },
313
+ *options[:if]
314
+ ]
315
+ end
316
+
317
+
318
+ super(name, *filter_list, &block)
319
+ end
320
+
269
321
  private
270
322
  def prepend_option
271
323
  if ActiveRecord.run_after_transaction_callbacks_in_order_defined
@@ -354,18 +406,19 @@ module ActiveRecord
354
406
  # This method is available within the context of an ActiveRecord::Base
355
407
  # instance.
356
408
  def with_transaction_returning_status
357
- status = nil
358
- connection = self.class.connection
359
- ensure_finalize = !connection.transaction_open?
409
+ self.class.with_connection do |connection|
410
+ status = nil
411
+ ensure_finalize = !connection.transaction_open?
360
412
 
361
- connection.transaction do
362
- add_to_transaction(ensure_finalize || has_transactional_callbacks?)
363
- remember_transaction_record_state
413
+ connection.transaction do
414
+ add_to_transaction(ensure_finalize || has_transactional_callbacks?)
415
+ remember_transaction_record_state
364
416
 
365
- status = yield
366
- raise ActiveRecord::Rollback unless status
417
+ status = yield
418
+ raise ActiveRecord::Rollback unless status
419
+ end
420
+ status
367
421
  end
368
- status
369
422
  end
370
423
 
371
424
  def trigger_transactional_callbacks? # :nodoc:
@@ -457,7 +510,9 @@ module ActiveRecord
457
510
  # Add the record to the current transaction so that the #after_rollback and #after_commit
458
511
  # callbacks can be called.
459
512
  def add_to_transaction(ensure_finalize = true)
460
- self.class.connection.add_transaction_record(self, ensure_finalize)
513
+ self.class.with_connection do |connection|
514
+ connection.add_transaction_record(self, ensure_finalize)
515
+ end
461
516
  end
462
517
 
463
518
  def has_transactional_callbacks?
@@ -2,8 +2,6 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module Translation
5
- include ActiveModel::Translation
6
-
7
5
  # Set the lookup ancestors for ActiveModel.
8
6
  def lookup_ancestors # :nodoc:
9
7
  klass = self
@@ -30,9 +30,7 @@ module ActiveRecord
30
30
  end
31
31
  end
32
32
 
33
- def inspect
34
- Kernel.instance_method(:inspect).bind_call(self)
35
- end
33
+ define_method(:inspect, Kernel.instance_method(:inspect))
36
34
 
37
35
  def changed_in_place?(raw_old_value, value)
38
36
  return false if value.nil?
@@ -14,18 +14,18 @@ module ActiveRecord
14
14
  end
15
15
 
16
16
  def type_for_attribute(attr_name)
17
- schema_cache = connection.schema_cache
17
+ schema_cache = @klass.schema_cache
18
18
 
19
19
  if schema_cache.data_source_exists?(table_name)
20
20
  column = schema_cache.columns_hash(table_name)[attr_name.to_s]
21
- type = connection.lookup_cast_type_from_column(column) if column
21
+ if column
22
+ type = @klass.with_connection { |connection| connection.lookup_cast_type_from_column(column) }
23
+ end
22
24
  end
23
25
 
24
26
  type || Type.default_value
25
27
  end
26
28
 
27
- delegate :connection, to: :@klass, private: true
28
-
29
29
  private
30
30
  attr_reader :table_name
31
31
  end
@@ -4,14 +4,20 @@ module ActiveRecord
4
4
  module Validations
5
5
  class AssociatedValidator < ActiveModel::EachValidator # :nodoc:
6
6
  def validate_each(record, attribute, value)
7
- if Array(value).reject { |r| valid_object?(r) }.any?
7
+ context = record_validation_context_for_association(record)
8
+
9
+ if Array(value).reject { |association| valid_object?(association, context) }.any?
8
10
  record.errors.add(attribute, :invalid, **options.merge(value: value))
9
11
  end
10
12
  end
11
13
 
12
14
  private
13
- def valid_object?(record)
14
- (record.respond_to?(:marked_for_destruction?) && record.marked_for_destruction?) || record.valid?
15
+ def valid_object?(record, context)
16
+ (record.respond_to?(:marked_for_destruction?) && record.marked_for_destruction?) || record.valid?(context)
17
+ end
18
+
19
+ def record_validation_context_for_association(record)
20
+ record.custom_validation_context? ? record.validation_context : nil
15
21
  end
16
22
  end
17
23
 
@@ -14,6 +14,7 @@ module ActiveRecord
14
14
  end
15
15
  super
16
16
  @klass = options[:class]
17
+ @klass = @klass.superclass if @klass.singleton_class?
17
18
  end
18
19
 
19
20
  def validate_each(record, attribute, value)
@@ -53,17 +54,17 @@ module ActiveRecord
53
54
  private
54
55
  # The check for an existing value should be run from a class that
55
56
  # isn't abstract. This means working down from the current class
56
- # (self), to the first non-abstract class. Since classes don't know
57
- # their subclasses, we have to build the hierarchy between self and
58
- # the record's class.
57
+ # (self), to the first non-abstract class.
59
58
  def find_finder_class_for(record)
60
- class_hierarchy = [record.class]
61
-
62
- while class_hierarchy.first != @klass
63
- class_hierarchy.unshift(class_hierarchy.first.superclass)
59
+ current_class = record.class
60
+ found_class = nil
61
+ loop do
62
+ found_class = current_class unless current_class.abstract_class?
63
+ break if current_class == @klass
64
+ current_class = current_class.superclass
64
65
  end
65
66
 
66
- class_hierarchy.detect { |klass| !klass.abstract_class? }
67
+ found_class
67
68
  end
68
69
 
69
70
  def validation_needed?(klass, record, attribute)
@@ -84,7 +85,7 @@ module ActiveRecord
84
85
  attributes = scope + [attr]
85
86
  attributes = resolve_attributes(record, attributes)
86
87
 
87
- klass.connection.schema_cache.indexes(klass.table_name).any? do |index|
88
+ klass.schema_cache.indexes(klass.table_name).any? do |index|
88
89
  index.unique &&
89
90
  index.where.nil? &&
90
91
  (Array(index.columns) - attributes).empty?
@@ -110,16 +111,20 @@ module ActiveRecord
110
111
 
111
112
  def build_relation(klass, attribute, value)
112
113
  relation = klass.unscoped
113
- comparison = relation.bind_attribute(attribute, value) do |attr, bind|
114
- return relation.none! if bind.unboundable?
114
+ # TODO: Add case-sensitive / case-insensitive operators to Arel
115
+ # to no longer need to checkout a connection here.
116
+ comparison = klass.with_connection do |connection|
117
+ relation.bind_attribute(attribute, value) do |attr, bind|
118
+ return relation.none! if bind.unboundable?
115
119
 
116
- if !options.key?(:case_sensitive) || bind.nil?
117
- klass.connection.default_uniqueness_comparison(attr, bind)
118
- elsif options[:case_sensitive]
119
- klass.connection.case_sensitive_comparison(attr, bind)
120
- else
121
- # will use SQL LOWER function before comparison, unless it detects a case insensitive collation
122
- klass.connection.case_insensitive_comparison(attr, bind)
120
+ if !options.key?(:case_sensitive) || bind.nil?
121
+ connection.default_uniqueness_comparison(attr, bind)
122
+ elsif options[:case_sensitive]
123
+ connection.case_sensitive_comparison(attr, bind)
124
+ else
125
+ # will use SQL LOWER function before comparison, unless it detects a case insensitive collation
126
+ connection.case_insensitive_comparison(attr, bind)
127
+ end
123
128
  end
124
129
  end
125
130
 
@@ -39,7 +39,6 @@ module ActiveRecord
39
39
  # {new_record?}[rdoc-ref:Persistence#new_record?].
40
40
  module Validations
41
41
  extend ActiveSupport::Concern
42
- include ActiveModel::Validations
43
42
 
44
43
  # The validation process on save can be skipped by passing <tt>validate: false</tt>.
45
44
  # The validation context can be changed by passing <tt>context: context</tt>.
@@ -75,6 +74,10 @@ module ActiveRecord
75
74
 
76
75
  alias_method :validate, :valid?
77
76
 
77
+ def custom_validation_context? # :nodoc:
78
+ validation_context && [:create, :update].exclude?(validation_context)
79
+ end
80
+
78
81
  private
79
82
  def default_validation_context
80
83
  new_record? ? :create : :update
data/lib/active_record.rb CHANGED
@@ -25,9 +25,11 @@
25
25
 
26
26
  require "active_support"
27
27
  require "active_support/rails"
28
+ require "active_support/ordered_options"
28
29
  require "active_model"
29
30
  require "arel"
30
31
  require "yaml"
32
+ require "zlib"
31
33
 
32
34
  require "active_record/version"
33
35
  require "active_record/deprecator"
@@ -85,6 +87,7 @@ module ActiveRecord
85
87
  autoload :Timestamp
86
88
  autoload :TokenFor
87
89
  autoload :TouchLater
90
+ autoload :Transaction
88
91
  autoload :Transactions
89
92
  autoload :Translation
90
93
  autoload :Validations
@@ -128,6 +131,8 @@ module ActiveRecord
128
131
  module AttributeMethods
129
132
  extend ActiveSupport::Autoload
130
133
 
134
+ autoload :CompositePrimaryKey
135
+
131
136
  eager_autoload do
132
137
  autoload :BeforeTypeCast
133
138
  autoload :Dirty
@@ -177,18 +182,35 @@ module ActiveRecord
177
182
  singleton_class.attr_accessor :disable_prepared_statements
178
183
  self.disable_prepared_statements = false
179
184
 
185
+ ##
186
+ # :singleton-method: lazily_load_schema_cache
180
187
  # Lazily load the schema cache. This option will load the schema cache
181
- # when a connection is established rather than on boot. If set,
182
- # +config.active_record.use_schema_cache_dump+ will be set to false.
188
+ # when a connection is established rather than on boot.
183
189
  singleton_class.attr_accessor :lazily_load_schema_cache
184
190
  self.lazily_load_schema_cache = false
185
191
 
192
+ ##
193
+ # :singleton-method: schema_cache_ignored_tables
186
194
  # A list of tables or regex's to match tables to ignore when
187
195
  # dumping the schema cache. For example if this is set to +[/^_/]+
188
196
  # the schema cache will not dump tables named with an underscore.
189
197
  singleton_class.attr_accessor :schema_cache_ignored_tables
190
198
  self.schema_cache_ignored_tables = []
191
199
 
200
+ # Checks to see if the +table_name+ is ignored by checking
201
+ # against the +schema_cache_ignored_tables+ option.
202
+ #
203
+ # ActiveRecord.schema_cache_ignored_table?(:developers)
204
+ #
205
+ def self.schema_cache_ignored_table?(table_name)
206
+ ActiveRecord.schema_cache_ignored_tables.any? do |ignored|
207
+ ignored === table_name
208
+ end
209
+ end
210
+
211
+ singleton_class.attr_accessor :database_cli
212
+ self.database_cli = { postgresql: "psql", mysql: %w[mysql mysql5], sqlite: "sqlite3" }
213
+
192
214
  singleton_class.attr_reader :default_timezone
193
215
 
194
216
  # Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling
@@ -203,6 +225,8 @@ module ActiveRecord
203
225
 
204
226
  self.default_timezone = :utc
205
227
 
228
+ ##
229
+ # :singleton-method: db_warnings_action
206
230
  # The action to take when database query produces warning.
207
231
  # Must be one of :ignore, :log, :raise, :report, or a custom proc.
208
232
  # The default is :ignore.
@@ -232,6 +256,8 @@ module ActiveRecord
232
256
 
233
257
  self.db_warnings_action = :ignore
234
258
 
259
+ ##
260
+ # :singleton-method: db_warnings_ignore
235
261
  # Specify allowlist of database warnings.
236
262
  singleton_class.attr_accessor :db_warnings_ignore
237
263
  self.db_warnings_ignore = []
@@ -242,14 +268,8 @@ module ActiveRecord
242
268
  singleton_class.attr_accessor :reading_role
243
269
  self.reading_role = :reading
244
270
 
245
- def self.legacy_connection_handling=(_)
246
- raise ArgumentError, <<~MSG.squish
247
- The `legacy_connection_handling` setter was deprecated in 7.0 and removed in 7.1,
248
- but is still defined in your configuration. Please remove this call as it no longer
249
- has any effect."
250
- MSG
251
- end
252
-
271
+ ##
272
+ # :singleton-method: async_query_executor
253
273
  # Sets the async_query_executor for an application. By default the thread pool executor
254
274
  # set to +nil+ which will not run queries in the background. Applications must configure
255
275
  # a thread pool executor to use this feature. Options are:
@@ -287,11 +307,22 @@ module ActiveRecord
287
307
  @global_executor_concurrency ||= nil
288
308
  end
289
309
 
310
+ @permanent_connection_checkout = true
311
+ singleton_class.attr_reader :permanent_connection_checkout
312
+
313
+ # Defines whether +ActiveRecord::Base.connection+ is allowed, deprecated, or entirely disallowed.
314
+ def self.permanent_connection_checkout=(value)
315
+ unless [true, :deprecated, :disallowed].include?(value)
316
+ raise ArgumentError, "permanent_connection_checkout must be one of: `true`, `:deprecated` or `:disallowed`"
317
+ end
318
+ @permanent_connection_checkout = value
319
+ end
320
+
290
321
  singleton_class.attr_accessor :index_nested_attribute_errors
291
322
  self.index_nested_attribute_errors = false
292
323
 
293
324
  ##
294
- # :singleton-method:
325
+ # :singleton-method: verbose_query_logs
295
326
  #
296
327
  # Specifies if the methods calling database queries should be logged below
297
328
  # their relevant queries. Defaults to false.
@@ -299,7 +330,7 @@ module ActiveRecord
299
330
  self.verbose_query_logs = false
300
331
 
301
332
  ##
302
- # :singleton-method:
333
+ # :singleton-method: queues
303
334
  #
304
335
  # Specifies the names of the queues used by background jobs.
305
336
  singleton_class.attr_accessor :queues
@@ -320,30 +351,18 @@ module ActiveRecord
320
351
  singleton_class.attr_accessor :run_after_transaction_callbacks_in_order_defined
321
352
  self.run_after_transaction_callbacks_in_order_defined = false
322
353
 
323
- singleton_class.attr_accessor :commit_transaction_on_non_local_return
324
- self.commit_transaction_on_non_local_return = false
325
-
326
- ##
327
- # :singleton-method:
328
- # Specify a threshold for the size of query result sets. If the number of
329
- # records in the set exceeds the threshold, a warning is logged. This can
330
- # be used to identify queries which load thousands of records and
331
- # potentially cause memory bloat.
332
- singleton_class.attr_accessor :warn_on_records_fetched_greater_than
333
- self.warn_on_records_fetched_greater_than = false
334
-
335
354
  singleton_class.attr_accessor :application_record_class
336
355
  self.application_record_class = nil
337
356
 
338
357
  ##
339
- # :singleton-method:
358
+ # :singleton-method: action_on_strict_loading_violation
340
359
  # Set the application to log or raise when an association violates strict loading.
341
360
  # Defaults to :raise.
342
361
  singleton_class.attr_accessor :action_on_strict_loading_violation
343
362
  self.action_on_strict_loading_violation = :raise
344
363
 
345
364
  ##
346
- # :singleton-method:
365
+ # :singleton-method: schema_format
347
366
  # Specifies the format to use when dumping the database schema with Rails'
348
367
  # Rakefile. If :sql, the schema is dumped as (potentially database-
349
368
  # specific) SQL statements. If :ruby, the schema is dumped as an
@@ -354,7 +373,7 @@ module ActiveRecord
354
373
  self.schema_format = :ruby
355
374
 
356
375
  ##
357
- # :singleton-method:
376
+ # :singleton-method: error_on_ignored_order
358
377
  # Specifies if an error should be raised if the query has an order being
359
378
  # ignored when doing batch queries. Useful in applications where the
360
379
  # scope being ignored is error-worthy, rather than a warning.
@@ -362,19 +381,27 @@ module ActiveRecord
362
381
  self.error_on_ignored_order = false
363
382
 
364
383
  ##
365
- # :singleton-method:
384
+ # :singleton-method: timestamped_migrations
366
385
  # Specify whether or not to use timestamps for migration versions
367
386
  singleton_class.attr_accessor :timestamped_migrations
368
387
  self.timestamped_migrations = true
369
388
 
370
389
  ##
371
- # :singleton-method:
390
+ # :singleton-method: validate_migration_timestamps
391
+ # Specify whether or not to validate migration timestamps. When set, an error
392
+ # will be raised if a timestamp is more than a day ahead of the timestamp
393
+ # associated with the current time. +timestamped_migrations+ must be set to true.
394
+ singleton_class.attr_accessor :validate_migration_timestamps
395
+ self.validate_migration_timestamps = false
396
+
397
+ ##
398
+ # :singleton-method: migration_strategy
372
399
  # Specify strategy to use for executing migrations.
373
400
  singleton_class.attr_accessor :migration_strategy
374
401
  self.migration_strategy = Migration::DefaultStrategy
375
402
 
376
403
  ##
377
- # :singleton-method:
404
+ # :singleton-method: dump_schema_after_migration
378
405
  # Specify whether schema dump should happen at the end of the
379
406
  # bin/rails db:migrate command. This is true by default, which is useful for the
380
407
  # development environment. This should ideally be false in the production
@@ -383,7 +410,7 @@ module ActiveRecord
383
410
  self.dump_schema_after_migration = true
384
411
 
385
412
  ##
386
- # :singleton-method:
413
+ # :singleton-method: dump_schemas
387
414
  # Specifies which database schemas to dump when calling db:schema:dump.
388
415
  # If the value is :schema_search_path (the default), any schemas listed in
389
416
  # schema_search_path are dumped. Use :all to dump all schemas regardless
@@ -392,22 +419,8 @@ module ActiveRecord
392
419
  singleton_class.attr_accessor :dump_schemas
393
420
  self.dump_schemas = :schema_search_path
394
421
 
395
- def self.suppress_multiple_database_warning
396
- ActiveRecord.deprecator.warn(<<-MSG.squish)
397
- config.active_record.suppress_multiple_database_warning is deprecated and will be removed in Rails 7.2.
398
- It no longer has any effect and should be removed from the configuration file.
399
- MSG
400
- end
401
-
402
- def self.suppress_multiple_database_warning=(value)
403
- ActiveRecord.deprecator.warn(<<-MSG.squish)
404
- config.active_record.suppress_multiple_database_warning= is deprecated and will be removed in Rails 7.2.
405
- It no longer has any effect and should be removed from the configuration file.
406
- MSG
407
- end
408
-
409
422
  ##
410
- # :singleton-method:
423
+ # :singleton-method: verify_foreign_keys_for_fixtures
411
424
  # If true, Rails will verify all foreign keys in the database after loading fixtures.
412
425
  # An error will be raised if there are any foreign key violations, indicating incorrectly
413
426
  # written fixtures.
@@ -415,25 +428,18 @@ module ActiveRecord
415
428
  singleton_class.attr_accessor :verify_foreign_keys_for_fixtures
416
429
  self.verify_foreign_keys_for_fixtures = false
417
430
 
418
- ##
419
- # :singleton-method:
420
- # If true, Rails will continue allowing plural association names in where clauses on singular associations
421
- # This behavior will be removed in Rails 7.2.
422
- singleton_class.attr_accessor :allow_deprecated_singular_associations_name
423
- self.allow_deprecated_singular_associations_name = true
424
-
425
431
  singleton_class.attr_accessor :query_transformers
426
432
  self.query_transformers = []
427
433
 
428
434
  ##
429
- # :singleton-method:
435
+ # :singleton-method: use_yaml_unsafe_load
430
436
  # Application configurable boolean that instructs the YAML Coder to use
431
437
  # an unsafe load if set to true.
432
438
  singleton_class.attr_accessor :use_yaml_unsafe_load
433
439
  self.use_yaml_unsafe_load = false
434
440
 
435
441
  ##
436
- # :singleton-method:
442
+ # :singleton-method: raise_int_wider_than_64bit
437
443
  # Application configurable boolean that denotes whether or not to raise
438
444
  # an exception when the PostgreSQLAdapter is provided with an integer that
439
445
  # is wider than signed 64bit representation
@@ -441,14 +447,14 @@ module ActiveRecord
441
447
  self.raise_int_wider_than_64bit = true
442
448
 
443
449
  ##
444
- # :singleton-method:
450
+ # :singleton-method: yaml_column_permitted_classes
445
451
  # Application configurable array that provides additional permitted classes
446
452
  # to Psych safe_load in the YAML Coder
447
453
  singleton_class.attr_accessor :yaml_column_permitted_classes
448
454
  self.yaml_column_permitted_classes = [Symbol]
449
455
 
450
456
  ##
451
- # :singleton-method:
457
+ # :singleton-method: generate_secure_token_on
452
458
  # Controls when to generate a value for <tt>has_secure_token</tt>
453
459
  # declarations. Defaults to <tt>:create</tt>.
454
460
  singleton_class.attr_accessor :generate_secure_token_on
@@ -462,6 +468,34 @@ module ActiveRecord
462
468
  Marshalling.format_version = value
463
469
  end
464
470
 
471
+ ##
472
+ # :singleton-method: protocol_adapters
473
+ # Provides a mapping between database protocols/DBMSs and the
474
+ # underlying database adapter to be used. This is used only by the
475
+ # <tt>DATABASE_URL</tt> environment variable.
476
+ #
477
+ # == Example
478
+ #
479
+ # DATABASE_URL="mysql://myuser:mypass@localhost/somedatabase"
480
+ #
481
+ # The above URL specifies that MySQL is the desired protocol/DBMS, and the
482
+ # application configuration can then decide which adapter to use. For this example
483
+ # the default mapping is from <tt>mysql</tt> to <tt>mysql2</tt>, but <tt>:trilogy</tt>
484
+ # is also supported.
485
+ #
486
+ # ActiveRecord.protocol_adapters.mysql = "mysql2"
487
+ #
488
+ # The protocols names are arbitrary, and external database adapters can be
489
+ # registered and set here.
490
+ singleton_class.attr_accessor :protocol_adapters
491
+ self.protocol_adapters = ActiveSupport::InheritableOptions.new(
492
+ {
493
+ sqlite: "sqlite3",
494
+ mysql: "mysql2",
495
+ postgres: "postgresql",
496
+ }
497
+ )
498
+
465
499
  def self.eager_load!
466
500
  super
467
501
  ActiveRecord::Locking.eager_load!
@@ -476,6 +510,53 @@ module ActiveRecord
476
510
  def self.disconnect_all!
477
511
  ConnectionAdapters::PoolConfig.disconnect_all!
478
512
  end
513
+
514
+ # Registers a block to be called after all the current transactions have been
515
+ # committed.
516
+ #
517
+ # If there is no currently open transaction, the block is called immediately.
518
+ #
519
+ # If there are multiple nested transactions, the block is called after the outermost one
520
+ # has been committed,
521
+ #
522
+ # If any of the currently open transactions is rolled back, the block is never called.
523
+ #
524
+ # If multiple transactions are open across multiple databases, the block will be invoked
525
+ # if and once all of them have been committed. But note that nesting transactions across
526
+ # two distinct databases is a sharding anti-pattern that comes with a world of hurts.
527
+ def self.after_all_transactions_commit(&block)
528
+ open_transactions = all_open_transactions
529
+
530
+ if open_transactions.empty?
531
+ yield
532
+ elsif open_transactions.size == 1
533
+ open_transactions.first.after_commit(&block)
534
+ else
535
+ count = open_transactions.size
536
+ callback = -> do
537
+ count -= 1
538
+ block.call if count.zero?
539
+ end
540
+ open_transactions.each do |t|
541
+ t.after_commit(&callback)
542
+ end
543
+ open_transactions = nil # rubocop:disable Lint/UselessAssignment avoid holding it in the closure
544
+ end
545
+ end
546
+
547
+ def self.all_open_transactions # :nodoc:
548
+ open_transactions = []
549
+ Base.connection_handler.each_connection_pool do |pool|
550
+ if active_connection = pool.active_connection
551
+ current_transaction = active_connection.current_transaction
552
+
553
+ if current_transaction.open? && current_transaction.joinable? && !current_transaction.state.invalidated?
554
+ open_transactions << current_transaction
555
+ end
556
+ end
557
+ end
558
+ open_transactions
559
+ end
479
560
  end
480
561
 
481
562
  ActiveSupport.on_load(:active_record) do
@@ -3,7 +3,7 @@
3
3
  module Arel # :nodoc: all
4
4
  module AliasPredication
5
5
  def as(other)
6
- Nodes::As.new self, Nodes::SqlLiteral.new(other)
6
+ Nodes::As.new self, Nodes::SqlLiteral.new(other, retryable: true)
7
7
  end
8
8
  end
9
9
  end