activerecord 7.1.5.2 → 8.1.2.1

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 (244) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +567 -2443
  3. data/README.rdoc +16 -16
  4. data/examples/performance.rb +2 -2
  5. data/lib/active_record/association_relation.rb +3 -2
  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 +31 -8
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  10. data/lib/active_record/associations/builder/association.rb +23 -11
  11. data/lib/active_record/associations/builder/belongs_to.rb +18 -4
  12. data/lib/active_record/associations/builder/collection_association.rb +7 -3
  13. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
  14. data/lib/active_record/associations/builder/has_many.rb +3 -4
  15. data/lib/active_record/associations/builder/has_one.rb +3 -4
  16. data/lib/active_record/associations/builder/singular_association.rb +33 -5
  17. data/lib/active_record/associations/collection_association.rb +17 -9
  18. data/lib/active_record/associations/collection_proxy.rb +36 -5
  19. data/lib/active_record/associations/deprecation.rb +88 -0
  20. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  21. data/lib/active_record/associations/errors.rb +268 -0
  22. data/lib/active_record/associations/has_many_association.rb +1 -1
  23. data/lib/active_record/associations/has_many_through_association.rb +10 -3
  24. data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
  25. data/lib/active_record/associations/join_dependency.rb +4 -2
  26. data/lib/active_record/associations/nested_error.rb +47 -0
  27. data/lib/active_record/associations/preloader/association.rb +4 -3
  28. data/lib/active_record/associations/preloader/batch.rb +7 -1
  29. data/lib/active_record/associations/preloader/branch.rb +8 -1
  30. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  31. data/lib/active_record/associations/singular_association.rb +14 -3
  32. data/lib/active_record/associations/through_association.rb +1 -1
  33. data/lib/active_record/associations.rb +250 -315
  34. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  35. data/lib/active_record/attribute_assignment.rb +0 -2
  36. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  37. data/lib/active_record/attribute_methods/primary_key.rb +25 -61
  38. data/lib/active_record/attribute_methods/query.rb +34 -0
  39. data/lib/active_record/attribute_methods/read.rb +1 -13
  40. data/lib/active_record/attribute_methods/serialization.rb +20 -27
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +19 -20
  42. data/lib/active_record/attribute_methods.rb +71 -75
  43. data/lib/active_record/attributes.rb +96 -68
  44. data/lib/active_record/autosave_association.rb +93 -58
  45. data/lib/active_record/base.rb +5 -7
  46. data/lib/active_record/callbacks.rb +1 -1
  47. data/lib/active_record/coders/json.rb +14 -5
  48. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +50 -126
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
  50. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +52 -13
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +681 -135
  52. data/lib/active_record/connection_adapters/abstract/database_statements.rb +165 -86
  53. data/lib/active_record/connection_adapters/abstract/query_cache.rb +226 -78
  54. data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -114
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +37 -36
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +136 -34
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +163 -68
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +197 -129
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +112 -63
  62. data/lib/active_record/connection_adapters/column.rb +17 -4
  63. data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -3
  64. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
  65. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
  66. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
  67. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +79 -46
  68. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +97 -101
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +14 -33
  70. data/lib/active_record/connection_adapters/pool_config.rb +14 -13
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +98 -44
  73. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
  74. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  75. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
  77. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  78. data/lib/active_record/connection_adapters/postgresql/quoting.rb +79 -68
  79. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  80. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
  81. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +44 -41
  82. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +69 -32
  83. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +155 -75
  84. data/lib/active_record/connection_adapters/postgresql_adapter.rb +104 -121
  85. data/lib/active_record/connection_adapters/schema_cache.rb +126 -133
  86. data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
  87. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +91 -95
  88. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -54
  89. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
  90. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
  91. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +43 -2
  92. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -15
  93. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +231 -111
  94. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  95. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +40 -69
  96. data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
  97. data/lib/active_record/connection_adapters.rb +66 -0
  98. data/lib/active_record/connection_handling.rb +88 -46
  99. data/lib/active_record/core.rb +142 -58
  100. data/lib/active_record/counter_cache.rb +52 -18
  101. data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
  102. data/lib/active_record/database_configurations/database_config.rb +28 -5
  103. data/lib/active_record/database_configurations/hash_config.rb +103 -41
  104. data/lib/active_record/database_configurations/url_config.rb +30 -1
  105. data/lib/active_record/database_configurations.rb +8 -4
  106. data/lib/active_record/delegated_type.rb +43 -19
  107. data/lib/active_record/dynamic_matchers.rb +56 -71
  108. data/lib/active_record/encryption/config.rb +3 -1
  109. data/lib/active_record/encryption/encryptable_record.rb +12 -12
  110. data/lib/active_record/encryption/encrypted_attribute_type.rb +35 -6
  111. data/lib/active_record/encryption/encryptor.rb +58 -22
  112. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  113. data/lib/active_record/encryption/key_provider.rb +1 -1
  114. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  115. data/lib/active_record/encryption/message_serializer.rb +4 -0
  116. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  117. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  118. data/lib/active_record/encryption/scheme.rb +9 -2
  119. data/lib/active_record/enum.rb +57 -36
  120. data/lib/active_record/errors.rb +76 -26
  121. data/lib/active_record/explain.rb +14 -25
  122. data/lib/active_record/explain_registry.rb +51 -2
  123. data/lib/active_record/filter_attribute_handler.rb +73 -0
  124. data/lib/active_record/fixture_set/table_row.rb +19 -2
  125. data/lib/active_record/fixtures.rb +39 -35
  126. data/lib/active_record/future_result.rb +21 -13
  127. data/lib/active_record/gem_version.rb +3 -3
  128. data/lib/active_record/inheritance.rb +5 -3
  129. data/lib/active_record/insert_all.rb +28 -20
  130. data/lib/active_record/integration.rb +4 -1
  131. data/lib/active_record/internal_metadata.rb +48 -34
  132. data/lib/active_record/locking/optimistic.rb +15 -7
  133. data/lib/active_record/locking/pessimistic.rb +5 -0
  134. data/lib/active_record/log_subscriber.rb +3 -34
  135. data/lib/active_record/message_pack.rb +1 -1
  136. data/lib/active_record/middleware/shard_selector.rb +34 -17
  137. data/lib/active_record/migration/command_recorder.rb +46 -14
  138. data/lib/active_record/migration/compatibility.rb +40 -25
  139. data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
  140. data/lib/active_record/migration/default_strategy.rb +4 -5
  141. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  142. data/lib/active_record/migration.rb +119 -103
  143. data/lib/active_record/model_schema.rb +66 -78
  144. data/lib/active_record/nested_attributes.rb +17 -9
  145. data/lib/active_record/persistence.rb +161 -454
  146. data/lib/active_record/query_cache.rb +33 -15
  147. data/lib/active_record/query_logs.rb +106 -39
  148. data/lib/active_record/query_logs_formatter.rb +17 -28
  149. data/lib/active_record/querying.rb +28 -16
  150. data/lib/active_record/railtie.rb +61 -74
  151. data/lib/active_record/railties/controller_runtime.rb +19 -5
  152. data/lib/active_record/railties/databases.rake +64 -78
  153. data/lib/active_record/railties/job_checkpoints.rb +15 -0
  154. data/lib/active_record/railties/job_runtime.rb +10 -11
  155. data/lib/active_record/reflection.rb +147 -53
  156. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  157. data/lib/active_record/relation/batches.rb +153 -73
  158. data/lib/active_record/relation/calculations.rb +146 -96
  159. data/lib/active_record/relation/delegation.rb +30 -23
  160. data/lib/active_record/relation/finder_methods.rb +64 -34
  161. data/lib/active_record/relation/merger.rb +12 -14
  162. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  163. data/lib/active_record/relation/predicate_builder/association_query_value.rb +19 -11
  164. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
  165. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  166. data/lib/active_record/relation/predicate_builder.rb +25 -10
  167. data/lib/active_record/relation/query_attribute.rb +4 -2
  168. data/lib/active_record/relation/query_methods.rb +351 -124
  169. data/lib/active_record/relation/spawn_methods.rb +9 -25
  170. data/lib/active_record/relation/where_clause.rb +17 -30
  171. data/lib/active_record/relation.rb +590 -124
  172. data/lib/active_record/result.rb +131 -59
  173. data/lib/active_record/runtime_registry.rb +44 -21
  174. data/lib/active_record/sanitization.rb +33 -25
  175. data/lib/active_record/schema.rb +8 -6
  176. data/lib/active_record/schema_dumper.rb +66 -31
  177. data/lib/active_record/schema_migration.rb +31 -14
  178. data/lib/active_record/scoping/named.rb +6 -2
  179. data/lib/active_record/scoping.rb +0 -1
  180. data/lib/active_record/secure_token.rb +3 -3
  181. data/lib/active_record/signed_id.rb +68 -20
  182. data/lib/active_record/statement_cache.rb +29 -25
  183. data/lib/active_record/store.rb +51 -22
  184. data/lib/active_record/structured_event_subscriber.rb +85 -0
  185. data/lib/active_record/table_metadata.rb +7 -33
  186. data/lib/active_record/tasks/abstract_tasks.rb +76 -0
  187. data/lib/active_record/tasks/database_tasks.rb +118 -90
  188. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
  189. data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
  190. data/lib/active_record/tasks/sqlite_database_tasks.rb +17 -28
  191. data/lib/active_record/test_databases.rb +14 -4
  192. data/lib/active_record/test_fixtures.rb +123 -89
  193. data/lib/active_record/testing/query_assertions.rb +127 -0
  194. data/lib/active_record/timestamp.rb +6 -4
  195. data/lib/active_record/token_for.rb +22 -12
  196. data/lib/active_record/touch_later.rb +1 -1
  197. data/lib/active_record/transaction.rb +129 -0
  198. data/lib/active_record/transactions.rb +97 -18
  199. data/lib/active_record/translation.rb +0 -2
  200. data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
  201. data/lib/active_record/type/internal/timezone.rb +7 -0
  202. data/lib/active_record/type/json.rb +15 -2
  203. data/lib/active_record/type/serialized.rb +12 -7
  204. data/lib/active_record/type/type_map.rb +1 -1
  205. data/lib/active_record/type_caster/connection.rb +5 -4
  206. data/lib/active_record/validations/associated.rb +10 -4
  207. data/lib/active_record/validations/uniqueness.rb +23 -18
  208. data/lib/active_record/validations.rb +4 -1
  209. data/lib/active_record.rb +203 -59
  210. data/lib/arel/alias_predication.rb +3 -1
  211. data/lib/arel/collectors/bind.rb +4 -2
  212. data/lib/arel/collectors/composite.rb +7 -0
  213. data/lib/arel/collectors/sql_string.rb +2 -2
  214. data/lib/arel/collectors/substitute_binds.rb +3 -3
  215. data/lib/arel/crud.rb +8 -11
  216. data/lib/arel/delete_manager.rb +5 -0
  217. data/lib/arel/nodes/binary.rb +1 -7
  218. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  219. data/lib/arel/nodes/count.rb +2 -2
  220. data/lib/arel/nodes/delete_statement.rb +4 -2
  221. data/lib/arel/nodes/function.rb +4 -10
  222. data/lib/arel/nodes/named_function.rb +2 -2
  223. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  224. data/lib/arel/nodes/node.rb +6 -5
  225. data/lib/arel/nodes/sql_literal.rb +8 -1
  226. data/lib/arel/nodes/update_statement.rb +4 -2
  227. data/lib/arel/nodes.rb +2 -4
  228. data/lib/arel/predications.rb +1 -1
  229. data/lib/arel/select_manager.rb +14 -5
  230. data/lib/arel/table.rb +3 -7
  231. data/lib/arel/tree_manager.rb +3 -2
  232. data/lib/arel/update_manager.rb +7 -1
  233. data/lib/arel/visitors/dot.rb +3 -3
  234. data/lib/arel/visitors/mysql.rb +9 -4
  235. data/lib/arel/visitors/postgresql.rb +56 -12
  236. data/lib/arel/visitors/sqlite.rb +80 -8
  237. data/lib/arel/visitors/to_sql.rb +34 -37
  238. data/lib/arel.rb +10 -4
  239. data/lib/rails/generators/active_record/application_record/USAGE +1 -1
  240. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  241. metadata +23 -14
  242. data/lib/active_record/explain_subscriber.rb +0 -34
  243. data/lib/active_record/normalization.rb +0 -167
  244. data/lib/active_record/relation/record_fetch_warning.rb +0 -49
@@ -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
  #
@@ -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,50 @@ 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.pool.with_pool_transaction_isolation_level(ActiveRecord.default_transaction_isolation_level, connection.transaction_open?) do
234
+ connection.transaction(**options, &block)
235
+ end
236
+ end
237
+ end
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
+
257
+ # Returns a representation of the current transaction state,
258
+ # which can be a top level transaction, a savepoint, or the absence of a transaction.
259
+ #
260
+ # An object is always returned, whether or not a transaction is currently active.
261
+ # To check if a transaction was opened, use <tt>current_transaction.open?</tt>.
262
+ #
263
+ # See the ActiveRecord::Transaction documentation for detailed behavior.
264
+ def current_transaction
265
+ connection_pool.active_connection&.current_transaction&.user_transaction || Transaction::NULL_TRANSACTION
213
266
  end
214
267
 
215
268
  def before_commit(*args, &block) # :nodoc:
@@ -266,6 +319,25 @@ module ActiveRecord
266
319
  set_callback(:rollback, :after, *args, &block)
267
320
  end
268
321
 
322
+ # Similar to ActiveSupport::Callbacks::ClassMethods#set_callback, but with
323
+ # support for options available on #after_commit and #after_rollback callbacks.
324
+ def set_callback(name, *filter_list, &block)
325
+ options = filter_list.extract_options!
326
+ filter_list << options
327
+
328
+ if name.in?([:commit, :rollback]) && options[:on]
329
+ fire_on = Array(options[:on])
330
+ assert_valid_transaction_action(fire_on)
331
+ options[:if] = [
332
+ -> { transaction_include_any_action?(fire_on) },
333
+ *options[:if]
334
+ ]
335
+ end
336
+
337
+
338
+ super(name, *filter_list, &block)
339
+ end
340
+
269
341
  private
270
342
  def prepend_option
271
343
  if ActiveRecord.run_after_transaction_callbacks_in_order_defined
@@ -354,18 +426,22 @@ module ActiveRecord
354
426
  # This method is available within the context of an ActiveRecord::Base
355
427
  # instance.
356
428
  def with_transaction_returning_status
357
- status = nil
358
- connection = self.class.connection
359
- ensure_finalize = !connection.transaction_open?
429
+ self.class.with_connection do |connection|
430
+ connection.pool.with_pool_transaction_isolation_level(ActiveRecord.default_transaction_isolation_level, connection.transaction_open?) do
431
+ status = nil
432
+ ensure_finalize = !connection.transaction_open?
360
433
 
361
- connection.transaction do
362
- add_to_transaction(ensure_finalize || has_transactional_callbacks?)
363
- remember_transaction_record_state
434
+ connection.transaction do
435
+ add_to_transaction(ensure_finalize || has_transactional_callbacks?)
436
+ remember_transaction_record_state
364
437
 
365
- status = yield
366
- raise ActiveRecord::Rollback unless status
438
+ status = yield
439
+ raise ActiveRecord::Rollback unless status
440
+ end
441
+ @_last_transaction_return_status = status
442
+ status
443
+ end
367
444
  end
368
- status
369
445
  end
370
446
 
371
447
  def trigger_transactional_callbacks? # :nodoc:
@@ -379,6 +455,7 @@ module ActiveRecord
379
455
  def init_internals
380
456
  super
381
457
  @_start_transaction_state = nil
458
+ @_last_transaction_return_status = nil
382
459
  @_committed_already_called = nil
383
460
  @_new_record_before_last_commit = nil
384
461
  end
@@ -457,7 +534,9 @@ module ActiveRecord
457
534
  # Add the record to the current transaction so that the #after_rollback and #after_commit
458
535
  # callbacks can be called.
459
536
  def add_to_transaction(ensure_finalize = true)
460
- self.class.connection.add_transaction_record(self, ensure_finalize)
537
+ self.class.with_connection do |connection|
538
+ connection.add_transaction_record(self, ensure_finalize)
539
+ end
461
540
  end
462
541
 
463
542
  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
@@ -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,22 @@ 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
 
28
+ JSON_ENCODER = ActiveSupport::JSON::Encoding.json_encoder.new(escape: false)
29
+
17
30
  def serialize(value)
18
- ActiveSupport::JSON.encode(value) unless value.nil?
31
+ JSON_ENCODER.encode(value) unless value.nil?
19
32
  end
20
33
 
21
34
  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
 
@@ -30,15 +31,19 @@ module ActiveRecord
30
31
  end
31
32
  end
32
33
 
33
- def inspect
34
- Kernel.instance_method(:inspect).bind_call(self)
35
- end
34
+ define_method(:inspect, Kernel.instance_method(:inspect))
36
35
 
37
36
  def changed_in_place?(raw_old_value, value)
38
37
  return false if value.nil?
39
- raw_new_value = encoded(value)
40
- raw_old_value.nil? != raw_new_value.nil? ||
41
- 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
42
47
  end
43
48
 
44
49
  def accessor
@@ -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
@@ -14,18 +14,19 @@ 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
+ # TODO: Remove fetch_cast_type and the need for connection after we release 8.1.
23
+ type = column.fetch_cast_type(@klass.lease_connection)
24
+ end
22
25
  end
23
26
 
24
27
  type || Type.default_value
25
28
  end
26
29
 
27
- delegate :connection, to: :@klass, private: true
28
-
29
30
  private
30
31
  attr_reader :table_name
31
32
  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?
8
- record.errors.add(attribute, :invalid, **options.merge(value: value))
7
+ context = record_validation_context_for_association(record)
8
+
9
+ if Array(value).reject { |association| valid_object?(association, context) }.any?
10
+ record.errors.add(attribute, :invalid, **options, 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