activerecord 8.0.0 → 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 (202) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1381 -241
  3. data/README.rdoc +2 -2
  4. data/lib/active_record/association_relation.rb +1 -1
  5. data/lib/active_record/associations/alias_tracker.rb +6 -4
  6. data/lib/active_record/associations/association.rb +35 -17
  7. data/lib/active_record/associations/association_scope.rb +1 -1
  8. data/lib/active_record/associations/belongs_to_association.rb +20 -4
  9. data/lib/active_record/associations/builder/association.rb +16 -5
  10. data/lib/active_record/associations/builder/belongs_to.rb +36 -8
  11. data/lib/active_record/associations/builder/collection_association.rb +7 -3
  12. data/lib/active_record/associations/builder/has_one.rb +1 -1
  13. data/lib/active_record/associations/builder/singular_association.rb +33 -5
  14. data/lib/active_record/associations/collection_association.rb +9 -5
  15. data/lib/active_record/associations/collection_proxy.rb +26 -4
  16. data/lib/active_record/associations/deprecation.rb +88 -0
  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_one_association.rb +1 -0
  20. data/lib/active_record/associations/join_dependency/join_association.rb +25 -27
  21. data/lib/active_record/associations/join_dependency.rb +5 -4
  22. data/lib/active_record/associations/preloader/association.rb +1 -1
  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.rb +159 -21
  26. data/lib/active_record/attribute_methods/primary_key.rb +2 -1
  27. data/lib/active_record/attribute_methods/query.rb +34 -0
  28. data/lib/active_record/attribute_methods/serialization.rb +19 -6
  29. data/lib/active_record/attribute_methods/time_zone_conversion.rb +10 -2
  30. data/lib/active_record/attribute_methods.rb +24 -17
  31. data/lib/active_record/attributes.rb +40 -26
  32. data/lib/active_record/autosave_association.rb +22 -12
  33. data/lib/active_record/base.rb +3 -4
  34. data/lib/active_record/callbacks.rb +1 -2
  35. data/lib/active_record/coders/column_serializer.rb +1 -1
  36. data/lib/active_record/coders/json.rb +16 -6
  37. data/lib/active_record/coders/yaml_column.rb +2 -2
  38. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +19 -18
  39. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -3
  40. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +68 -12
  41. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +474 -112
  42. data/lib/active_record/connection_adapters/abstract/database_statements.rb +68 -41
  43. data/lib/active_record/connection_adapters/abstract/query_cache.rb +36 -9
  44. data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -24
  45. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +7 -2
  46. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +31 -35
  47. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
  48. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +94 -28
  49. data/lib/active_record/connection_adapters/abstract/transaction.rb +25 -3
  50. data/lib/active_record/connection_adapters/abstract_adapter.rb +166 -64
  51. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +133 -40
  52. data/lib/active_record/connection_adapters/column.rb +14 -4
  53. data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
  54. data/lib/active_record/connection_adapters/mysql/quoting.rb +7 -1
  55. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
  56. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +42 -5
  57. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +40 -25
  58. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +66 -15
  59. data/lib/active_record/connection_adapters/mysql2_adapter.rb +9 -3
  60. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  61. data/lib/active_record/connection_adapters/postgresql/column.rb +8 -2
  62. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +29 -33
  63. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -2
  64. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +46 -3
  65. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
  66. data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
  67. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  68. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +8 -6
  69. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -33
  70. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +70 -32
  71. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +99 -54
  72. data/lib/active_record/connection_adapters/postgresql_adapter.rb +40 -12
  73. data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
  74. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +39 -23
  75. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -8
  76. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +15 -3
  77. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +7 -14
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +68 -37
  79. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  80. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +8 -3
  81. data/lib/active_record/connection_adapters/trilogy_adapter.rb +16 -2
  82. data/lib/active_record/connection_adapters.rb +1 -0
  83. data/lib/active_record/connection_handling.rb +15 -10
  84. data/lib/active_record/core.rb +44 -12
  85. data/lib/active_record/counter_cache.rb +36 -9
  86. data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
  87. data/lib/active_record/database_configurations/database_config.rb +5 -1
  88. data/lib/active_record/database_configurations/hash_config.rb +66 -9
  89. data/lib/active_record/database_configurations/url_config.rb +15 -3
  90. data/lib/active_record/database_configurations.rb +7 -3
  91. data/lib/active_record/delegated_type.rb +19 -19
  92. data/lib/active_record/disable_joins_association_relation.rb +5 -1
  93. data/lib/active_record/dynamic_matchers.rb +54 -69
  94. data/lib/active_record/encryption/encryptable_record.rb +5 -5
  95. data/lib/active_record/encryption/encrypted_attribute_type.rb +2 -2
  96. data/lib/active_record/encryption/encrypted_fixtures.rb +12 -7
  97. data/lib/active_record/encryption/encryptor.rb +39 -25
  98. data/lib/active_record/encryption/scheme.rb +1 -1
  99. data/lib/active_record/enum.rb +37 -20
  100. data/lib/active_record/errors.rb +23 -7
  101. data/lib/active_record/explain.rb +1 -1
  102. data/lib/active_record/explain_registry.rb +51 -2
  103. data/lib/active_record/filter_attribute_handler.rb +76 -0
  104. data/lib/active_record/fixture_set/table_row.rb +19 -2
  105. data/lib/active_record/fixtures.rb +2 -2
  106. data/lib/active_record/future_result.rb +5 -3
  107. data/lib/active_record/gem_version.rb +2 -2
  108. data/lib/active_record/inheritance.rb +9 -4
  109. data/lib/active_record/insert_all.rb +14 -9
  110. data/lib/active_record/locking/optimistic.rb +7 -0
  111. data/lib/active_record/locking/pessimistic.rb +5 -0
  112. data/lib/active_record/log_subscriber.rb +30 -22
  113. data/lib/active_record/message_pack.rb +13 -0
  114. data/lib/active_record/middleware/shard_selector.rb +34 -17
  115. data/lib/active_record/migration/command_recorder.rb +32 -6
  116. data/lib/active_record/migration/compatibility.rb +34 -24
  117. data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
  118. data/lib/active_record/migration.rb +31 -21
  119. data/lib/active_record/model_schema.rb +38 -12
  120. data/lib/active_record/nested_attributes.rb +10 -6
  121. data/lib/active_record/persistence.rb +35 -3
  122. data/lib/active_record/query_cache.rb +22 -15
  123. data/lib/active_record/query_logs.rb +7 -7
  124. data/lib/active_record/querying.rb +17 -5
  125. data/lib/active_record/railtie.rb +35 -6
  126. data/lib/active_record/railties/controller_runtime.rb +11 -6
  127. data/lib/active_record/railties/databases.rake +24 -20
  128. data/lib/active_record/railties/job_checkpoints.rb +15 -0
  129. data/lib/active_record/railties/job_runtime.rb +10 -11
  130. data/lib/active_record/reflection.rb +36 -1
  131. data/lib/active_record/relation/batches.rb +25 -11
  132. data/lib/active_record/relation/calculations.rb +62 -41
  133. data/lib/active_record/relation/delegation.rb +0 -1
  134. data/lib/active_record/relation/finder_methods.rb +76 -45
  135. data/lib/active_record/relation/merger.rb +4 -2
  136. data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
  137. data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -9
  138. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +7 -7
  139. data/lib/active_record/relation/predicate_builder.rb +10 -8
  140. data/lib/active_record/relation/query_attribute.rb +4 -2
  141. data/lib/active_record/relation/query_methods.rb +106 -45
  142. data/lib/active_record/relation/spawn_methods.rb +6 -6
  143. data/lib/active_record/relation/where_clause.rb +10 -11
  144. data/lib/active_record/relation.rb +63 -31
  145. data/lib/active_record/result.rb +44 -21
  146. data/lib/active_record/runtime_registry.rb +42 -58
  147. data/lib/active_record/sanitization.rb +2 -0
  148. data/lib/active_record/schema.rb +1 -1
  149. data/lib/active_record/schema_dumper.rb +41 -21
  150. data/lib/active_record/scoping.rb +0 -1
  151. data/lib/active_record/secure_token.rb +3 -3
  152. data/lib/active_record/signed_id.rb +48 -19
  153. data/lib/active_record/statement_cache.rb +16 -12
  154. data/lib/active_record/store.rb +46 -19
  155. data/lib/active_record/structured_event_subscriber.rb +85 -0
  156. data/lib/active_record/table_metadata.rb +5 -20
  157. data/lib/active_record/tasks/abstract_tasks.rb +83 -0
  158. data/lib/active_record/tasks/database_tasks.rb +44 -45
  159. data/lib/active_record/tasks/mysql_database_tasks.rb +4 -56
  160. data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
  161. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -26
  162. data/lib/active_record/test_databases.rb +18 -4
  163. data/lib/active_record/test_fixtures.rb +40 -6
  164. data/lib/active_record/testing/query_assertions.rb +8 -2
  165. data/lib/active_record/timestamp.rb +4 -2
  166. data/lib/active_record/transaction.rb +2 -5
  167. data/lib/active_record/transactions.rb +58 -32
  168. data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
  169. data/lib/active_record/type/internal/timezone.rb +7 -0
  170. data/lib/active_record/type/json.rb +13 -2
  171. data/lib/active_record/type/serialized.rb +16 -4
  172. data/lib/active_record/type/type_map.rb +1 -1
  173. data/lib/active_record/type_caster/connection.rb +2 -1
  174. data/lib/active_record/validations/associated.rb +1 -1
  175. data/lib/active_record.rb +72 -7
  176. data/lib/arel/alias_predication.rb +2 -0
  177. data/lib/arel/collectors/bind.rb +1 -1
  178. data/lib/arel/collectors/sql_string.rb +1 -1
  179. data/lib/arel/collectors/substitute_binds.rb +2 -2
  180. data/lib/arel/crud.rb +8 -11
  181. data/lib/arel/delete_manager.rb +5 -0
  182. data/lib/arel/nodes/binary.rb +1 -1
  183. data/lib/arel/nodes/count.rb +2 -2
  184. data/lib/arel/nodes/delete_statement.rb +4 -2
  185. data/lib/arel/nodes/function.rb +4 -10
  186. data/lib/arel/nodes/named_function.rb +2 -2
  187. data/lib/arel/nodes/node.rb +2 -2
  188. data/lib/arel/nodes/sql_literal.rb +1 -1
  189. data/lib/arel/nodes/update_statement.rb +4 -2
  190. data/lib/arel/nodes.rb +0 -2
  191. data/lib/arel/predications.rb +1 -3
  192. data/lib/arel/select_manager.rb +13 -4
  193. data/lib/arel/update_manager.rb +5 -0
  194. data/lib/arel/visitors/dot.rb +2 -3
  195. data/lib/arel/visitors/postgresql.rb +55 -0
  196. data/lib/arel/visitors/sqlite.rb +55 -8
  197. data/lib/arel/visitors/to_sql.rb +7 -23
  198. data/lib/arel.rb +3 -1
  199. data/lib/rails/generators/active_record/application_record/USAGE +1 -1
  200. metadata +16 -15
  201. data/lib/active_record/explain_subscriber.rb +0 -34
  202. data/lib/active_record/normalization.rb +0 -163
@@ -13,7 +13,7 @@ module ActiveRecord
13
13
  scope: [:kind, :name]
14
14
  end
15
15
 
16
- attr_accessor :_new_record_before_last_commit # :nodoc:
16
+ attr_accessor :_new_record_before_last_commit, :_last_transaction_return_status # :nodoc:
17
17
 
18
18
  # = Active Record \Transactions
19
19
  #
@@ -219,22 +219,41 @@ module ActiveRecord
219
219
  # database error will occur because the savepoint has already been
220
220
  # automatically released. The following example demonstrates the problem:
221
221
  #
222
- # Model.lease_connection.transaction do # BEGIN
223
- # Model.lease_connection.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
- # # ^^^^ BOOM! database error!
227
- # 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!
228
227
  #
229
228
  # Note that "TRUNCATE" is also a MySQL DDL statement!
230
229
  module ClassMethods
231
230
  # See the ConnectionAdapters::DatabaseStatements#transaction API docs.
232
231
  def transaction(**options, &block)
233
232
  with_connection do |connection|
234
- connection.transaction(**options, &block)
233
+ connection.pool.with_pool_transaction_isolation_level(ActiveRecord.default_transaction_isolation_level, connection.transaction_open?) do
234
+ connection.transaction(**options, &block)
235
+ end
235
236
  end
236
237
  end
237
238
 
239
+ # Makes all transactions the current pool use the isolation level initiated within the block.
240
+ def with_pool_transaction_isolation_level(isolation_level, &block)
241
+ if current_transaction.open?
242
+ raise ActiveRecord::TransactionIsolationError, "cannot set default isolation level while transaction is open"
243
+ end
244
+
245
+ old_level = connection_pool.pool_transaction_isolation_level
246
+ connection_pool.pool_transaction_isolation_level = isolation_level
247
+ yield
248
+ ensure
249
+ connection_pool.pool_transaction_isolation_level = old_level
250
+ end
251
+
252
+ # Returns the default isolation level for the connection pool, set earlier by #with_pool_transaction_isolation_level.
253
+ def pool_transaction_isolation_level
254
+ connection_pool.pool_transaction_isolation_level
255
+ end
256
+
238
257
  # Returns a representation of the current transaction state,
239
258
  # which can be a top level transaction, a savepoint, or the absence of a transaction.
240
259
  #
@@ -247,7 +266,7 @@ module ActiveRecord
247
266
  end
248
267
 
249
268
  def before_commit(*args, &block) # :nodoc:
250
- set_options_for_callbacks!(args)
269
+ set_options_for_callbacks!(:before, args)
251
270
  set_callback(:before_commit, :before, *args, &block)
252
271
  end
253
272
 
@@ -264,31 +283,31 @@ module ActiveRecord
264
283
  # after_commit :do_bar_baz, on: [:update, :destroy]
265
284
  #
266
285
  def after_commit(*args, &block)
267
- set_options_for_callbacks!(args, prepend_option)
286
+ set_options_for_callbacks!(:after, args)
268
287
  set_callback(:commit, :after, *args, &block)
269
288
  end
270
289
 
271
290
  # Shortcut for <tt>after_commit :hook, on: [ :create, :update ]</tt>.
272
291
  def after_save_commit(*args, &block)
273
- set_options_for_callbacks!(args, on: [ :create, :update ], **prepend_option)
292
+ set_options_for_callbacks!(:after, args, on: [ :create, :update ])
274
293
  set_callback(:commit, :after, *args, &block)
275
294
  end
276
295
 
277
296
  # Shortcut for <tt>after_commit :hook, on: :create</tt>.
278
297
  def after_create_commit(*args, &block)
279
- set_options_for_callbacks!(args, on: :create, **prepend_option)
298
+ set_options_for_callbacks!(:after, args, on: :create)
280
299
  set_callback(:commit, :after, *args, &block)
281
300
  end
282
301
 
283
302
  # Shortcut for <tt>after_commit :hook, on: :update</tt>.
284
303
  def after_update_commit(*args, &block)
285
- set_options_for_callbacks!(args, on: :update, **prepend_option)
304
+ set_options_for_callbacks!(:after, args, on: :update)
286
305
  set_callback(:commit, :after, *args, &block)
287
306
  end
288
307
 
289
308
  # Shortcut for <tt>after_commit :hook, on: :destroy</tt>.
290
309
  def after_destroy_commit(*args, &block)
291
- set_options_for_callbacks!(args, on: :destroy, **prepend_option)
310
+ set_options_for_callbacks!(:after, args, on: :destroy)
292
311
  set_callback(:commit, :after, *args, &block)
293
312
  end
294
313
 
@@ -296,7 +315,7 @@ module ActiveRecord
296
315
  #
297
316
  # Please check the documentation of #after_commit for options.
298
317
  def after_rollback(*args, &block)
299
- set_options_for_callbacks!(args, prepend_option)
318
+ set_options_for_callbacks!(:after, args)
300
319
  set_callback(:rollback, :after, *args, &block)
301
320
  end
302
321
 
@@ -320,16 +339,11 @@ module ActiveRecord
320
339
  end
321
340
 
322
341
  private
323
- def prepend_option
324
- if ActiveRecord.run_after_transaction_callbacks_in_order_defined
325
- { prepend: true }
326
- else
327
- {}
328
- end
329
- end
342
+ def set_options_for_callbacks!(position, args, enforced_options = {})
343
+ options = args.extract_options!
344
+ enforced_options.merge!(enforced_prepend_option(position, options))
345
+ options.merge!(enforced_options)
330
346
 
331
- def set_options_for_callbacks!(args, enforced_options = {})
332
- options = args.extract_options!.merge!(enforced_options)
333
347
  args << options
334
348
 
335
349
  if options[:on]
@@ -342,6 +356,14 @@ module ActiveRecord
342
356
  end
343
357
  end
344
358
 
359
+ def enforced_prepend_option(position, options)
360
+ if position == :after && ActiveRecord.run_after_transaction_callbacks_in_order_defined
361
+ { prepend: !options[:prepend] }
362
+ else
363
+ {}
364
+ end
365
+ end
366
+
345
367
  def assert_valid_transaction_action(actions)
346
368
  if (actions - ACTIONS).any?
347
369
  raise ArgumentError, ":on conditions for after_commit and after_rollback callbacks have to be one of #{ACTIONS}"
@@ -408,17 +430,20 @@ module ActiveRecord
408
430
  # instance.
409
431
  def with_transaction_returning_status
410
432
  self.class.with_connection do |connection|
411
- status = nil
412
- ensure_finalize = !connection.transaction_open?
433
+ connection.pool.with_pool_transaction_isolation_level(ActiveRecord.default_transaction_isolation_level, connection.transaction_open?) do
434
+ status = nil
435
+ ensure_finalize = !connection.transaction_open?
413
436
 
414
- connection.transaction do
415
- add_to_transaction(ensure_finalize || has_transactional_callbacks?)
416
- remember_transaction_record_state
437
+ connection.transaction do
438
+ add_to_transaction(ensure_finalize || has_transactional_callbacks?)
439
+ remember_transaction_record_state
417
440
 
418
- status = yield
419
- raise ActiveRecord::Rollback unless status
441
+ status = yield
442
+ raise ActiveRecord::Rollback unless status
443
+ end
444
+ @_last_transaction_return_status = status
445
+ status
420
446
  end
421
- status
422
447
  end
423
448
  end
424
449
 
@@ -433,6 +458,7 @@ module ActiveRecord
433
458
  def init_internals
434
459
  super
435
460
  @_start_transaction_state = nil
461
+ @_last_transaction_return_status = nil
436
462
  @_committed_already_called = nil
437
463
  @_new_record_before_last_commit = nil
438
464
  end
@@ -26,6 +26,7 @@ module ActiveRecord
26
26
  if block
27
27
  @mapping[key] = block
28
28
  else
29
+ value.freeze
29
30
  @mapping[key] = proc { value }
30
31
  end
31
32
  @cache.clear
@@ -50,7 +51,7 @@ module ActiveRecord
50
51
 
51
52
  private
52
53
  def perform_fetch(type, *args, &block)
53
- @mapping.fetch(type, block).call(type, *args)
54
+ @mapping.fetch(type, block).call(type, *args).freeze
54
55
  end
55
56
  end
56
57
  end
@@ -16,6 +16,13 @@ module ActiveRecord
16
16
  def default_timezone
17
17
  @timezone || ActiveRecord.default_timezone
18
18
  end
19
+
20
+ def ==(other)
21
+ super(other) && timezone == other.timezone
22
+ end
23
+
24
+ protected
25
+ attr_reader :timezone
19
26
  end
20
27
  end
21
28
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/json"
4
+
3
5
  module ActiveRecord
4
6
  module Type
5
7
  class Json < ActiveModel::Type::Value
@@ -11,11 +13,20 @@ module ActiveRecord
11
13
 
12
14
  def deserialize(value)
13
15
  return value unless value.is_a?(::String)
14
- ActiveSupport::JSON.decode(value) rescue nil
16
+ begin
17
+ ActiveSupport::JSON.decode(value)
18
+ rescue JSON::ParserError => e
19
+ # NOTE: This may hide json with duplicate keys. We don't really want to just ignore it
20
+ # but it's the best we can do in order to still allow updating columns that somehow already
21
+ # contain invalid json from some other source.
22
+ # See https://github.com/rails/rails/pull/55536
23
+ ActiveSupport.error_reporter.report(e, source: "application.active_record")
24
+ nil
25
+ end
15
26
  end
16
27
 
17
28
  def serialize(value)
18
- ActiveSupport::JSON.encode(value) unless value.nil?
29
+ ActiveSupport::JSON::Encoding.encode_without_escape(value) unless value.nil?
19
30
  end
20
31
 
21
32
  def changed_in_place?(raw_old_value, new_value)
@@ -9,9 +9,10 @@ module ActiveRecord
9
9
 
10
10
  attr_reader :subtype, :coder
11
11
 
12
- def initialize(subtype, coder)
12
+ def initialize(subtype, coder, comparable: false)
13
13
  @subtype = subtype
14
14
  @coder = coder
15
+ @comparable = comparable
15
16
  super(subtype)
16
17
  end
17
18
 
@@ -34,9 +35,15 @@ module ActiveRecord
34
35
 
35
36
  def changed_in_place?(raw_old_value, value)
36
37
  return false if value.nil?
37
- raw_new_value = encoded(value)
38
- raw_old_value.nil? != raw_new_value.nil? ||
39
- subtype.changed_in_place?(raw_old_value, raw_new_value)
38
+
39
+ if @comparable
40
+ old_value = deserialize(raw_old_value)
41
+ old_value != value
42
+ else
43
+ raw_new_value = encoded(value)
44
+ raw_old_value.nil? != raw_new_value.nil? ||
45
+ subtype.changed_in_place?(raw_old_value, raw_new_value)
46
+ end
40
47
  end
41
48
 
42
49
  def accessor
@@ -58,6 +65,11 @@ module ActiveRecord
58
65
  end
59
66
 
60
67
  private
68
+ # Prevent Ruby 4.0 "delegator does not forward private method" warning.
69
+ # Kernel#inspect calls instance_variables_to_inspect which, without this,
70
+ # triggers Delegator#respond_to_missing? for a private method.
71
+ define_method(:instance_variables_to_inspect, Kernel.instance_method(:instance_variables))
72
+
61
73
  def default_value?(value)
62
74
  value == coder.load(nil)
63
75
  end
@@ -46,7 +46,7 @@ module ActiveRecord
46
46
  end
47
47
 
48
48
  if matching_pair
49
- matching_pair.last.call(lookup_key)
49
+ matching_pair.last.call(lookup_key).freeze
50
50
  elsif @parent
51
51
  @parent.perform_fetch(lookup_key, &block)
52
52
  else
@@ -19,7 +19,8 @@ module ActiveRecord
19
19
  if schema_cache.data_source_exists?(table_name)
20
20
  column = schema_cache.columns_hash(table_name)[attr_name.to_s]
21
21
  if column
22
- type = @klass.with_connection { |connection| connection.lookup_cast_type_from_column(column) }
22
+ # TODO: Remove fetch_cast_type and the need for connection after we release 8.1.
23
+ type = @klass.with_connection { |c| column.fetch_cast_type(c) }
23
24
  end
24
25
  end
25
26
 
@@ -7,7 +7,7 @@ module ActiveRecord
7
7
  context = record_validation_context_for_association(record)
8
8
 
9
9
  if Array(value).reject { |association| valid_object?(association, context) }.any?
10
- record.errors.add(attribute, :invalid, **options.merge(value: value))
10
+ record.errors.add(attribute, :invalid, **options, value: value)
11
11
  end
12
12
  end
13
13
 
data/lib/active_record.rb CHANGED
@@ -26,6 +26,7 @@
26
26
  require "active_support"
27
27
  require "active_support/rails"
28
28
  require "active_support/ordered_options"
29
+ require "active_support/core_ext/array/conversions"
29
30
  require "active_model"
30
31
  require "arel"
31
32
  require "yaml"
@@ -52,6 +53,7 @@ module ActiveRecord
52
53
  autoload :Enum
53
54
  autoload :Explain
54
55
  autoload :FixtureSet, "active_record/fixtures"
56
+ autoload :FilterAttributeHandler
55
57
  autoload :Inheritance
56
58
  autoload :Integration
57
59
  autoload :InternalMetadata
@@ -62,7 +64,6 @@ module ActiveRecord
62
64
  autoload :ModelSchema
63
65
  autoload :NestedAttributes
64
66
  autoload :NoTouching
65
- autoload :Normalization
66
67
  autoload :Persistence
67
68
  autoload :QueryCache
68
69
  autoload :QueryLogs
@@ -87,7 +88,6 @@ module ActiveRecord
87
88
  autoload :Timestamp
88
89
  autoload :TokenFor
89
90
  autoload :TouchLater
90
- autoload :Transaction
91
91
  autoload :Transactions
92
92
  autoload :Translation
93
93
  autoload :Validations
@@ -109,6 +109,7 @@ module ActiveRecord
109
109
  autoload :Result
110
110
  autoload :StatementCache
111
111
  autoload :TableMetadata
112
+ autoload :Transaction
112
113
  autoload :Type
113
114
 
114
115
  autoload_under "relation" do
@@ -122,7 +123,7 @@ module ActiveRecord
122
123
  end
123
124
  end
124
125
 
125
- module Coders
126
+ module Coders # :nodoc:
126
127
  autoload :ColumnSerializer, "active_record/coders/column_serializer"
127
128
  autoload :JSON, "active_record/coders/json"
128
129
  autoload :YAMLColumn, "active_record/coders/yaml_column"
@@ -174,7 +175,8 @@ module ActiveRecord
174
175
  extend ActiveSupport::Autoload
175
176
 
176
177
  autoload :DatabaseTasks
177
- autoload :MySQLDatabaseTasks, "active_record/tasks/mysql_database_tasks"
178
+ autoload :AbstractTasks, "active_record/tasks/abstract_tasks"
179
+ autoload :MySQLDatabaseTasks, "active_record/tasks/mysql_database_tasks"
178
180
  autoload :PostgreSQLDatabaseTasks, "active_record/tasks/postgresql_database_tasks"
179
181
  autoload :SQLiteDatabaseTasks, "active_record/tasks/sqlite_database_tasks"
180
182
  end
@@ -259,6 +261,9 @@ module ActiveRecord
259
261
  ##
260
262
  # :singleton-method: db_warnings_ignore
261
263
  # Specify allowlist of database warnings.
264
+ # Can be a string, regular expression, or an error code from the database.
265
+ #
266
+ # ActiveRecord::Base.db_warnings_ignore = [/`SHOW WARNINGS` did not return the warnings/, "01000"]
262
267
  singleton_class.attr_accessor :db_warnings_ignore
263
268
  self.db_warnings_ignore = []
264
269
 
@@ -286,6 +291,7 @@ module ActiveRecord
286
291
  def self.global_thread_pool_async_query_executor # :nodoc:
287
292
  concurrency = global_executor_concurrency || 4
288
293
  @global_thread_pool_async_query_executor ||= Concurrent::ThreadPoolExecutor.new(
294
+ name: "ActiveRecord-global-async-query-executor",
289
295
  min_threads: 0,
290
296
  max_threads: concurrency,
291
297
  max_queue: concurrency * 4,
@@ -351,6 +357,9 @@ module ActiveRecord
351
357
  singleton_class.attr_accessor :run_after_transaction_callbacks_in_order_defined
352
358
  self.run_after_transaction_callbacks_in_order_defined = false
353
359
 
360
+ singleton_class.attr_accessor :raise_on_missing_required_finder_order_columns
361
+ self.raise_on_missing_required_finder_order_columns = false
362
+
354
363
  singleton_class.attr_accessor :application_record_class
355
364
  self.application_record_class = nil
356
365
 
@@ -368,7 +377,8 @@ module ActiveRecord
368
377
  # specific) SQL statements. If :ruby, the schema is dumped as an
369
378
  # ActiveRecord::Schema file which can be loaded into any database that
370
379
  # supports migrations. Use :ruby if you want to have different database
371
- # adapters for, e.g., your development and test environments.
380
+ # adapters for, e.g., your development and test environments. This can be
381
+ # overridden per-database in the database configuration.
372
382
  singleton_class.attr_accessor :schema_format
373
383
  self.schema_format = :ruby
374
384
 
@@ -400,6 +410,12 @@ module ActiveRecord
400
410
  singleton_class.attr_accessor :migration_strategy
401
411
  self.migration_strategy = Migration::DefaultStrategy
402
412
 
413
+ ##
414
+ # :singleton-method: schema_versions_formatter
415
+ # Specify the formatter used by schema dumper to format versions information.
416
+ singleton_class.attr_accessor :schema_versions_formatter
417
+ self.schema_versions_formatter = Migration::DefaultSchemaVersionsFormatter
418
+
403
419
  ##
404
420
  # :singleton-method: dump_schema_after_migration
405
421
  # Specify whether schema dump should happen at the end of the
@@ -460,6 +476,29 @@ module ActiveRecord
460
476
  singleton_class.attr_accessor :generate_secure_token_on
461
477
  self.generate_secure_token_on = :create
462
478
 
479
+ def self.deprecated_associations_options=(options)
480
+ raise ArgumentError, "deprecated_associations_options must be a hash" unless options.is_a?(Hash)
481
+
482
+ valid_keys = [:mode, :backtrace]
483
+
484
+ invalid_keys = options.keys - valid_keys
485
+ unless invalid_keys.empty?
486
+ inflected_key = invalid_keys.size == 1 ? "key" : "keys"
487
+ raise ArgumentError, "invalid deprecated_associations_options #{inflected_key} #{invalid_keys.map(&:inspect).to_sentence} (valid keys are #{valid_keys.map(&:inspect).to_sentence})"
488
+ end
489
+
490
+ options.each do |key, value|
491
+ ActiveRecord::Associations::Deprecation.send("#{key}=", value)
492
+ end
493
+ end
494
+
495
+ def self.deprecated_associations_options
496
+ {
497
+ mode: ActiveRecord::Associations::Deprecation.mode,
498
+ backtrace: ActiveRecord::Associations::Deprecation.backtrace
499
+ }
500
+ end
501
+
463
502
  def self.marshalling_format_version
464
503
  Marshalling.format_version
465
504
  end
@@ -496,6 +535,13 @@ module ActiveRecord
496
535
  }
497
536
  )
498
537
 
538
+ ##
539
+ # :singleton-method: message_verifiers
540
+ #
541
+ # ActiveSupport::MessageVerifiers instance for Active Record. If you are using
542
+ # Rails, this will be set to +Rails.application.message_verifiers+.
543
+ singleton_class.attr_accessor :message_verifiers
544
+
499
545
  def self.eager_load!
500
546
  super
501
547
  ActiveRecord::Locking.eager_load!
@@ -548,13 +594,32 @@ module ActiveRecord
548
594
  open_transactions = []
549
595
  Base.connection_handler.each_connection_pool do |pool|
550
596
  if active_connection = pool.active_connection
551
- if active_connection.current_transaction.open? && active_connection.current_transaction.joinable?
552
- open_transactions << active_connection.current_transaction
597
+ current_transaction = active_connection.current_transaction
598
+
599
+ if current_transaction.open? && current_transaction.joinable?
600
+ open_transactions << current_transaction
553
601
  end
554
602
  end
555
603
  end
556
604
  open_transactions
557
605
  end
606
+
607
+ def self.default_transaction_isolation_level=(isolation_level) # :nodoc:
608
+ ActiveSupport::IsolatedExecutionState[:active_record_transaction_isolation] = isolation_level
609
+ end
610
+
611
+ def self.default_transaction_isolation_level # :nodoc:
612
+ ActiveSupport::IsolatedExecutionState[:active_record_transaction_isolation]
613
+ end
614
+
615
+ # Sets a transaction isolation level for all connection pools within the block.
616
+ def self.with_transaction_isolation_level(isolation_level, &block)
617
+ original_level = self.default_transaction_isolation_level
618
+ self.default_transaction_isolation_level = isolation_level
619
+ yield
620
+ ensure
621
+ self.default_transaction_isolation_level = original_level
622
+ end
558
623
  end
559
624
 
560
625
  ActiveSupport.on_load(:active_record) do
@@ -3,6 +3,8 @@
3
3
  module Arel # :nodoc: all
4
4
  module AliasPredication
5
5
  def as(other)
6
+ other = other.name if other.is_a?(Symbol)
7
+
6
8
  Nodes::As.new self, Nodes::SqlLiteral.new(other, retryable: true)
7
9
  end
8
10
  end
@@ -18,7 +18,7 @@ module Arel # :nodoc: all
18
18
  self
19
19
  end
20
20
 
21
- def add_binds(binds, proc_for_binds = nil)
21
+ def add_binds(binds, proc_for_binds = nil, &)
22
22
  @binds.concat proc_for_binds ? binds.map(&proc_for_binds) : binds
23
23
  self
24
24
  end
@@ -12,7 +12,7 @@ module Arel # :nodoc: all
12
12
  @bind_index = 1
13
13
  end
14
14
 
15
- def add_bind(bind)
15
+ def add_bind(bind, &)
16
16
  self << yield(@bind_index)
17
17
  @bind_index += 1
18
18
  self
@@ -15,12 +15,12 @@ module Arel # :nodoc: all
15
15
  self
16
16
  end
17
17
 
18
- def add_bind(bind)
18
+ def add_bind(bind, &)
19
19
  bind = bind.value_for_database if bind.respond_to?(:value_for_database)
20
20
  self << quoter.quote(bind)
21
21
  end
22
22
 
23
- def add_binds(binds, proc_for_binds = nil)
23
+ def add_binds(binds, proc_for_binds = nil, &)
24
24
  self << binds.map { |bind| quoter.quote(bind) }.join(", ")
25
25
  end
26
26
 
data/lib/arel/crud.rb CHANGED
@@ -14,34 +14,31 @@ module Arel # :nodoc: all
14
14
  InsertManager.new
15
15
  end
16
16
 
17
- def compile_update(
18
- values,
19
- key = nil,
20
- having_clause = nil,
21
- group_values_columns = []
22
- )
17
+ def compile_update(values, key = nil)
23
18
  um = UpdateManager.new(source)
24
19
  um.set(values)
25
20
  um.take(limit)
26
21
  um.offset(offset)
27
22
  um.order(*orders)
28
23
  um.wheres = constraints
24
+ um.comment(comment)
29
25
  um.key = key
30
26
 
31
- um.group(group_values_columns) unless group_values_columns.empty?
32
- um.having(having_clause) unless having_clause.nil?
27
+ um.ast.groups = @ctx.groups
28
+ @ctx.havings.each { |h| um.having(h) }
33
29
  um
34
30
  end
35
31
 
36
- def compile_delete(key = nil, having_clause = nil, group_values_columns = [])
32
+ def compile_delete(key = nil)
37
33
  dm = DeleteManager.new(source)
38
34
  dm.take(limit)
39
35
  dm.offset(offset)
40
36
  dm.order(*orders)
41
37
  dm.wheres = constraints
38
+ dm.comment(comment)
42
39
  dm.key = key
43
- dm.group(group_values_columns) unless group_values_columns.empty?
44
- dm.having(having_clause) unless having_clause.nil?
40
+ dm.ast.groups = @ctx.groups
41
+ @ctx.havings.each { |h| dm.having(h) }
45
42
  dm
46
43
  end
47
44
  end
@@ -28,5 +28,10 @@ module Arel # :nodoc: all
28
28
  @ast.havings << expr
29
29
  self
30
30
  end
31
+
32
+ def comment(value)
33
+ @ast.comment = value
34
+ self
35
+ end
31
36
  end
32
37
  end
@@ -30,7 +30,7 @@ module Arel # :nodoc: all
30
30
  end
31
31
 
32
32
  module FetchAttribute
33
- def fetch_attribute
33
+ def fetch_attribute(&)
34
34
  if left.is_a?(Arel::Attributes::Attribute)
35
35
  yield left
36
36
  elsif right.is_a?(Arel::Attributes::Attribute)
@@ -3,8 +3,8 @@
3
3
  module Arel # :nodoc: all
4
4
  module Nodes
5
5
  class Count < Arel::Nodes::Function
6
- def initialize(expr, distinct = false, aliaz = nil)
7
- super(expr, aliaz)
6
+ def initialize(expr, distinct = false)
7
+ super(expr)
8
8
  @distinct = distinct
9
9
  end
10
10
  end
@@ -3,7 +3,7 @@
3
3
  module Arel # :nodoc: all
4
4
  module Nodes
5
5
  class DeleteStatement < Arel::Nodes::Node
6
- attr_accessor :relation, :wheres, :groups, :havings, :orders, :limit, :offset, :key
6
+ attr_accessor :relation, :wheres, :groups, :havings, :orders, :limit, :offset, :comment, :key
7
7
 
8
8
  def initialize(relation = nil, wheres = [])
9
9
  super()
@@ -14,6 +14,7 @@ module Arel # :nodoc: all
14
14
  @orders = []
15
15
  @limit = nil
16
16
  @offset = nil
17
+ @comment = nil
17
18
  @key = nil
18
19
  end
19
20
 
@@ -24,7 +25,7 @@ module Arel # :nodoc: all
24
25
  end
25
26
 
26
27
  def hash
27
- [self.class, @relation, @wheres, @orders, @limit, @offset, @key].hash
28
+ [self.class, @relation, @wheres, @orders, @limit, @offset, @comment, @key].hash
28
29
  end
29
30
 
30
31
  def eql?(other)
@@ -36,6 +37,7 @@ module Arel # :nodoc: all
36
37
  self.havings == other.havings &&
37
38
  self.limit == other.limit &&
38
39
  self.offset == other.offset &&
40
+ self.comment == other.comment &&
39
41
  self.key == other.key
40
42
  end
41
43
  alias :== :eql?