activerecord 7.1.5.1 → 7.2.0.beta1

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