activerecord 7.1.3.4 → 7.2.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 (196) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +652 -2032
  3. data/README.rdoc +15 -15
  4. data/examples/performance.rb +2 -2
  5. data/lib/active_record/association_relation.rb +1 -1
  6. data/lib/active_record/associations/alias_tracker.rb +25 -19
  7. data/lib/active_record/associations/association.rb +15 -8
  8. data/lib/active_record/associations/belongs_to_association.rb +18 -11
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  10. data/lib/active_record/associations/builder/belongs_to.rb +1 -0
  11. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
  12. data/lib/active_record/associations/builder/has_many.rb +3 -4
  13. data/lib/active_record/associations/builder/has_one.rb +3 -4
  14. data/lib/active_record/associations/collection_association.rb +11 -5
  15. data/lib/active_record/associations/collection_proxy.rb +14 -1
  16. data/lib/active_record/associations/errors.rb +265 -0
  17. data/lib/active_record/associations/has_many_association.rb +3 -3
  18. data/lib/active_record/associations/has_many_through_association.rb +7 -1
  19. data/lib/active_record/associations/has_one_association.rb +2 -2
  20. data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
  21. data/lib/active_record/associations/join_dependency.rb +10 -12
  22. data/lib/active_record/associations/nested_error.rb +47 -0
  23. data/lib/active_record/associations/preloader/association.rb +2 -1
  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 +6 -0
  27. data/lib/active_record/associations/through_association.rb +1 -1
  28. data/lib/active_record/associations.rb +62 -289
  29. data/lib/active_record/attribute_assignment.rb +0 -2
  30. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  31. data/lib/active_record/attribute_methods/dirty.rb +2 -2
  32. data/lib/active_record/attribute_methods/primary_key.rb +23 -55
  33. data/lib/active_record/attribute_methods/read.rb +4 -16
  34. data/lib/active_record/attribute_methods/serialization.rb +4 -24
  35. data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
  36. data/lib/active_record/attribute_methods/write.rb +3 -3
  37. data/lib/active_record/attribute_methods.rb +89 -58
  38. data/lib/active_record/attributes.rb +61 -47
  39. data/lib/active_record/autosave_association.rb +17 -31
  40. data/lib/active_record/base.rb +2 -3
  41. data/lib/active_record/callbacks.rb +1 -1
  42. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +24 -107
  43. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
  44. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +270 -58
  45. data/lib/active_record/connection_adapters/abstract/database_statements.rb +35 -18
  46. data/lib/active_record/connection_adapters/abstract/query_cache.rb +190 -75
  47. data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
  48. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
  49. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +23 -10
  50. data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -62
  51. data/lib/active_record/connection_adapters/abstract_adapter.rb +38 -59
  52. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +73 -19
  53. data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
  54. data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
  55. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +8 -1
  56. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +16 -15
  57. data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -32
  58. data/lib/active_record/connection_adapters/pool_config.rb +7 -6
  59. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
  60. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  61. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  62. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  63. data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
  64. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -0
  65. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +18 -12
  66. data/lib/active_record/connection_adapters/postgresql_adapter.rb +29 -24
  67. data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
  68. data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
  69. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
  70. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
  71. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  72. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
  73. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  74. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
  75. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +127 -77
  76. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +15 -15
  77. data/lib/active_record/connection_adapters/trilogy_adapter.rb +32 -65
  78. data/lib/active_record/connection_adapters.rb +121 -0
  79. data/lib/active_record/connection_handling.rb +56 -41
  80. data/lib/active_record/core.rb +93 -40
  81. data/lib/active_record/counter_cache.rb +23 -10
  82. data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
  83. data/lib/active_record/database_configurations/database_config.rb +19 -4
  84. data/lib/active_record/database_configurations/hash_config.rb +44 -36
  85. data/lib/active_record/database_configurations/url_config.rb +20 -1
  86. data/lib/active_record/database_configurations.rb +1 -1
  87. data/lib/active_record/delegated_type.rb +30 -6
  88. data/lib/active_record/destroy_association_async_job.rb +1 -1
  89. data/lib/active_record/dynamic_matchers.rb +2 -2
  90. data/lib/active_record/encryption/encryptable_record.rb +3 -3
  91. data/lib/active_record/encryption/encrypted_attribute_type.rb +26 -6
  92. data/lib/active_record/encryption/encryptor.rb +18 -3
  93. data/lib/active_record/encryption/key_provider.rb +1 -1
  94. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  95. data/lib/active_record/encryption/message_serializer.rb +4 -0
  96. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  97. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  98. data/lib/active_record/encryption/scheme.rb +8 -4
  99. data/lib/active_record/encryption.rb +2 -0
  100. data/lib/active_record/enum.rb +19 -2
  101. data/lib/active_record/errors.rb +46 -20
  102. data/lib/active_record/explain.rb +13 -24
  103. data/lib/active_record/fixtures.rb +37 -31
  104. data/lib/active_record/future_result.rb +17 -4
  105. data/lib/active_record/gem_version.rb +3 -3
  106. data/lib/active_record/inheritance.rb +4 -2
  107. data/lib/active_record/insert_all.rb +18 -15
  108. data/lib/active_record/integration.rb +4 -1
  109. data/lib/active_record/internal_metadata.rb +48 -34
  110. data/lib/active_record/locking/optimistic.rb +8 -7
  111. data/lib/active_record/log_subscriber.rb +0 -21
  112. data/lib/active_record/marshalling.rb +4 -1
  113. data/lib/active_record/message_pack.rb +2 -2
  114. data/lib/active_record/migration/command_recorder.rb +2 -3
  115. data/lib/active_record/migration/compatibility.rb +11 -3
  116. data/lib/active_record/migration/default_strategy.rb +4 -5
  117. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  118. data/lib/active_record/migration.rb +85 -76
  119. data/lib/active_record/model_schema.rb +38 -70
  120. data/lib/active_record/nested_attributes.rb +24 -5
  121. data/lib/active_record/normalization.rb +3 -7
  122. data/lib/active_record/persistence.rb +32 -354
  123. data/lib/active_record/query_cache.rb +19 -8
  124. data/lib/active_record/query_logs.rb +15 -0
  125. data/lib/active_record/query_logs_formatter.rb +1 -1
  126. data/lib/active_record/querying.rb +21 -9
  127. data/lib/active_record/railtie.rb +50 -68
  128. data/lib/active_record/railties/controller_runtime.rb +13 -4
  129. data/lib/active_record/railties/databases.rake +42 -45
  130. data/lib/active_record/reflection.rb +106 -38
  131. data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
  132. data/lib/active_record/relation/batches.rb +14 -8
  133. data/lib/active_record/relation/calculations.rb +96 -63
  134. data/lib/active_record/relation/delegation.rb +8 -11
  135. data/lib/active_record/relation/finder_methods.rb +16 -2
  136. data/lib/active_record/relation/merger.rb +4 -6
  137. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  138. data/lib/active_record/relation/predicate_builder/association_query_value.rb +9 -3
  139. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +6 -1
  140. data/lib/active_record/relation/predicate_builder.rb +3 -3
  141. data/lib/active_record/relation/query_methods.rb +245 -65
  142. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  143. data/lib/active_record/relation/spawn_methods.rb +2 -18
  144. data/lib/active_record/relation/where_clause.rb +7 -19
  145. data/lib/active_record/relation.rb +500 -66
  146. data/lib/active_record/result.rb +32 -45
  147. data/lib/active_record/runtime_registry.rb +39 -0
  148. data/lib/active_record/sanitization.rb +24 -19
  149. data/lib/active_record/schema.rb +8 -6
  150. data/lib/active_record/schema_dumper.rb +19 -9
  151. data/lib/active_record/schema_migration.rb +30 -14
  152. data/lib/active_record/scoping/named.rb +1 -0
  153. data/lib/active_record/signed_id.rb +20 -1
  154. data/lib/active_record/statement_cache.rb +7 -7
  155. data/lib/active_record/table_metadata.rb +1 -10
  156. data/lib/active_record/tasks/database_tasks.rb +98 -48
  157. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  158. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  159. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
  160. data/lib/active_record/test_fixtures.rb +87 -89
  161. data/lib/active_record/testing/query_assertions.rb +121 -0
  162. data/lib/active_record/timestamp.rb +5 -3
  163. data/lib/active_record/token_for.rb +22 -12
  164. data/lib/active_record/touch_later.rb +1 -1
  165. data/lib/active_record/transaction.rb +132 -0
  166. data/lib/active_record/transactions.rb +70 -14
  167. data/lib/active_record/translation.rb +0 -2
  168. data/lib/active_record/type/serialized.rb +1 -3
  169. data/lib/active_record/type_caster/connection.rb +4 -4
  170. data/lib/active_record/validations/associated.rb +9 -3
  171. data/lib/active_record/validations/uniqueness.rb +15 -10
  172. data/lib/active_record/validations.rb +4 -1
  173. data/lib/active_record.rb +150 -41
  174. data/lib/arel/alias_predication.rb +1 -1
  175. data/lib/arel/collectors/bind.rb +2 -0
  176. data/lib/arel/collectors/composite.rb +7 -0
  177. data/lib/arel/collectors/sql_string.rb +1 -1
  178. data/lib/arel/collectors/substitute_binds.rb +1 -1
  179. data/lib/arel/nodes/binary.rb +0 -6
  180. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  181. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  182. data/lib/arel/nodes/node.rb +4 -3
  183. data/lib/arel/nodes/sql_literal.rb +7 -0
  184. data/lib/arel/nodes.rb +2 -2
  185. data/lib/arel/predications.rb +1 -1
  186. data/lib/arel/select_manager.rb +1 -1
  187. data/lib/arel/tree_manager.rb +8 -3
  188. data/lib/arel/update_manager.rb +2 -1
  189. data/lib/arel/visitors/dot.rb +1 -0
  190. data/lib/arel/visitors/mysql.rb +9 -4
  191. data/lib/arel/visitors/postgresql.rb +1 -12
  192. data/lib/arel/visitors/sqlite.rb +25 -0
  193. data/lib/arel/visitors/to_sql.rb +31 -17
  194. data/lib/arel.rb +7 -3
  195. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  196. metadata +21 -15
@@ -125,11 +125,11 @@ module ActiveRecord
125
125
  end
126
126
 
127
127
  def create_all
128
- each_local_configuration do |db_config|
129
- with_temporary_connection(db_config) do
130
- create(db_config)
131
- end
132
- end
128
+ db_config = migration_connection.pool.db_config
129
+
130
+ each_local_configuration { |db_config| create(db_config) }
131
+
132
+ migration_class.establish_connection(db_config)
133
133
  end
134
134
 
135
135
  def setup_initial_database_yaml # :nodoc:
@@ -175,11 +175,12 @@ module ActiveRecord
175
175
 
176
176
  def prepare_all
177
177
  seed = false
178
+ dump_db_configs = []
178
179
 
179
180
  each_current_configuration(env) do |db_config|
180
181
  with_temporary_pool(db_config) do
181
182
  begin
182
- database_initialized = migration_connection.schema_migration.table_exists?
183
+ database_initialized = migration_connection_pool.schema_migration.table_exists?
183
184
  rescue ActiveRecord::NoDatabaseError
184
185
  create(db_config)
185
186
  retry
@@ -192,9 +193,27 @@ module ActiveRecord
192
193
 
193
194
  seed = true
194
195
  end
196
+ end
197
+ end
198
+
199
+ each_current_environment(env) do |environment|
200
+ db_configs_with_versions(environment).sort.each do |version, db_configs|
201
+ dump_db_configs |= db_configs
202
+
203
+ db_configs.each do |db_config|
204
+ with_temporary_pool(db_config) do
205
+ migrate(version)
206
+ end
207
+ end
208
+ end
209
+ end
195
210
 
196
- migrate
197
- dump_schema(db_config) if ActiveRecord.dump_schema_after_migration
211
+ # Dump schema for databases that were migrated.
212
+ if ActiveRecord.dump_schema_after_migration
213
+ dump_db_configs.each do |db_config|
214
+ with_temporary_pool(db_config) do
215
+ dump_schema(db_config)
216
+ end
198
217
  end
199
218
  end
200
219
 
@@ -240,7 +259,7 @@ module ActiveRecord
240
259
 
241
260
  check_target_version
242
261
 
243
- migration_connection.migration_context.migrate(target_version) do |migration|
262
+ migration_connection_pool.migration_context.migrate(target_version) do |migration|
244
263
  if version.blank?
245
264
  scope.blank? || scope == migration.scope
246
265
  else
@@ -250,17 +269,17 @@ module ActiveRecord
250
269
  Migration.write("No migrations ran. (using #{scope} scope)") if scope.present? && migrations_ran.empty?
251
270
  end
252
271
 
253
- migration_connection.schema_cache.clear!
272
+ migration_connection_pool.schema_cache.clear!
254
273
  ensure
255
274
  Migration.verbose = verbose_was
256
275
  end
257
276
 
258
- def db_configs_with_versions(db_configs) # :nodoc:
277
+ def db_configs_with_versions(environment = env) # :nodoc:
259
278
  db_configs_with_versions = Hash.new { |h, k| h[k] = [] }
260
279
 
261
- with_temporary_connection_for_each do |conn|
262
- db_config = conn.pool.db_config
263
- versions_to_run = conn.migration_context.pending_migration_versions
280
+ with_temporary_pool_for_each(env: environment) do |pool|
281
+ db_config = pool.db_config
282
+ versions_to_run = pool.migration_context.pending_migration_versions
264
283
  target_version = ActiveRecord::Tasks::DatabaseTasks.target_version
265
284
 
266
285
  versions_to_run.each do |version|
@@ -273,15 +292,15 @@ module ActiveRecord
273
292
  end
274
293
 
275
294
  def migrate_status
276
- unless migration_connection.schema_migration.table_exists?
295
+ unless migration_connection_pool.schema_migration.table_exists?
277
296
  Kernel.abort "Schema migrations table does not exist yet."
278
297
  end
279
298
 
280
299
  # output
281
- puts "\ndatabase: #{migration_connection.pool.db_config.database}\n\n"
300
+ puts "\ndatabase: #{migration_connection_pool.db_config.database}\n\n"
282
301
  puts "#{'Status'.center(8)} #{'Migration ID'.ljust(14)} Migration Name"
283
302
  puts "-" * 50
284
- migration_connection.migration_context.migrations_status.each do |status, version, name|
303
+ migration_connection_pool.migration_context.migrations_status.each do |status, version, name|
285
304
  puts "#{status.center(8)} #{version.ljust(14)} #{name}"
286
305
  end
287
306
  puts
@@ -362,7 +381,7 @@ module ActiveRecord
362
381
  raise ArgumentError, "unknown format #{format.inspect}"
363
382
  end
364
383
 
365
- migration_connection.internal_metadata.create_table_and_set_flags(db_config.env_name, schema_sha1(file))
384
+ migration_connection_pool.internal_metadata.create_table_and_set_flags(db_config.env_name, schema_sha1(file))
366
385
  ensure
367
386
  Migration.verbose = verbose_was
368
387
  end
@@ -374,11 +393,12 @@ module ActiveRecord
374
393
 
375
394
  return true unless file && File.exist?(file)
376
395
 
377
- with_temporary_connection(db_config) do |connection|
378
- return false unless connection.internal_metadata.enabled?
379
- return false unless connection.internal_metadata.table_exists?
396
+ with_temporary_pool(db_config) do |pool|
397
+ internal_metadata = pool.internal_metadata
398
+ return false unless internal_metadata.enabled?
399
+ return false unless internal_metadata.table_exists?
380
400
 
381
- connection.internal_metadata[:schema_sha1] == schema_sha1(file)
401
+ internal_metadata[:schema_sha1] == schema_sha1(file)
382
402
  end
383
403
  end
384
404
 
@@ -389,7 +409,7 @@ module ActiveRecord
389
409
 
390
410
  with_temporary_pool(db_config, clobber: true) do
391
411
  if schema_up_to_date?(db_config, format, file)
392
- truncate_tables(db_config)
412
+ truncate_tables(db_config) unless ENV["SKIP_TEST_DATABASE_TRUNCATE"]
393
413
  else
394
414
  purge(db_config)
395
415
  load_schema(db_config, format, file)
@@ -411,11 +431,11 @@ module ActiveRecord
411
431
  case format
412
432
  when :ruby
413
433
  File.open(filename, "w:utf-8") do |file|
414
- ActiveRecord::SchemaDumper.dump(migration_connection, file)
434
+ ActiveRecord::SchemaDumper.dump(migration_connection_pool, file)
415
435
  end
416
436
  when :sql
417
437
  structure_dump(db_config, filename)
418
- if migration_connection.schema_migration.table_exists?
438
+ if migration_connection_pool.schema_migration.table_exists?
419
439
  File.open(filename, "a") do |f|
420
440
  f.puts migration_connection.dump_schema_information
421
441
  f.print "\n"
@@ -437,14 +457,26 @@ module ActiveRecord
437
457
  end
438
458
  end
439
459
 
440
- def cache_dump_filename(db_config_name, schema_cache_path: nil)
441
- filename = if ActiveRecord::Base.configurations.primary?(db_config_name)
442
- "schema_cache.yml"
460
+ def cache_dump_filename(db_config_or_name, schema_cache_path: nil)
461
+ if db_config_or_name.is_a?(DatabaseConfigurations::DatabaseConfig)
462
+ schema_cache_path ||
463
+ db_config_or_name.schema_cache_path ||
464
+ schema_cache_env ||
465
+ db_config_or_name.default_schema_cache_path(ActiveRecord::Tasks::DatabaseTasks.db_dir)
443
466
  else
444
- "#{db_config_name}_schema_cache.yml"
445
- end
467
+ ActiveRecord.deprecator.warn(<<~MSG.squish)
468
+ Passing a database name to `cache_dump_filename` is deprecated and will be removed in Rails 8.0. Pass a
469
+ `ActiveRecord::DatabaseConfigurations::DatabaseConfig` object instead.
470
+ MSG
446
471
 
447
- schema_cache_path || ENV["SCHEMA_CACHE"] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, filename)
472
+ filename = if ActiveRecord::Base.configurations.primary?(db_config_or_name)
473
+ "schema_cache.yml"
474
+ else
475
+ "#{db_config_or_name}_schema_cache.yml"
476
+ end
477
+
478
+ schema_cache_path || schema_cache_env || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, filename)
479
+ end
448
480
  end
449
481
 
450
482
  def load_schema_current(format = ActiveRecord.schema_format, file = nil, environment = env)
@@ -476,29 +508,29 @@ module ActiveRecord
476
508
  # Dumps the schema cache in YAML format for the connection into the file
477
509
  #
478
510
  # ==== Examples
479
- # ActiveRecord::Tasks::DatabaseTasks.dump_schema_cache(ActiveRecord::Base.connection, "tmp/schema_dump.yaml")
480
- def dump_schema_cache(conn, filename)
481
- conn.schema_cache.dump_to(filename)
511
+ # ActiveRecord::Tasks::DatabaseTasks.dump_schema_cache(ActiveRecord::Base.lease_connection, "tmp/schema_dump.yaml")
512
+ def dump_schema_cache(conn_or_pool, filename)
513
+ conn_or_pool.schema_cache.dump_to(filename)
482
514
  end
483
515
 
484
516
  def clear_schema_cache(filename)
485
517
  FileUtils.rm_f filename, verbose: false
486
518
  end
487
519
 
488
- def with_temporary_connection_for_each(env: ActiveRecord::Tasks::DatabaseTasks.env, name: nil, clobber: false, &block) # :nodoc:
520
+ def with_temporary_pool_for_each(env: ActiveRecord::Tasks::DatabaseTasks.env, name: nil, clobber: false, &block) # :nodoc:
489
521
  if name
490
522
  db_config = ActiveRecord::Base.configurations.configs_for(env_name: env, name: name)
491
- with_temporary_connection(db_config, clobber: clobber, &block)
523
+ with_temporary_pool(db_config, clobber: clobber, &block)
492
524
  else
493
525
  ActiveRecord::Base.configurations.configs_for(env_name: env, name: name).each do |db_config|
494
- with_temporary_connection(db_config, clobber: clobber, &block)
526
+ with_temporary_pool(db_config, clobber: clobber, &block)
495
527
  end
496
528
  end
497
529
  end
498
530
 
499
- def with_temporary_connection(db_config, clobber: false) # :nodoc:
531
+ def with_temporary_connection(db_config, clobber: false, &block) # :nodoc:
500
532
  with_temporary_pool(db_config, clobber: clobber) do |pool|
501
- yield pool.connection
533
+ pool.with_connection(&block)
502
534
  end
503
535
  end
504
536
 
@@ -507,10 +539,25 @@ module ActiveRecord
507
539
  end
508
540
 
509
541
  def migration_connection # :nodoc:
510
- migration_class.connection
542
+ migration_class.lease_connection
543
+ end
544
+
545
+ def migration_connection_pool # :nodoc:
546
+ migration_class.connection_pool
511
547
  end
512
548
 
513
549
  private
550
+ def schema_cache_env
551
+ if ENV["SCHEMA_CACHE"]
552
+ ActiveRecord.deprecator.warn(<<~MSG.squish)
553
+ Setting `ENV["SCHEMA_CACHE"]` is deprecated and will be removed in Rails 8.0.
554
+ Configure the `:schema_cache_path` in the database configuration instead.
555
+ MSG
556
+
557
+ nil
558
+ end
559
+ end
560
+
514
561
  def with_temporary_pool(db_config, clobber: false)
515
562
  original_db_config = migration_class.connection_db_config
516
563
  pool = migration_class.connection_handler.establish_connection(db_config, clobber: clobber)
@@ -552,10 +599,7 @@ module ActiveRecord
552
599
  end
553
600
 
554
601
  def each_current_configuration(environment, name = nil)
555
- environments = [environment]
556
- environments << "test" if environment == "development" && !ENV["SKIP_TEST_DATABASE"] && !ENV["DATABASE_URL"]
557
-
558
- environments.each do |env|
602
+ each_current_environment(environment) do |env|
559
603
  configs_for(env_name: env).each do |db_config|
560
604
  next if name && name != db_config.name
561
605
 
@@ -564,6 +608,12 @@ module ActiveRecord
564
608
  end
565
609
  end
566
610
 
611
+ def each_current_environment(environment, &block)
612
+ environments = [environment]
613
+ environments << "test" if environment == "development" && !ENV["SKIP_TEST_DATABASE"] && !ENV["DATABASE_URL"]
614
+ environments.each(&block)
615
+ end
616
+
567
617
  def each_local_configuration
568
618
  configs_for.each do |db_config|
569
619
  next unless db_config.database
@@ -603,11 +653,11 @@ module ActiveRecord
603
653
 
604
654
  def check_current_protected_environment!(db_config)
605
655
  with_temporary_pool(db_config) do |pool|
606
- connection = pool.connection
607
- current = connection.migration_context.current_environment
608
- stored = connection.migration_context.last_stored_environment
656
+ migration_context = pool.migration_context
657
+ current = migration_context.current_environment
658
+ stored = migration_context.last_stored_environment
609
659
 
610
- if connection.migration_context.protected_environment?
660
+ if migration_context.protected_environment?
611
661
  raise ActiveRecord::ProtectedEnvironmentError.new(stored)
612
662
  end
613
663
 
@@ -71,7 +71,7 @@ module ActiveRecord
71
71
  attr_reader :db_config, :configuration_hash
72
72
 
73
73
  def connection
74
- ActiveRecord::Base.connection
74
+ ActiveRecord::Base.lease_connection
75
75
  end
76
76
 
77
77
  def establish_connection(config = db_config)
@@ -88,7 +88,7 @@ module ActiveRecord
88
88
  attr_reader :db_config, :configuration_hash
89
89
 
90
90
  def connection
91
- ActiveRecord::Base.connection
91
+ ActiveRecord::Base.lease_connection
92
92
  end
93
93
 
94
94
  def establish_connection(config = db_config)
@@ -66,11 +66,12 @@ module ActiveRecord
66
66
  attr_reader :db_config, :root
67
67
 
68
68
  def connection
69
- ActiveRecord::Base.connection
69
+ ActiveRecord::Base.lease_connection
70
70
  end
71
71
 
72
72
  def establish_connection(config = db_config)
73
73
  ActiveRecord::Base.establish_connection(config)
74
+ connection.connect!
74
75
  end
75
76
 
76
77
  def run_cmd(cmd, args, out)
@@ -13,6 +13,7 @@ module ActiveRecord
13
13
 
14
14
  def after_teardown # :nodoc:
15
15
  super
16
+ ensure
16
17
  teardown_fixtures
17
18
  end
18
19
 
@@ -52,20 +53,6 @@ module ActiveRecord
52
53
  self.fixture_class_names = fixture_class_names.merge(class_names.stringify_keys)
53
54
  end
54
55
 
55
- def fixture_path # :nodoc:
56
- ActiveRecord.deprecator.warn(<<~WARNING)
57
- TestFixtures.fixture_path is deprecated and will be removed in Rails 7.2. Use .fixture_paths instead.
58
- If multiple fixture paths have been configured with .fixture_paths, then .fixture_path will just return
59
- the first path.
60
- WARNING
61
- fixture_paths.first
62
- end
63
-
64
- def fixture_path=(path) # :nodoc:
65
- ActiveRecord.deprecator.warn("TestFixtures.fixture_path= is deprecated and will be removed in Rails 7.2. Use .fixture_paths= instead.")
66
- self.fixture_paths = Array(path)
67
- end
68
-
69
56
  def fixtures(*fixture_set_names)
70
57
  if fixture_set_names.first == :all
71
58
  raise StandardError, "No fixture path found. Please set `#{self}.fixture_paths`." if fixture_paths.blank?
@@ -78,7 +65,7 @@ module ActiveRecord
78
65
  fixture_set_names = fixture_set_names.flatten.map(&:to_s)
79
66
  end
80
67
 
81
- self.fixture_table_names |= fixture_set_names
68
+ self.fixture_table_names = (fixture_table_names | fixture_set_names).sort
82
69
  setup_fixture_accessors(fixture_set_names)
83
70
  end
84
71
 
@@ -109,45 +96,76 @@ module ActiveRecord
109
96
  end
110
97
  end
111
98
 
112
- def fixture_path # :nodoc:
113
- ActiveRecord.deprecator.warn(<<~WARNING)
114
- TestFixtures#fixture_path is deprecated and will be removed in Rails 7.2. Use #fixture_paths instead.
115
- If multiple fixture paths have been configured with #fixture_paths, then #fixture_path will just return
116
- the first path.
117
- WARNING
118
- fixture_paths.first
99
+ # Generic fixture accessor for fixture names that may conflict with other methods.
100
+ #
101
+ # assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
102
+ # assert_equal "Ruby on Rails", fixture(:web_sites, :rubyonrails).name
103
+ def fixture(fixture_set_name, *fixture_names)
104
+ active_record_fixture(fixture_set_name, *fixture_names)
119
105
  end
120
106
 
121
- def run_in_transaction?
122
- use_transactional_tests &&
123
- !self.class.uses_transaction?(name)
124
- end
125
-
126
- def setup_fixtures(config = ActiveRecord::Base)
127
- if pre_loaded_fixtures && !use_transactional_tests
128
- raise RuntimeError, "pre_loaded_fixtures requires use_transactional_tests"
107
+ private
108
+ def run_in_transaction?
109
+ use_transactional_tests &&
110
+ !self.class.uses_transaction?(name)
129
111
  end
130
112
 
131
- @fixture_cache = {}
132
- @fixture_connections = []
133
- @@already_loaded_fixtures ||= {}
134
- @connection_subscriber = nil
135
- @saved_pool_configs = Hash.new { |hash, key| hash[key] = {} }
113
+ def setup_fixtures(config = ActiveRecord::Base)
114
+ if pre_loaded_fixtures && !use_transactional_tests
115
+ raise RuntimeError, "pre_loaded_fixtures requires use_transactional_tests"
116
+ end
117
+
118
+ @fixture_cache = {}
119
+ @fixture_cache_key = [self.class.fixture_table_names.dup, self.class.fixture_paths.dup, self.class.fixture_class_names.dup]
120
+ @fixture_connection_pools = []
121
+ @@already_loaded_fixtures ||= {}
122
+ @connection_subscriber = nil
123
+ @saved_pool_configs = Hash.new { |hash, key| hash[key] = {} }
124
+
125
+ if run_in_transaction?
126
+ # Load fixtures once and begin transaction.
127
+ @loaded_fixtures = @@already_loaded_fixtures[@fixture_cache_key]
128
+ unless @loaded_fixtures
129
+ @@already_loaded_fixtures.clear
130
+ @loaded_fixtures = @@already_loaded_fixtures[@fixture_cache_key] = load_fixtures(config)
131
+ end
136
132
 
137
- # Load fixtures once and begin transaction.
138
- if run_in_transaction?
139
- if @@already_loaded_fixtures[self.class]
140
- @loaded_fixtures = @@already_loaded_fixtures[self.class]
133
+ setup_transactional_fixtures
141
134
  else
135
+ # Load fixtures for every test.
136
+ ActiveRecord::FixtureSet.reset_cache
137
+ invalidate_already_loaded_fixtures
142
138
  @loaded_fixtures = load_fixtures(config)
143
- @@already_loaded_fixtures[self.class] = @loaded_fixtures
144
139
  end
145
140
 
141
+ # Instantiate fixtures for every test if requested.
142
+ instantiate_fixtures if use_instantiated_fixtures
143
+ end
144
+
145
+ def teardown_fixtures
146
+ # Rollback changes if a transaction is active.
147
+ if run_in_transaction?
148
+ teardown_transactional_fixtures
149
+ else
150
+ ActiveRecord::FixtureSet.reset_cache
151
+ invalidate_already_loaded_fixtures
152
+ end
153
+
154
+ ActiveRecord::Base.connection_handler.clear_active_connections!(:all)
155
+ end
156
+
157
+ def invalidate_already_loaded_fixtures
158
+ @@already_loaded_fixtures.clear
159
+ end
160
+
161
+ def setup_transactional_fixtures
162
+ setup_shared_connection_pool
163
+
146
164
  # Begin transactions for connections already established
147
- @fixture_connections = enlist_fixture_connections
148
- @fixture_connections.each do |connection|
149
- connection.begin_transaction joinable: false, _lazy: false
150
- connection.pool.lock_thread = true if lock_threads
165
+ @fixture_connection_pools = ActiveRecord::Base.connection_handler.connection_pool_list(:writing)
166
+ @fixture_connection_pools.each do |pool|
167
+ pool.pin_connection!(lock_threads)
168
+ pool.lease_connection
151
169
  end
152
170
 
153
171
  # When connections are established in the future, begin a transaction too
@@ -156,59 +174,31 @@ module ActiveRecord
156
174
  shard = payload[:shard] if payload.key?(:shard)
157
175
 
158
176
  if connection_name
159
- begin
160
- connection = ActiveRecord::Base.connection_handler.retrieve_connection(connection_name, shard: shard)
161
- rescue ConnectionNotEstablished
162
- connection = nil
163
- end
164
-
165
- if connection
177
+ pool = ActiveRecord::Base.connection_handler.retrieve_connection_pool(connection_name, shard: shard)
178
+ if pool
166
179
  setup_shared_connection_pool
167
180
 
168
- if !@fixture_connections.include?(connection)
169
- connection.begin_transaction joinable: false, _lazy: false
170
- connection.pool.lock_thread = true if lock_threads
171
- @fixture_connections << connection
181
+ unless @fixture_connection_pools.include?(pool)
182
+ pool.pin_connection!(lock_threads)
183
+ pool.lease_connection
184
+ @fixture_connection_pools << pool
172
185
  end
173
186
  end
174
187
  end
175
188
  end
176
-
177
- # Load fixtures for every test.
178
- else
179
- ActiveRecord::FixtureSet.reset_cache
180
- @@already_loaded_fixtures[self.class] = nil
181
- @loaded_fixtures = load_fixtures(config)
182
189
  end
183
190
 
184
- # Instantiate fixtures for every test if requested.
185
- instantiate_fixtures if use_instantiated_fixtures
186
- end
187
-
188
- def teardown_fixtures
189
- # Rollback changes if a transaction is active.
190
- if run_in_transaction?
191
+ def teardown_transactional_fixtures
191
192
  ActiveSupport::Notifications.unsubscribe(@connection_subscriber) if @connection_subscriber
192
- @fixture_connections.each do |connection|
193
- connection.rollback_transaction if connection.transaction_open?
194
- connection.pool.lock_thread = false
193
+ unless @fixture_connection_pools.map(&:unpin_connection!).all?
194
+ # Something caused the transaction to be committed or rolled back
195
+ # We can no longer trust the database is in a clean state.
196
+ @@already_loaded_fixtures.clear
195
197
  end
196
- @fixture_connections.clear
198
+ @fixture_connection_pools.clear
197
199
  teardown_shared_connection_pool
198
- else
199
- ActiveRecord::FixtureSet.reset_cache
200
200
  end
201
201
 
202
- ActiveRecord::Base.connection_handler.clear_active_connections!(:all)
203
- end
204
-
205
- def enlist_fixture_connections
206
- setup_shared_connection_pool
207
-
208
- ActiveRecord::Base.connection_handler.connection_pool_list(:writing).map(&:connection)
209
- end
210
-
211
- private
212
202
  # Shares the writing connection pool with connections on
213
203
  # other handlers.
214
204
  #
@@ -271,22 +261,30 @@ module ActiveRecord
271
261
  use_instantiated_fixtures != :no_instances
272
262
  end
273
263
 
274
- def method_missing(name, *args, **kwargs, &block)
275
- if fs_name = fixture_sets[name.to_s]
276
- access_fixture(fs_name, *args, **kwargs, &block)
264
+ def method_missing(method, ...)
265
+ if fixture_sets.key?(method.name)
266
+ active_record_fixture(method, ...)
277
267
  else
278
268
  super
279
269
  end
280
270
  end
281
271
 
282
- def respond_to_missing?(name, include_private = false)
283
- if include_private && fixture_sets.key?(name.to_s)
272
+ def respond_to_missing?(method, include_private = false)
273
+ if include_private && fixture_sets.key?(method.name)
284
274
  true
285
275
  else
286
276
  super
287
277
  end
288
278
  end
289
279
 
280
+ def active_record_fixture(fixture_set_name, *fixture_names)
281
+ if fs_name = fixture_sets[fixture_set_name.name]
282
+ access_fixture(fs_name, *fixture_names)
283
+ else
284
+ raise StandardError, "No fixture set named '#{fixture_set_name.inspect}'"
285
+ end
286
+ end
287
+
290
288
  def access_fixture(fs_name, *fixture_names)
291
289
  force_reload = fixture_names.pop if fixture_names.last == true || fixture_names.last == :reload
292
290
  return_single_record = fixture_names.size == 1