activerecord 7.2.3 → 8.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +391 -958
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/association_relation.rb +1 -0
  5. data/lib/active_record/associations/association.rb +34 -10
  6. data/lib/active_record/associations/builder/association.rb +7 -6
  7. data/lib/active_record/associations/collection_association.rb +1 -1
  8. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  9. data/lib/active_record/associations/has_many_through_association.rb +3 -2
  10. data/lib/active_record/associations/preloader/association.rb +2 -2
  11. data/lib/active_record/associations/singular_association.rb +8 -3
  12. data/lib/active_record/associations.rb +34 -4
  13. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  14. data/lib/active_record/attribute_methods/primary_key.rb +4 -8
  15. data/lib/active_record/attribute_methods/query.rb +34 -0
  16. data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -12
  17. data/lib/active_record/autosave_association.rb +69 -27
  18. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +34 -25
  19. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
  20. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +0 -1
  21. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +6 -15
  22. data/lib/active_record/connection_adapters/abstract/database_statements.rb +90 -43
  23. data/lib/active_record/connection_adapters/abstract/query_cache.rb +8 -2
  24. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
  25. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
  26. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +7 -2
  27. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +34 -7
  28. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
  29. data/lib/active_record/connection_adapters/abstract_adapter.rb +31 -43
  30. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +21 -40
  31. data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
  32. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
  33. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +50 -45
  34. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +84 -94
  35. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -8
  36. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  37. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +72 -43
  38. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  39. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  40. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  41. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
  42. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +6 -12
  43. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +2 -1
  44. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +59 -16
  45. data/lib/active_record/connection_adapters/postgresql_adapter.rb +46 -96
  46. data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
  47. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +80 -100
  48. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
  49. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +13 -0
  50. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +9 -1
  51. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -12
  52. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  53. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +37 -67
  54. data/lib/active_record/connection_adapters/trilogy_adapter.rb +0 -17
  55. data/lib/active_record/connection_adapters.rb +0 -56
  56. data/lib/active_record/connection_handling.rb +23 -1
  57. data/lib/active_record/core.rb +29 -14
  58. data/lib/active_record/database_configurations/database_config.rb +4 -0
  59. data/lib/active_record/database_configurations/hash_config.rb +16 -2
  60. data/lib/active_record/encryption/config.rb +3 -1
  61. data/lib/active_record/encryption/encryptable_record.rb +4 -4
  62. data/lib/active_record/encryption/encrypted_attribute_type.rb +10 -1
  63. data/lib/active_record/encryption/encryptor.rb +16 -8
  64. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  65. data/lib/active_record/encryption/scheme.rb +8 -1
  66. data/lib/active_record/enum.rb +9 -22
  67. data/lib/active_record/errors.rb +13 -5
  68. data/lib/active_record/fixtures.rb +0 -2
  69. data/lib/active_record/future_result.rb +13 -9
  70. data/lib/active_record/gem_version.rb +3 -3
  71. data/lib/active_record/insert_all.rb +1 -1
  72. data/lib/active_record/locking/optimistic.rb +1 -1
  73. data/lib/active_record/log_subscriber.rb +5 -11
  74. data/lib/active_record/migration/command_recorder.rb +31 -11
  75. data/lib/active_record/migration/compatibility.rb +5 -2
  76. data/lib/active_record/migration.rb +38 -42
  77. data/lib/active_record/model_schema.rb +3 -4
  78. data/lib/active_record/nested_attributes.rb +4 -6
  79. data/lib/active_record/persistence.rb +128 -130
  80. data/lib/active_record/query_logs.rb +102 -50
  81. data/lib/active_record/query_logs_formatter.rb +17 -28
  82. data/lib/active_record/querying.rb +8 -8
  83. data/lib/active_record/railtie.rb +2 -26
  84. data/lib/active_record/railties/databases.rake +11 -35
  85. data/lib/active_record/reflection.rb +18 -21
  86. data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
  87. data/lib/active_record/relation/batches.rb +132 -72
  88. data/lib/active_record/relation/calculations.rb +40 -39
  89. data/lib/active_record/relation/delegation.rb +25 -14
  90. data/lib/active_record/relation/finder_methods.rb +18 -18
  91. data/lib/active_record/relation/merger.rb +8 -8
  92. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
  93. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  94. data/lib/active_record/relation/predicate_builder.rb +13 -0
  95. data/lib/active_record/relation/query_methods.rb +105 -61
  96. data/lib/active_record/relation/spawn_methods.rb +7 -7
  97. data/lib/active_record/relation.rb +79 -61
  98. data/lib/active_record/result.rb +66 -4
  99. data/lib/active_record/sanitization.rb +7 -6
  100. data/lib/active_record/schema_dumper.rb +5 -0
  101. data/lib/active_record/schema_migration.rb +2 -1
  102. data/lib/active_record/scoping/named.rb +5 -2
  103. data/lib/active_record/statement_cache.rb +14 -14
  104. data/lib/active_record/store.rb +7 -3
  105. data/lib/active_record/table_metadata.rb +1 -3
  106. data/lib/active_record/tasks/database_tasks.rb +69 -60
  107. data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
  108. data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -1
  109. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
  110. data/lib/active_record/test_databases.rb +1 -1
  111. data/lib/active_record/test_fixtures.rb +12 -0
  112. data/lib/active_record/token_for.rb +1 -1
  113. data/lib/active_record/transactions.rb +5 -6
  114. data/lib/active_record/validations/uniqueness.rb +8 -8
  115. data/lib/active_record.rb +21 -48
  116. data/lib/arel/collectors/bind.rb +2 -2
  117. data/lib/arel/collectors/sql_string.rb +1 -1
  118. data/lib/arel/collectors/substitute_binds.rb +2 -2
  119. data/lib/arel/nodes/binary.rb +1 -1
  120. data/lib/arel/nodes/node.rb +1 -1
  121. data/lib/arel/nodes/sql_literal.rb +1 -1
  122. data/lib/arel/table.rb +3 -7
  123. metadata +9 -10
  124. data/lib/active_record/relation/record_fetch_warning.rb +0 -52
@@ -25,8 +25,8 @@ module ActiveRecord
25
25
  # You can set custom coder to encode/decode your serialized attributes to/from different formats.
26
26
  # JSON, YAML, Marshal are supported out of the box. Generally it can be any wrapper that provides +load+ and +dump+.
27
27
  #
28
- # NOTE: If you are using structured database data types (e.g. PostgreSQL +hstore+/+json+, or MySQL 5.7+
29
- # +json+) there is no need for the serialization provided by {.store}[rdoc-ref:rdoc-ref:ClassMethods#store].
28
+ # NOTE: If you are using structured database data types (e.g. PostgreSQL +hstore+/+json+, MySQL 5.7+
29
+ # +json+, or SQLite 3.38+ +json+) there is no need for the serialization provided by {.store}[rdoc-ref:rdoc-ref:ClassMethods#store].
30
30
  # Simply use {.store_accessor}[rdoc-ref:ClassMethods#store_accessor] instead to generate
31
31
  # the accessor methods. Be aware that these columns use a string keyed hash and do not allow access
32
32
  # using a symbol.
@@ -217,7 +217,11 @@ module ActiveRecord
217
217
  end
218
218
 
219
219
  def store_accessor_for(store_attribute)
220
- type_for_attribute(store_attribute).accessor
220
+ type_for_attribute(store_attribute).tap do |type|
221
+ unless type.respond_to?(:accessor)
222
+ raise ConfigurationError, "the column '#{store_attribute}' has not been configured as a store. Please make sure the column is declared serializable via 'ActiveRecord.store' or, if your database supports it, use a structured column type like hstore or json."
223
+ end
224
+ end.accessor
221
225
  end
222
226
 
223
227
  class HashAccessor # :nodoc:
@@ -69,9 +69,7 @@ module ActiveRecord
69
69
 
70
70
  def predicate_builder
71
71
  if klass
72
- predicate_builder = klass.predicate_builder.dup
73
- predicate_builder.instance_variable_set(:@table, self)
74
- predicate_builder
72
+ klass.predicate_builder.with(self)
75
73
  else
76
74
  PredicateBuilder.new(self)
77
75
  end
@@ -45,7 +45,7 @@ module ActiveRecord
45
45
  # Example:
46
46
  # ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags = {
47
47
  # mysql2: ['--no-defaults', '--skip-add-drop-table'],
48
- # postgres: '--no-tablespaces'
48
+ # postgresql: '--no-tablespaces'
49
49
  # }
50
50
  mattr_accessor :structure_dump_flags, instance_accessor: false
51
51
 
@@ -178,22 +178,9 @@ module ActiveRecord
178
178
  dump_db_configs = []
179
179
 
180
180
  each_current_configuration(env) do |db_config|
181
- with_temporary_pool(db_config) do
182
- begin
183
- database_initialized = migration_connection_pool.schema_migration.table_exists?
184
- rescue ActiveRecord::NoDatabaseError
185
- create(db_config)
186
- retry
187
- end
181
+ database_initialized = initialize_database(db_config)
188
182
 
189
- unless database_initialized
190
- if File.exist?(schema_dump_path(db_config))
191
- load_schema(db_config, ActiveRecord.schema_format, nil)
192
- end
193
-
194
- seed = true
195
- end
196
- end
183
+ seed = true if database_initialized && db_config.seeds?
197
184
  end
198
185
 
199
186
  each_current_environment(env) do |environment|
@@ -253,12 +240,33 @@ module ActiveRecord
253
240
  end
254
241
  end
255
242
 
256
- def migrate(version = nil)
243
+ def migrate_all
244
+ db_configs = ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env)
245
+ db_configs.each { |db_config| initialize_database(db_config) }
246
+
247
+ if db_configs.size == 1 && db_configs.first.primary?
248
+ ActiveRecord::Tasks::DatabaseTasks.migrate(skip_initialize: true)
249
+ else
250
+ mapped_versions = ActiveRecord::Tasks::DatabaseTasks.db_configs_with_versions
251
+
252
+ mapped_versions.sort.each do |version, db_configs|
253
+ db_configs.each do |db_config|
254
+ ActiveRecord::Tasks::DatabaseTasks.with_temporary_connection(db_config) do
255
+ ActiveRecord::Tasks::DatabaseTasks.migrate(version, skip_initialize: true)
256
+ end
257
+ end
258
+ end
259
+ end
260
+ end
261
+
262
+ def migrate(version = nil, skip_initialize: false)
257
263
  scope = ENV["SCOPE"]
258
264
  verbose_was, Migration.verbose = Migration.verbose, verbose?
259
265
 
260
266
  check_target_version
261
267
 
268
+ initialize_database(migration_connection_pool.db_config) unless skip_initialize
269
+
262
270
  migration_connection_pool.migration_context.migrate(target_version) do |migration|
263
271
  if version.blank?
264
272
  scope.blank? || scope == migration.scope
@@ -365,7 +373,8 @@ module ActiveRecord
365
373
  database_adapter_for(db_config, *arguments).structure_load(filename, flags)
366
374
  end
367
375
 
368
- def load_schema(db_config, format = ActiveRecord.schema_format, file = nil) # :nodoc:
376
+ def load_schema(db_config, format = db_config.schema_format, file = nil) # :nodoc:
377
+ format = format.to_sym
369
378
  file ||= schema_dump_path(db_config, format)
370
379
  return unless file
371
380
 
@@ -386,7 +395,7 @@ module ActiveRecord
386
395
  Migration.verbose = verbose_was
387
396
  end
388
397
 
389
- def schema_up_to_date?(configuration, format = ActiveRecord.schema_format, file = nil)
398
+ def schema_up_to_date?(configuration, _ = nil, file = nil)
390
399
  db_config = resolve_configuration(configuration)
391
400
 
392
401
  file ||= schema_dump_path(db_config)
@@ -402,25 +411,32 @@ module ActiveRecord
402
411
  end
403
412
  end
404
413
 
405
- def reconstruct_from_schema(db_config, format = ActiveRecord.schema_format, file = nil) # :nodoc:
406
- file ||= schema_dump_path(db_config, format)
414
+ def reconstruct_from_schema(db_config, file = nil) # :nodoc:
415
+ file ||= schema_dump_path(db_config, db_config.schema_format)
407
416
 
408
417
  check_schema_file(file) if file
409
418
 
410
419
  with_temporary_pool(db_config, clobber: true) do
411
- if schema_up_to_date?(db_config, format, file)
420
+ if schema_up_to_date?(db_config, nil, file)
412
421
  truncate_tables(db_config) unless ENV["SKIP_TEST_DATABASE_TRUNCATE"]
413
422
  else
414
423
  purge(db_config)
415
- load_schema(db_config, format, file)
424
+ load_schema(db_config, db_config.schema_format, file)
416
425
  end
417
426
  rescue ActiveRecord::NoDatabaseError
418
427
  create(db_config)
419
- load_schema(db_config, format, file)
428
+ load_schema(db_config, db_config.schema_format, file)
420
429
  end
421
430
  end
422
431
 
423
- def dump_schema(db_config, format = ActiveRecord.schema_format) # :nodoc:
432
+ def dump_all
433
+ with_temporary_pool_for_each do |pool|
434
+ db_config = pool.db_config
435
+ ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config, ENV["SCHEMA_FORMAT"] || db_config.schema_format)
436
+ end
437
+ end
438
+
439
+ def dump_schema(db_config, format = db_config.schema_format) # :nodoc:
424
440
  return unless db_config.schema_dump
425
441
 
426
442
  require "active_record/schema_dumper"
@@ -428,7 +444,7 @@ module ActiveRecord
428
444
  return unless filename
429
445
 
430
446
  FileUtils.mkdir_p(db_dir)
431
- case format
447
+ case format.to_sym
432
448
  when :ruby
433
449
  File.open(filename, "w:utf-8") do |file|
434
450
  ActiveRecord::SchemaDumper.dump(migration_connection_pool, file)
@@ -444,7 +460,7 @@ module ActiveRecord
444
460
  end
445
461
  end
446
462
 
447
- def schema_dump_path(db_config, format = ActiveRecord.schema_format)
463
+ def schema_dump_path(db_config, format = db_config.schema_format)
448
464
  return ENV["SCHEMA"] if ENV["SCHEMA"]
449
465
 
450
466
  filename = db_config.schema_dump(format)
@@ -457,32 +473,16 @@ module ActiveRecord
457
473
  end
458
474
  end
459
475
 
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)
466
- else
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
471
-
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
476
+ def cache_dump_filename(db_config, schema_cache_path: nil)
477
+ schema_cache_path ||
478
+ db_config.schema_cache_path ||
479
+ db_config.default_schema_cache_path(ActiveRecord::Tasks::DatabaseTasks.db_dir)
480
480
  end
481
481
 
482
- def load_schema_current(format = ActiveRecord.schema_format, file = nil, environment = env)
482
+ def load_schema_current(format = nil, file = nil, environment = env)
483
483
  each_current_configuration(environment) do |db_config|
484
484
  with_temporary_connection(db_config) do
485
- load_schema(db_config, format, file)
485
+ load_schema(db_config, format || db_config.schema_format, file)
486
486
  end
487
487
  end
488
488
  end
@@ -547,17 +547,6 @@ module ActiveRecord
547
547
  end
548
548
 
549
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
-
561
550
  def with_temporary_pool(db_config, clobber: false)
562
551
  original_db_config = migration_class.connection_db_config
563
552
  pool = migration_class.connection_handler.establish_connection(db_config, clobber: clobber)
@@ -667,6 +656,26 @@ module ActiveRecord
667
656
  rescue ActiveRecord::NoDatabaseError
668
657
  end
669
658
  end
659
+
660
+ def initialize_database(db_config)
661
+ with_temporary_pool(db_config) do
662
+ begin
663
+ database_already_initialized = migration_connection_pool.schema_migration.table_exists?
664
+ rescue ActiveRecord::NoDatabaseError
665
+ create(db_config)
666
+ retry
667
+ end
668
+
669
+ unless database_already_initialized
670
+ schema_dump_path = schema_dump_path(db_config)
671
+ if schema_dump_path && File.exist?(schema_dump_path)
672
+ load_schema(db_config)
673
+ end
674
+ end
675
+
676
+ !database_already_initialized
677
+ end
678
+ end
670
679
  end
671
680
  end
672
681
  end
@@ -3,8 +3,6 @@
3
3
  module ActiveRecord
4
4
  module Tasks # :nodoc:
5
5
  class MySQLDatabaseTasks # :nodoc:
6
- ER_DB_CREATE_EXISTS = 1007
7
-
8
6
  def self.using_database_configurations?
9
7
  true
10
8
  end
@@ -78,8 +78,9 @@ module ActiveRecord
78
78
  end
79
79
 
80
80
  def structure_load(filename, extra_flags)
81
- args = ["--set", ON_ERROR_STOP_1, "--quiet", "--no-psqlrc", "--output", File::NULL, "--file", filename]
81
+ args = ["--set", ON_ERROR_STOP_1, "--quiet", "--no-psqlrc", "--output", File::NULL]
82
82
  args.concat(Array(extra_flags)) if extra_flags
83
+ args.concat(["--file", filename])
83
84
  args << db_config.database
84
85
  run_cmd("psql", args, "loading")
85
86
  end
@@ -50,9 +50,9 @@ module ActiveRecord
50
50
  if ignore_tables.any?
51
51
  ignore_tables = connection.data_sources.select { |table| ignore_tables.any? { |pattern| pattern === table } }
52
52
  condition = ignore_tables.map { |table| connection.quote(table) }.join(", ")
53
- args << "SELECT sql FROM sqlite_master WHERE tbl_name NOT IN (#{condition}) ORDER BY tbl_name, type DESC, name"
53
+ args << "SELECT sql || ';' FROM sqlite_master WHERE tbl_name NOT IN (#{condition}) ORDER BY tbl_name, type DESC, name"
54
54
  else
55
- args << ".schema"
55
+ args << ".schema --nosys"
56
56
  end
57
57
  run_cmd("sqlite3", args, filename)
58
58
  end
@@ -14,7 +14,7 @@ module ActiveRecord
14
14
  ActiveRecord::Base.configurations.configs_for(env_name: env_name).each do |db_config|
15
15
  db_config._database = "#{db_config.database}-#{i}"
16
16
 
17
- ActiveRecord::Tasks::DatabaseTasks.reconstruct_from_schema(db_config, ActiveRecord.schema_format, nil)
17
+ ActiveRecord::Tasks::DatabaseTasks.reconstruct_from_schema(db_config, nil)
18
18
  end
19
19
  ensure
20
20
  ActiveRecord::Base.establish_connection
@@ -137,12 +137,15 @@ module ActiveRecord
137
137
  invalidate_already_loaded_fixtures
138
138
  @loaded_fixtures = load_fixtures(config)
139
139
  end
140
+ setup_asynchronous_queries_session
140
141
 
141
142
  # Instantiate fixtures for every test if requested.
142
143
  instantiate_fixtures if use_instantiated_fixtures
143
144
  end
144
145
 
145
146
  def teardown_fixtures
147
+ teardown_asynchronous_queries_session
148
+
146
149
  # Rollback changes if a transaction is active.
147
150
  if run_in_transaction?
148
151
  teardown_transactional_fixtures
@@ -154,6 +157,14 @@ module ActiveRecord
154
157
  ActiveRecord::Base.connection_handler.clear_active_connections!(:all)
155
158
  end
156
159
 
160
+ def setup_asynchronous_queries_session
161
+ @_async_queries_session = ActiveRecord::Base.asynchronous_queries_tracker.start_session
162
+ end
163
+
164
+ def teardown_asynchronous_queries_session
165
+ ActiveRecord::Base.asynchronous_queries_tracker.finalize_session(true) if @_async_queries_session
166
+ end
167
+
157
168
  def invalidate_already_loaded_fixtures
158
169
  @@already_loaded_fixtures.clear
159
170
  end
@@ -190,6 +201,7 @@ module ActiveRecord
190
201
 
191
202
  def teardown_transactional_fixtures
192
203
  ActiveSupport::Notifications.unsubscribe(@connection_subscriber) if @connection_subscriber
204
+
193
205
  unless @fixture_connection_pools.map(&:unpin_connection!).all?
194
206
  # Something caused the transaction to be committed or rolled back
195
207
  # We can no longer trust the database is in a clean state.
@@ -40,7 +40,7 @@ module ActiveRecord
40
40
  # +nil+ if the token is invalid or the record was not found.
41
41
  def find_by_token_for(purpose, token)
42
42
  raise UnknownPrimaryKey.new(self) unless model.primary_key
43
- model.token_definitions.fetch(purpose).resolve_token(token) { |id| find_by(model.primary_key => id) }
43
+ model.token_definitions.fetch(purpose).resolve_token(token) { |id| find_by(model.primary_key => [id]) }
44
44
  end
45
45
 
46
46
  # Finds a record using a given +token+ for a predefined +purpose+. Raises
@@ -219,12 +219,11 @@ module ActiveRecord
219
219
  # database error will occur because the savepoint has already been
220
220
  # automatically released. The following example demonstrates the problem:
221
221
  #
222
- # Model.lease_connection.transaction do # BEGIN
223
- # Model.lease_connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
224
- # Model.lease_connection.create_table(...) # active_record_1 now automatically released
225
- # end # RELEASE SAVEPOINT active_record_1
226
- # # ^^^^ BOOM! database error!
227
- # end
222
+ # Model.transaction do # BEGIN
223
+ # Model.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
224
+ # Model.lease_connection.create_table(...) # active_record_1 now automatically released
225
+ # end # RELEASE SAVEPOINT active_record_1
226
+ # end # ^^^^ BOOM! database error!
228
227
  #
229
228
  # Note that "TRUNCATE" is also a MySQL DDL statement!
230
229
  module ClassMethods
@@ -54,17 +54,17 @@ module ActiveRecord
54
54
  private
55
55
  # The check for an existing value should be run from a class that
56
56
  # isn't abstract. This means working down from the current class
57
- # (self), to the first non-abstract class. Since classes don't know
58
- # their subclasses, we have to build the hierarchy between self and
59
- # the record's class.
57
+ # (self), to the first non-abstract class.
60
58
  def find_finder_class_for(record)
61
- class_hierarchy = [record.class]
62
-
63
- while class_hierarchy.first != @klass
64
- class_hierarchy.unshift(class_hierarchy.first.superclass)
59
+ current_class = record.class
60
+ found_class = nil
61
+ loop do
62
+ found_class = current_class unless current_class.abstract_class?
63
+ break if current_class == @klass
64
+ current_class = current_class.superclass
65
65
  end
66
66
 
67
- class_hierarchy.detect { |klass| !klass.abstract_class? }
67
+ found_class
68
68
  end
69
69
 
70
70
  def validation_needed?(klass, record, attribute)
data/lib/active_record.rb CHANGED
@@ -29,6 +29,7 @@ require "active_support/ordered_options"
29
29
  require "active_model"
30
30
  require "arel"
31
31
  require "yaml"
32
+ require "zlib"
32
33
 
33
34
  require "active_record/version"
34
35
  require "active_record/deprecator"
@@ -196,6 +197,20 @@ module ActiveRecord
196
197
  singleton_class.attr_accessor :schema_cache_ignored_tables
197
198
  self.schema_cache_ignored_tables = []
198
199
 
200
+ # Checks to see if the +table_name+ is ignored by checking
201
+ # against the +schema_cache_ignored_tables+ option.
202
+ #
203
+ # ActiveRecord.schema_cache_ignored_table?(:developers)
204
+ #
205
+ def self.schema_cache_ignored_table?(table_name)
206
+ ActiveRecord.schema_cache_ignored_tables.any? do |ignored|
207
+ ignored === table_name
208
+ end
209
+ end
210
+
211
+ singleton_class.attr_accessor :database_cli
212
+ self.database_cli = { postgresql: "psql", mysql: %w[mysql mysql5], sqlite: "sqlite3" }
213
+
199
214
  singleton_class.attr_reader :default_timezone
200
215
 
201
216
  # Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling
@@ -253,14 +268,6 @@ module ActiveRecord
253
268
  singleton_class.attr_accessor :reading_role
254
269
  self.reading_role = :reading
255
270
 
256
- def self.legacy_connection_handling=(_)
257
- raise ArgumentError, <<~MSG.squish
258
- The `legacy_connection_handling` setter was deprecated in 7.0 and removed in 7.1,
259
- but is still defined in your configuration. Please remove this call as it no longer
260
- has any effect."
261
- MSG
262
- end
263
-
264
271
  ##
265
272
  # :singleton-method: async_query_executor
266
273
  # Sets the async_query_executor for an application. By default the thread pool executor
@@ -344,29 +351,6 @@ module ActiveRecord
344
351
  singleton_class.attr_accessor :run_after_transaction_callbacks_in_order_defined
345
352
  self.run_after_transaction_callbacks_in_order_defined = false
346
353
 
347
- def self.commit_transaction_on_non_local_return
348
- ActiveRecord.deprecator.warn <<-WARNING.squish
349
- `Rails.application.config.active_record.commit_transaction_on_non_local_return`
350
- is deprecated and will be removed in Rails 8.0.
351
- WARNING
352
- end
353
-
354
- def self.commit_transaction_on_non_local_return=(value)
355
- ActiveRecord.deprecator.warn <<-WARNING.squish
356
- `Rails.application.config.active_record.commit_transaction_on_non_local_return`
357
- is deprecated and will be removed in Rails 8.0.
358
- WARNING
359
- end
360
-
361
- ##
362
- # :singleton-method: warn_on_records_fetched_greater_than
363
- # Specify a threshold for the size of query result sets. If the number of
364
- # records in the set exceeds the threshold, a warning is logged. This can
365
- # be used to identify queries which load thousands of records and
366
- # potentially cause memory bloat.
367
- singleton_class.attr_accessor :warn_on_records_fetched_greater_than
368
- self.warn_on_records_fetched_greater_than = false
369
-
370
354
  singleton_class.attr_accessor :application_record_class
371
355
  self.application_record_class = nil
372
356
 
@@ -384,7 +368,8 @@ module ActiveRecord
384
368
  # specific) SQL statements. If :ruby, the schema is dumped as an
385
369
  # ActiveRecord::Schema file which can be loaded into any database that
386
370
  # supports migrations. Use :ruby if you want to have different database
387
- # adapters for, e.g., your development and test environments.
371
+ # adapters for, e.g., your development and test environments. This can be
372
+ # overridden per-database in the database configuration.
388
373
  singleton_class.attr_accessor :schema_format
389
374
  self.schema_format = :ruby
390
375
 
@@ -444,20 +429,6 @@ module ActiveRecord
444
429
  singleton_class.attr_accessor :verify_foreign_keys_for_fixtures
445
430
  self.verify_foreign_keys_for_fixtures = false
446
431
 
447
- def self.allow_deprecated_singular_associations_name
448
- ActiveRecord.deprecator.warn <<-WARNING.squish
449
- `Rails.application.config.active_record.allow_deprecated_singular_associations_name`
450
- is deprecated and will be removed in Rails 8.0.
451
- WARNING
452
- end
453
-
454
- def self.allow_deprecated_singular_associations_name=(value)
455
- ActiveRecord.deprecator.warn <<-WARNING.squish
456
- `Rails.application.config.active_record.allow_deprecated_singular_associations_name`
457
- is deprecated and will be removed in Rails 8.0.
458
- WARNING
459
- end
460
-
461
432
  singleton_class.attr_accessor :query_transformers
462
433
  self.query_transformers = []
463
434
 
@@ -578,8 +549,10 @@ module ActiveRecord
578
549
  open_transactions = []
579
550
  Base.connection_handler.each_connection_pool do |pool|
580
551
  if active_connection = pool.active_connection
581
- if active_connection.current_transaction.open? && active_connection.current_transaction.joinable?
582
- open_transactions << active_connection.current_transaction
552
+ current_transaction = active_connection.current_transaction
553
+
554
+ if current_transaction.open? && current_transaction.joinable? && !current_transaction.state.invalidated?
555
+ open_transactions << current_transaction
583
556
  end
584
557
  end
585
558
  end
@@ -13,12 +13,12 @@ module Arel # :nodoc: all
13
13
  self
14
14
  end
15
15
 
16
- def add_bind(bind, &block)
16
+ def add_bind(bind, &)
17
17
  @binds << bind
18
18
  self
19
19
  end
20
20
 
21
- def add_binds(binds, proc_for_binds = nil)
21
+ def add_binds(binds, proc_for_binds = nil, &)
22
22
  @binds.concat proc_for_binds ? binds.map(&proc_for_binds) : binds
23
23
  self
24
24
  end
@@ -12,7 +12,7 @@ module Arel # :nodoc: all
12
12
  @bind_index = 1
13
13
  end
14
14
 
15
- def add_bind(bind)
15
+ def add_bind(bind, &)
16
16
  self << yield(@bind_index)
17
17
  @bind_index += 1
18
18
  self
@@ -15,12 +15,12 @@ module Arel # :nodoc: all
15
15
  self
16
16
  end
17
17
 
18
- def add_bind(bind)
18
+ def add_bind(bind, &)
19
19
  bind = bind.value_for_database if bind.respond_to?(:value_for_database)
20
20
  self << quoter.quote(bind)
21
21
  end
22
22
 
23
- def add_binds(binds, proc_for_binds = nil)
23
+ def add_binds(binds, proc_for_binds = nil, &)
24
24
  self << binds.map { |bind| quoter.quote(bind) }.join(", ")
25
25
  end
26
26
 
@@ -30,7 +30,7 @@ module Arel # :nodoc: all
30
30
  end
31
31
 
32
32
  module FetchAttribute
33
- def fetch_attribute
33
+ def fetch_attribute(&)
34
34
  if left.is_a?(Arel::Attributes::Attribute)
35
35
  yield left
36
36
  elsif right.is_a?(Arel::Attributes::Attribute)
@@ -152,7 +152,7 @@ module Arel # :nodoc: all
152
152
  end
153
153
  end
154
154
 
155
- def fetch_attribute
155
+ def fetch_attribute(&)
156
156
  end
157
157
 
158
158
  def equality?; false; end
@@ -19,7 +19,7 @@ module Arel # :nodoc: all
19
19
  coder.scalar = self.to_s
20
20
  end
21
21
 
22
- def fetch_attribute
22
+ def fetch_attribute(&)
23
23
  end
24
24
 
25
25
  def +(other)
data/lib/arel/table.rb CHANGED
@@ -12,13 +12,9 @@ module Arel # :nodoc: all
12
12
  attr_reader :table_alias
13
13
 
14
14
  def initialize(name, as: nil, klass: nil, type_caster: klass&.type_caster)
15
- @name =
16
- case name
17
- when Symbol then name.to_s
18
- else
19
- name
20
- end
15
+ name = name.name if name.is_a?(Symbol)
21
16
 
17
+ @name = name
22
18
  @klass = klass
23
19
  @type_caster = type_caster
24
20
 
@@ -84,7 +80,7 @@ module Arel # :nodoc: all
84
80
  end
85
81
 
86
82
  def [](name, table = self)
87
- name = name.to_s if name.is_a?(Symbol)
83
+ name = name.name if name.is_a?(Symbol)
88
84
  name = @klass.attribute_aliases[name] || name if @klass
89
85
  Attribute.new(table, name)
90
86
  end