activerecord 7.1.3.2 → 7.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +570 -2094
- data/README.rdoc +15 -15
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +15 -8
- data/lib/active_record/associations/belongs_to_association.rb +18 -11
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/belongs_to.rb +1 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/collection_association.rb +11 -5
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/has_many_association.rb +3 -3
- data/lib/active_record/associations/has_one_association.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/join_dependency.rb +6 -8
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +2 -1
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/singular_association.rb +6 -0
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +34 -273
- data/lib/active_record/attribute_assignment.rb +1 -11
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +2 -2
- data/lib/active_record/attribute_methods/primary_key.rb +23 -55
- data/lib/active_record/attribute_methods/read.rb +4 -16
- data/lib/active_record/attribute_methods/serialization.rb +4 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -6
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +89 -58
- data/lib/active_record/attributes.rb +60 -45
- data/lib/active_record/autosave_association.rb +17 -31
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +24 -107
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +244 -58
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +35 -18
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +188 -75
- data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +22 -9
- data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -62
- data/lib/active_record/connection_adapters/abstract_adapter.rb +38 -59
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +69 -19
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +8 -1
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +16 -15
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -32
- data/lib/active_record/connection_adapters/pool_config.rb +7 -6
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +16 -12
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -21
- data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +127 -77
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +15 -15
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +32 -65
- data/lib/active_record/connection_adapters.rb +121 -0
- data/lib/active_record/connection_handling.rb +56 -41
- data/lib/active_record/core.rb +60 -39
- data/lib/active_record/counter_cache.rb +23 -10
- data/lib/active_record/database_configurations/connection_url_resolver.rb +7 -2
- data/lib/active_record/database_configurations/database_config.rb +19 -4
- data/lib/active_record/database_configurations/hash_config.rb +44 -36
- data/lib/active_record/database_configurations/url_config.rb +20 -1
- data/lib/active_record/database_configurations.rb +1 -1
- data/lib/active_record/delegated_type.rb +30 -6
- data/lib/active_record/destroy_association_async_job.rb +1 -1
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/encryptable_record.rb +3 -3
- data/lib/active_record/encryption/encrypted_attribute_type.rb +26 -6
- data/lib/active_record/encryption/encryptor.rb +18 -3
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +4 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +8 -4
- data/lib/active_record/enum.rb +26 -6
- data/lib/active_record/errors.rb +46 -20
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixtures.rb +37 -31
- data/lib/active_record/future_result.rb +17 -4
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +4 -2
- data/lib/active_record/insert_all.rb +18 -15
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/log_subscriber.rb +0 -21
- data/lib/active_record/marshalling.rb +1 -1
- data/lib/active_record/message_pack.rb +2 -2
- data/lib/active_record/migration/command_recorder.rb +2 -3
- data/lib/active_record/migration/compatibility.rb +11 -3
- data/lib/active_record/migration/default_strategy.rb +4 -5
- data/lib/active_record/migration/pending_migration_connection.rb +2 -2
- data/lib/active_record/migration.rb +85 -76
- data/lib/active_record/model_schema.rb +39 -70
- data/lib/active_record/nested_attributes.rb +11 -3
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +32 -354
- data/lib/active_record/query_cache.rb +18 -6
- data/lib/active_record/query_logs.rb +15 -0
- data/lib/active_record/query_logs_formatter.rb +1 -1
- data/lib/active_record/querying.rb +21 -9
- data/lib/active_record/railtie.rb +54 -67
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +42 -45
- data/lib/active_record/reflection.rb +102 -37
- data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
- data/lib/active_record/relation/batches.rb +14 -8
- data/lib/active_record/relation/calculations.rb +95 -62
- data/lib/active_record/relation/delegation.rb +8 -11
- data/lib/active_record/relation/finder_methods.rb +16 -2
- data/lib/active_record/relation/merger.rb +4 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +9 -3
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +6 -1
- data/lib/active_record/relation/predicate_builder.rb +3 -3
- data/lib/active_record/relation/query_methods.rb +212 -47
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +2 -18
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +500 -66
- data/lib/active_record/result.rb +32 -45
- data/lib/active_record/runtime_registry.rb +39 -0
- data/lib/active_record/sanitization.rb +24 -19
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +19 -9
- data/lib/active_record/schema_migration.rb +30 -14
- data/lib/active_record/scoping/named.rb +1 -0
- data/lib/active_record/signed_id.rb +20 -1
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/table_metadata.rb +1 -10
- data/lib/active_record/tasks/database_tasks.rb +87 -48
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
- data/lib/active_record/test_fixtures.rb +87 -89
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +5 -3
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +70 -14
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/associated.rb +9 -3
- data/lib/active_record/validations/uniqueness.rb +14 -10
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +150 -41
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/nodes/binary.rb +0 -6
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +4 -3
- data/lib/arel/nodes/sql_literal.rb +7 -0
- data/lib/arel/nodes.rb +2 -2
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/tree_manager.rb +8 -3
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/to_sql.rb +31 -17
- data/lib/arel.rb +7 -3
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- metadata +18 -12
@@ -125,11 +125,11 @@ module ActiveRecord
|
|
125
125
|
end
|
126
126
|
|
127
127
|
def create_all
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
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:
|
@@ -179,7 +179,7 @@ module ActiveRecord
|
|
179
179
|
each_current_configuration(env) do |db_config|
|
180
180
|
with_temporary_pool(db_config) do
|
181
181
|
begin
|
182
|
-
database_initialized =
|
182
|
+
database_initialized = migration_connection_pool.schema_migration.table_exists?
|
183
183
|
rescue ActiveRecord::NoDatabaseError
|
184
184
|
create(db_config)
|
185
185
|
retry
|
@@ -192,9 +192,17 @@ module ActiveRecord
|
|
192
192
|
|
193
193
|
seed = true
|
194
194
|
end
|
195
|
+
end
|
196
|
+
end
|
195
197
|
|
196
|
-
|
197
|
-
|
198
|
+
each_current_environment(env) do |environment|
|
199
|
+
db_configs_with_versions(environment).sort.each do |version, db_configs|
|
200
|
+
db_configs.each do |db_config|
|
201
|
+
with_temporary_pool(db_config) do
|
202
|
+
migrate(version)
|
203
|
+
dump_schema(db_config) if ActiveRecord.dump_schema_after_migration
|
204
|
+
end
|
205
|
+
end
|
198
206
|
end
|
199
207
|
end
|
200
208
|
|
@@ -240,7 +248,7 @@ module ActiveRecord
|
|
240
248
|
|
241
249
|
check_target_version
|
242
250
|
|
243
|
-
|
251
|
+
migration_connection_pool.migration_context.migrate(target_version) do |migration|
|
244
252
|
if version.blank?
|
245
253
|
scope.blank? || scope == migration.scope
|
246
254
|
else
|
@@ -250,17 +258,17 @@ module ActiveRecord
|
|
250
258
|
Migration.write("No migrations ran. (using #{scope} scope)") if scope.present? && migrations_ran.empty?
|
251
259
|
end
|
252
260
|
|
253
|
-
|
261
|
+
migration_connection_pool.schema_cache.clear!
|
254
262
|
ensure
|
255
263
|
Migration.verbose = verbose_was
|
256
264
|
end
|
257
265
|
|
258
|
-
def db_configs_with_versions(
|
266
|
+
def db_configs_with_versions(environment = env) # :nodoc:
|
259
267
|
db_configs_with_versions = Hash.new { |h, k| h[k] = [] }
|
260
268
|
|
261
|
-
|
262
|
-
db_config =
|
263
|
-
versions_to_run =
|
269
|
+
with_temporary_pool_for_each(env: environment) do |pool|
|
270
|
+
db_config = pool.db_config
|
271
|
+
versions_to_run = pool.migration_context.pending_migration_versions
|
264
272
|
target_version = ActiveRecord::Tasks::DatabaseTasks.target_version
|
265
273
|
|
266
274
|
versions_to_run.each do |version|
|
@@ -273,15 +281,15 @@ module ActiveRecord
|
|
273
281
|
end
|
274
282
|
|
275
283
|
def migrate_status
|
276
|
-
unless
|
284
|
+
unless migration_connection_pool.schema_migration.table_exists?
|
277
285
|
Kernel.abort "Schema migrations table does not exist yet."
|
278
286
|
end
|
279
287
|
|
280
288
|
# output
|
281
|
-
puts "\ndatabase: #{
|
289
|
+
puts "\ndatabase: #{migration_connection_pool.db_config.database}\n\n"
|
282
290
|
puts "#{'Status'.center(8)} #{'Migration ID'.ljust(14)} Migration Name"
|
283
291
|
puts "-" * 50
|
284
|
-
|
292
|
+
migration_connection_pool.migration_context.migrations_status.each do |status, version, name|
|
285
293
|
puts "#{status.center(8)} #{version.ljust(14)} #{name}"
|
286
294
|
end
|
287
295
|
puts
|
@@ -362,7 +370,7 @@ module ActiveRecord
|
|
362
370
|
raise ArgumentError, "unknown format #{format.inspect}"
|
363
371
|
end
|
364
372
|
|
365
|
-
|
373
|
+
migration_connection_pool.internal_metadata.create_table_and_set_flags(db_config.env_name, schema_sha1(file))
|
366
374
|
ensure
|
367
375
|
Migration.verbose = verbose_was
|
368
376
|
end
|
@@ -374,11 +382,12 @@ module ActiveRecord
|
|
374
382
|
|
375
383
|
return true unless file && File.exist?(file)
|
376
384
|
|
377
|
-
|
378
|
-
|
379
|
-
return false unless
|
385
|
+
with_temporary_pool(db_config) do |pool|
|
386
|
+
internal_metadata = pool.internal_metadata
|
387
|
+
return false unless internal_metadata.enabled?
|
388
|
+
return false unless internal_metadata.table_exists?
|
380
389
|
|
381
|
-
|
390
|
+
internal_metadata[:schema_sha1] == schema_sha1(file)
|
382
391
|
end
|
383
392
|
end
|
384
393
|
|
@@ -389,7 +398,7 @@ module ActiveRecord
|
|
389
398
|
|
390
399
|
with_temporary_pool(db_config, clobber: true) do
|
391
400
|
if schema_up_to_date?(db_config, format, file)
|
392
|
-
truncate_tables(db_config)
|
401
|
+
truncate_tables(db_config) unless ENV["SKIP_TEST_DATABASE_TRUNCATE"]
|
393
402
|
else
|
394
403
|
purge(db_config)
|
395
404
|
load_schema(db_config, format, file)
|
@@ -411,11 +420,11 @@ module ActiveRecord
|
|
411
420
|
case format
|
412
421
|
when :ruby
|
413
422
|
File.open(filename, "w:utf-8") do |file|
|
414
|
-
ActiveRecord::SchemaDumper.dump(
|
423
|
+
ActiveRecord::SchemaDumper.dump(migration_connection_pool, file)
|
415
424
|
end
|
416
425
|
when :sql
|
417
426
|
structure_dump(db_config, filename)
|
418
|
-
if
|
427
|
+
if migration_connection_pool.schema_migration.table_exists?
|
419
428
|
File.open(filename, "a") do |f|
|
420
429
|
f.puts migration_connection.dump_schema_information
|
421
430
|
f.print "\n"
|
@@ -437,14 +446,26 @@ module ActiveRecord
|
|
437
446
|
end
|
438
447
|
end
|
439
448
|
|
440
|
-
def cache_dump_filename(
|
441
|
-
|
442
|
-
|
449
|
+
def cache_dump_filename(db_config_or_name, schema_cache_path: nil)
|
450
|
+
if db_config_or_name.is_a?(DatabaseConfigurations::DatabaseConfig)
|
451
|
+
schema_cache_path ||
|
452
|
+
db_config_or_name.schema_cache_path ||
|
453
|
+
schema_cache_env ||
|
454
|
+
db_config_or_name.default_schema_cache_path(ActiveRecord::Tasks::DatabaseTasks.db_dir)
|
443
455
|
else
|
444
|
-
|
445
|
-
|
456
|
+
ActiveRecord.deprecator.warn(<<~MSG.squish)
|
457
|
+
Passing a database name to `cache_dump_filename` is deprecated and will be removed in Rails 8.0. Pass a
|
458
|
+
`ActiveRecord::DatabaseConfigurations::DatabaseConfig` object instead.
|
459
|
+
MSG
|
460
|
+
|
461
|
+
filename = if ActiveRecord::Base.configurations.primary?(db_config_or_name)
|
462
|
+
"schema_cache.yml"
|
463
|
+
else
|
464
|
+
"#{db_config_or_name}_schema_cache.yml"
|
465
|
+
end
|
446
466
|
|
447
|
-
|
467
|
+
schema_cache_path || schema_cache_env || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, filename)
|
468
|
+
end
|
448
469
|
end
|
449
470
|
|
450
471
|
def load_schema_current(format = ActiveRecord.schema_format, file = nil, environment = env)
|
@@ -476,29 +497,29 @@ module ActiveRecord
|
|
476
497
|
# Dumps the schema cache in YAML format for the connection into the file
|
477
498
|
#
|
478
499
|
# ==== Examples
|
479
|
-
# ActiveRecord::Tasks::DatabaseTasks.dump_schema_cache(ActiveRecord::Base.
|
480
|
-
def dump_schema_cache(
|
481
|
-
|
500
|
+
# ActiveRecord::Tasks::DatabaseTasks.dump_schema_cache(ActiveRecord::Base.lease_connection, "tmp/schema_dump.yaml")
|
501
|
+
def dump_schema_cache(conn_or_pool, filename)
|
502
|
+
conn_or_pool.schema_cache.dump_to(filename)
|
482
503
|
end
|
483
504
|
|
484
505
|
def clear_schema_cache(filename)
|
485
506
|
FileUtils.rm_f filename, verbose: false
|
486
507
|
end
|
487
508
|
|
488
|
-
def
|
509
|
+
def with_temporary_pool_for_each(env: ActiveRecord::Tasks::DatabaseTasks.env, name: nil, clobber: false, &block) # :nodoc:
|
489
510
|
if name
|
490
511
|
db_config = ActiveRecord::Base.configurations.configs_for(env_name: env, name: name)
|
491
|
-
|
512
|
+
with_temporary_pool(db_config, clobber: clobber, &block)
|
492
513
|
else
|
493
514
|
ActiveRecord::Base.configurations.configs_for(env_name: env, name: name).each do |db_config|
|
494
|
-
|
515
|
+
with_temporary_pool(db_config, clobber: clobber, &block)
|
495
516
|
end
|
496
517
|
end
|
497
518
|
end
|
498
519
|
|
499
|
-
def with_temporary_connection(db_config, clobber: false) # :nodoc:
|
520
|
+
def with_temporary_connection(db_config, clobber: false, &block) # :nodoc:
|
500
521
|
with_temporary_pool(db_config, clobber: clobber) do |pool|
|
501
|
-
|
522
|
+
pool.with_connection(&block)
|
502
523
|
end
|
503
524
|
end
|
504
525
|
|
@@ -507,10 +528,25 @@ module ActiveRecord
|
|
507
528
|
end
|
508
529
|
|
509
530
|
def migration_connection # :nodoc:
|
510
|
-
migration_class.
|
531
|
+
migration_class.lease_connection
|
532
|
+
end
|
533
|
+
|
534
|
+
def migration_connection_pool # :nodoc:
|
535
|
+
migration_class.connection_pool
|
511
536
|
end
|
512
537
|
|
513
538
|
private
|
539
|
+
def schema_cache_env
|
540
|
+
if ENV["SCHEMA_CACHE"]
|
541
|
+
ActiveRecord.deprecator.warn(<<~MSG.squish)
|
542
|
+
Setting `ENV["SCHEMA_CACHE"]` is deprecated and will be removed in Rails 8.0.
|
543
|
+
Configure the `:schema_cache_path` in the database configuration instead.
|
544
|
+
MSG
|
545
|
+
|
546
|
+
nil
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
514
550
|
def with_temporary_pool(db_config, clobber: false)
|
515
551
|
original_db_config = migration_class.connection_db_config
|
516
552
|
pool = migration_class.connection_handler.establish_connection(db_config, clobber: clobber)
|
@@ -552,10 +588,7 @@ module ActiveRecord
|
|
552
588
|
end
|
553
589
|
|
554
590
|
def each_current_configuration(environment, name = nil)
|
555
|
-
|
556
|
-
environments << "test" if environment == "development" && !ENV["SKIP_TEST_DATABASE"] && !ENV["DATABASE_URL"]
|
557
|
-
|
558
|
-
environments.each do |env|
|
591
|
+
each_current_environment(environment) do |env|
|
559
592
|
configs_for(env_name: env).each do |db_config|
|
560
593
|
next if name && name != db_config.name
|
561
594
|
|
@@ -564,6 +597,12 @@ module ActiveRecord
|
|
564
597
|
end
|
565
598
|
end
|
566
599
|
|
600
|
+
def each_current_environment(environment, &block)
|
601
|
+
environments = [environment]
|
602
|
+
environments << "test" if environment == "development" && !ENV["SKIP_TEST_DATABASE"] && !ENV["DATABASE_URL"]
|
603
|
+
environments.each(&block)
|
604
|
+
end
|
605
|
+
|
567
606
|
def each_local_configuration
|
568
607
|
configs_for.each do |db_config|
|
569
608
|
next unless db_config.database
|
@@ -603,11 +642,11 @@ module ActiveRecord
|
|
603
642
|
|
604
643
|
def check_current_protected_environment!(db_config)
|
605
644
|
with_temporary_pool(db_config) do |pool|
|
606
|
-
|
607
|
-
current =
|
608
|
-
stored =
|
645
|
+
migration_context = pool.migration_context
|
646
|
+
current = migration_context.current_environment
|
647
|
+
stored = migration_context.last_stored_environment
|
609
648
|
|
610
|
-
if
|
649
|
+
if migration_context.protected_environment?
|
611
650
|
raise ActiveRecord::ProtectedEnvironmentError.new(stored)
|
612
651
|
end
|
613
652
|
|
@@ -66,11 +66,12 @@ module ActiveRecord
|
|
66
66
|
attr_reader :db_config, :root
|
67
67
|
|
68
68
|
def connection
|
69
|
-
ActiveRecord::Base.
|
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
|
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
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
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
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
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
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
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
|
-
|
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
|
-
@
|
148
|
-
@
|
149
|
-
|
150
|
-
|
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
|
-
|
160
|
-
|
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
|
-
|
169
|
-
|
170
|
-
|
171
|
-
@
|
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
|
-
|
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
|
-
@
|
193
|
-
|
194
|
-
|
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
|
-
@
|
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(
|
275
|
-
if
|
276
|
-
|
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?(
|
283
|
-
if include_private && fixture_sets.key?(name
|
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
|