activerecord 7.2.2.1 → 8.0.5

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 (156) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +523 -677
  3. data/README.rdoc +2 -2
  4. data/lib/active_record/association_relation.rb +1 -0
  5. data/lib/active_record/associations/alias_tracker.rb +6 -4
  6. data/lib/active_record/associations/association.rb +34 -10
  7. data/lib/active_record/associations/belongs_to_association.rb +18 -2
  8. data/lib/active_record/associations/builder/association.rb +7 -6
  9. data/lib/active_record/associations/collection_association.rb +10 -8
  10. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  11. data/lib/active_record/associations/has_many_through_association.rb +3 -2
  12. data/lib/active_record/associations/join_dependency/join_association.rb +25 -27
  13. data/lib/active_record/associations/join_dependency.rb +2 -2
  14. data/lib/active_record/associations/preloader/association.rb +2 -2
  15. data/lib/active_record/associations/preloader/batch.rb +7 -1
  16. data/lib/active_record/associations/singular_association.rb +8 -3
  17. data/lib/active_record/associations.rb +34 -4
  18. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  19. data/lib/active_record/attribute_methods/primary_key.rb +4 -8
  20. data/lib/active_record/attribute_methods/query.rb +34 -0
  21. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  22. data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -12
  23. data/lib/active_record/attribute_methods.rb +24 -19
  24. data/lib/active_record/attributes.rb +37 -26
  25. data/lib/active_record/autosave_association.rb +91 -39
  26. data/lib/active_record/base.rb +2 -2
  27. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +34 -25
  28. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
  29. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +0 -1
  30. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +55 -47
  31. data/lib/active_record/connection_adapters/abstract/database_statements.rb +91 -44
  32. data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -8
  33. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
  34. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
  35. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +12 -3
  36. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +40 -10
  37. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
  38. data/lib/active_record/connection_adapters/abstract_adapter.rb +92 -70
  39. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +35 -46
  40. data/lib/active_record/connection_adapters/mysql/quoting.rb +7 -9
  41. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
  42. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +50 -45
  43. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +84 -94
  44. data/lib/active_record/connection_adapters/mysql2_adapter.rb +9 -9
  45. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  46. data/lib/active_record/connection_adapters/postgresql/column.rb +4 -2
  47. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +72 -43
  48. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  49. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  50. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  51. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
  52. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -24
  53. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +3 -2
  54. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +60 -17
  55. data/lib/active_record/connection_adapters/postgresql_adapter.rb +61 -99
  56. data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
  57. data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
  58. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +80 -100
  59. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -0
  60. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
  61. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +27 -2
  62. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +10 -1
  63. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +63 -17
  64. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  65. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +37 -67
  66. data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -18
  67. data/lib/active_record/connection_adapters.rb +0 -56
  68. data/lib/active_record/connection_handling.rb +35 -9
  69. data/lib/active_record/core.rb +57 -22
  70. data/lib/active_record/counter_cache.rb +1 -1
  71. data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
  72. data/lib/active_record/database_configurations/database_config.rb +4 -0
  73. data/lib/active_record/database_configurations/hash_config.rb +16 -2
  74. data/lib/active_record/delegated_type.rb +18 -18
  75. data/lib/active_record/encryption/config.rb +3 -1
  76. data/lib/active_record/encryption/encryptable_record.rb +5 -5
  77. data/lib/active_record/encryption/encrypted_attribute_type.rb +11 -2
  78. data/lib/active_record/encryption/encryptor.rb +37 -28
  79. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  80. data/lib/active_record/encryption/scheme.rb +8 -1
  81. data/lib/active_record/enum.rb +22 -34
  82. data/lib/active_record/errors.rb +16 -8
  83. data/lib/active_record/fixture_set/table_row.rb +19 -2
  84. data/lib/active_record/fixtures.rb +0 -2
  85. data/lib/active_record/future_result.rb +15 -9
  86. data/lib/active_record/gem_version.rb +4 -4
  87. data/lib/active_record/insert_all.rb +3 -3
  88. data/lib/active_record/locking/optimistic.rb +1 -1
  89. data/lib/active_record/log_subscriber.rb +5 -11
  90. data/lib/active_record/migration/command_recorder.rb +32 -12
  91. data/lib/active_record/migration/compatibility.rb +5 -2
  92. data/lib/active_record/migration.rb +40 -43
  93. data/lib/active_record/model_schema.rb +3 -4
  94. data/lib/active_record/nested_attributes.rb +4 -6
  95. data/lib/active_record/persistence.rb +128 -130
  96. data/lib/active_record/query_logs.rb +106 -50
  97. data/lib/active_record/query_logs_formatter.rb +17 -28
  98. data/lib/active_record/querying.rb +12 -12
  99. data/lib/active_record/railtie.rb +4 -28
  100. data/lib/active_record/railties/databases.rake +11 -34
  101. data/lib/active_record/reflection.rb +18 -21
  102. data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
  103. data/lib/active_record/relation/batches.rb +132 -72
  104. data/lib/active_record/relation/calculations.rb +71 -65
  105. data/lib/active_record/relation/delegation.rb +25 -14
  106. data/lib/active_record/relation/finder_methods.rb +32 -31
  107. data/lib/active_record/relation/merger.rb +8 -8
  108. data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
  109. data/lib/active_record/relation/predicate_builder/association_query_value.rb +2 -0
  110. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
  111. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  112. data/lib/active_record/relation/predicate_builder.rb +13 -0
  113. data/lib/active_record/relation/query_attribute.rb +1 -1
  114. data/lib/active_record/relation/query_methods.rb +121 -70
  115. data/lib/active_record/relation/spawn_methods.rb +7 -7
  116. data/lib/active_record/relation/where_clause.rb +9 -3
  117. data/lib/active_record/relation.rb +95 -67
  118. data/lib/active_record/result.rb +66 -4
  119. data/lib/active_record/sanitization.rb +7 -6
  120. data/lib/active_record/schema_dumper.rb +34 -11
  121. data/lib/active_record/schema_migration.rb +2 -1
  122. data/lib/active_record/scoping/named.rb +5 -2
  123. data/lib/active_record/secure_token.rb +3 -3
  124. data/lib/active_record/signed_id.rb +7 -6
  125. data/lib/active_record/statement_cache.rb +14 -14
  126. data/lib/active_record/store.rb +7 -3
  127. data/lib/active_record/table_metadata.rb +1 -3
  128. data/lib/active_record/tasks/database_tasks.rb +69 -60
  129. data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
  130. data/lib/active_record/tasks/postgresql_database_tasks.rb +9 -1
  131. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
  132. data/lib/active_record/test_databases.rb +1 -1
  133. data/lib/active_record/test_fixtures.rb +12 -0
  134. data/lib/active_record/token_for.rb +1 -1
  135. data/lib/active_record/transactions.rb +8 -7
  136. data/lib/active_record/type/serialized.rb +5 -0
  137. data/lib/active_record/validations/uniqueness.rb +8 -8
  138. data/lib/active_record.rb +22 -49
  139. data/lib/arel/collectors/bind.rb +2 -2
  140. data/lib/arel/collectors/sql_string.rb +1 -1
  141. data/lib/arel/collectors/substitute_binds.rb +2 -2
  142. data/lib/arel/crud.rb +2 -0
  143. data/lib/arel/delete_manager.rb +5 -0
  144. data/lib/arel/nodes/binary.rb +1 -1
  145. data/lib/arel/nodes/delete_statement.rb +4 -2
  146. data/lib/arel/nodes/node.rb +1 -1
  147. data/lib/arel/nodes/sql_literal.rb +1 -1
  148. data/lib/arel/nodes/update_statement.rb +4 -2
  149. data/lib/arel/predications.rb +1 -3
  150. data/lib/arel/select_manager.rb +6 -2
  151. data/lib/arel/table.rb +3 -7
  152. data/lib/arel/update_manager.rb +5 -0
  153. data/lib/arel/visitors/dot.rb +2 -0
  154. data/lib/arel/visitors/to_sql.rb +3 -1
  155. metadata +11 -15
  156. data/lib/active_record/relation/record_fetch_warning.rb +0 -52
@@ -34,7 +34,8 @@ module ActiveRecord
34
34
  end
35
35
 
36
36
  def delete_all_versions
37
- @pool.with_connection do |connection|
37
+ # Eagerly check in connection to avoid checking in/out many times in the called method.
38
+ @pool.with_connection do
38
39
  versions.each do |version|
39
40
  delete_version(version)
40
41
  end
@@ -23,7 +23,7 @@ module ActiveRecord
23
23
  scope = current_scope
24
24
 
25
25
  if scope
26
- if self == scope.klass
26
+ if self == scope.model
27
27
  scope.clone
28
28
  else
29
29
  relation.merge!(scope)
@@ -191,7 +191,10 @@ module ActiveRecord
191
191
  private
192
192
  def singleton_method_added(name)
193
193
  super
194
- generate_relation_method(name) if Kernel.respond_to?(name) && !ActiveRecord::Relation.method_defined?(name)
194
+ # Most Kernel extends are both singleton and instance methods so
195
+ # respond_to is a fast check, but we don't want to define methods
196
+ # only on the module (ex. Module#name)
197
+ generate_relation_method(name) if Kernel.respond_to?(name) && (Kernel.method_defined?(name) || Kernel.private_method_defined?(name)) && !ActiveRecord::Relation.method_defined?(name)
195
198
  end
196
199
  end
197
200
  end
@@ -30,13 +30,13 @@ module ActiveRecord
30
30
  # {validates_uniqueness_of}[rdoc-ref:Validations::ClassMethods#validates_uniqueness_of] can.
31
31
  # You're encouraged to add a unique index in the database to deal with this even more unlikely scenario.
32
32
  #
33
- # === Options
33
+ # ==== Options
34
34
  #
35
- # [:length]
35
+ # [+:length+]
36
36
  # Length of the Secure Random, with a minimum of 24 characters. It will
37
37
  # default to 24.
38
38
  #
39
- # [:on]
39
+ # [+:on+]
40
40
  # The callback when the value is generated. When called with <tt>on:
41
41
  # :initialize</tt>, the value is generated in an
42
42
  # <tt>after_initialize</tt> callback, otherwise the value will be used
@@ -57,12 +57,12 @@ module ActiveRecord
57
57
  end
58
58
  end
59
59
 
60
- # Works like find_signed, but will raise an +ActiveSupport::MessageVerifier::InvalidSignature+
60
+ # Works like find_signed, but will raise an ActiveSupport::MessageVerifier::InvalidSignature
61
61
  # exception if the +signed_id+ has either expired, has a purpose mismatch, is for another record,
62
- # or has been tampered with. It will also raise an +ActiveRecord::RecordNotFound+ exception if
62
+ # or has been tampered with. It will also raise an ActiveRecord::RecordNotFound exception if
63
63
  # the valid signed id can't find a record.
64
64
  #
65
- # === Examples
65
+ # ==== Examples
66
66
  #
67
67
  # User.find_signed! "bad data" # => ActiveSupport::MessageVerifier::InvalidSignature
68
68
  #
@@ -76,8 +76,9 @@ module ActiveRecord
76
76
  end
77
77
 
78
78
  # The verifier instance that all signed ids are generated and verified from. By default, it'll be initialized
79
- # with the class-level +signed_id_verifier_secret+, which within \Rails comes from the
80
- # Rails.application.key_generator. By default, it's SHA256 for the digest and JSON for the serialization.
79
+ # with the class-level +signed_id_verifier_secret+, which within Rails comes from
80
+ # {Rails.application.key_generator}[rdoc-ref:Rails::Application#key_generator].
81
+ # By default, it's SHA256 for the digest and JSON for the serialization.
81
82
  def signed_id_verifier
82
83
  @signed_id_verifier ||= begin
83
84
  secret = signed_id_verifier_secret
@@ -93,7 +94,7 @@ module ActiveRecord
93
94
 
94
95
  # Allows you to pass in a custom verifier used for the signed ids. This also allows you to use different
95
96
  # verifiers for different classes. This is also helpful if you need to rotate keys, as you can prepare
96
- # your custom verifier for that in advance. See +ActiveSupport::MessageVerifier+ for details.
97
+ # your custom verifier for that in advance. See ActiveSupport::MessageVerifier for details.
97
98
  def signed_id_verifier=(verifier)
98
99
  @signed_id_verifier = verifier
99
100
  end
@@ -74,13 +74,13 @@ module ActiveRecord
74
74
  self
75
75
  end
76
76
 
77
- def add_bind(obj)
77
+ def add_bind(obj, &)
78
78
  @binds << obj
79
79
  @parts << Substitute.new
80
80
  self
81
81
  end
82
82
 
83
- def add_binds(binds, proc_for_binds = nil)
83
+ def add_binds(binds, proc_for_binds = nil, &)
84
84
  @binds.concat proc_for_binds ? binds.map(&proc_for_binds) : binds
85
85
  binds.size.times do |i|
86
86
  @parts << ", " unless i == 0
@@ -133,23 +133,26 @@ module ActiveRecord
133
133
  relation = (callable || block).call Params.new
134
134
  query_builder, binds = connection.cacheable_query(self, relation.arel)
135
135
  bind_map = BindMap.new(binds)
136
- new(query_builder, bind_map, relation.klass)
136
+ new(query_builder, bind_map, relation.model)
137
137
  end
138
138
 
139
- def initialize(query_builder, bind_map, klass)
139
+ def initialize(query_builder, bind_map, model)
140
140
  @query_builder = query_builder
141
141
  @bind_map = bind_map
142
- @klass = klass
142
+ @model = model
143
143
  end
144
144
 
145
- def execute(params, connection, allow_retry: false, &block)
146
- bind_values = bind_map.bind params
145
+ def execute(params, connection, allow_retry: false, async: false, &block)
146
+ bind_values = @bind_map.bind params
147
+ sql = @query_builder.sql_for bind_values, connection
147
148
 
148
- sql = query_builder.sql_for bind_values, connection
149
-
150
- klass.find_by_sql(sql, bind_values, preparable: true, allow_retry: allow_retry, &block)
149
+ if async
150
+ @model.async_find_by_sql(sql, bind_values, preparable: true, allow_retry: allow_retry, &block)
151
+ else
152
+ @model.find_by_sql(sql, bind_values, preparable: true, allow_retry: allow_retry, &block)
153
+ end
151
154
  rescue ::RangeError
152
- []
155
+ async ? Promise.wrap([]) : []
153
156
  end
154
157
 
155
158
  def self.unsupported_value?(value)
@@ -157,8 +160,5 @@ module ActiveRecord
157
160
  when NilClass, Array, Range, Hash, Relation, Base then true
158
161
  end
159
162
  end
160
-
161
- private
162
- attr_reader :query_builder, :bind_map, :klass
163
163
  end
164
164
  end
@@ -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
@@ -132,6 +133,13 @@ module ActiveRecord
132
133
  tempfile = Tempfile.open("uncommented_structure.sql")
133
134
  begin
134
135
  File.foreach(filename) do |line|
136
+ next if line.start_with?("\\restrict ")
137
+
138
+ if line.start_with?("\\unrestrict ")
139
+ removing_comments = true
140
+ next
141
+ end
142
+
135
143
  unless removing_comments && (line.start_with?(SQL_COMMENT_BEGIN) || line.blank?)
136
144
  tempfile << line
137
145
  removing_comments = false
@@ -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
@@ -13,7 +13,7 @@ module ActiveRecord
13
13
  scope: [:kind, :name]
14
14
  end
15
15
 
16
- attr_accessor :_new_record_before_last_commit # :nodoc:
16
+ attr_accessor :_new_record_before_last_commit, :_last_transaction_return_status # :nodoc:
17
17
 
18
18
  # = Active Record \Transactions
19
19
  #
@@ -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
@@ -418,6 +417,7 @@ module ActiveRecord
418
417
  status = yield
419
418
  raise ActiveRecord::Rollback unless status
420
419
  end
420
+ @_last_transaction_return_status = status
421
421
  status
422
422
  end
423
423
  end
@@ -433,6 +433,7 @@ module ActiveRecord
433
433
  def init_internals
434
434
  super
435
435
  @_start_transaction_state = nil
436
+ @_last_transaction_return_status = nil
436
437
  @_committed_already_called = nil
437
438
  @_new_record_before_last_commit = nil
438
439
  end
@@ -58,6 +58,11 @@ module ActiveRecord
58
58
  end
59
59
 
60
60
  private
61
+ # Prevent Ruby 4.0 "delegator does not forward private method" warning.
62
+ # Kernel#inspect calls instance_variables_to_inspect which, without this,
63
+ # triggers Delegator#respond_to_missing? for a private method.
64
+ define_method(:instance_variables_to_inspect, Kernel.instance_method(:instance_variables))
65
+
61
66
  def default_value?(value)
62
67
  value == coder.load(nil)
63
68
  end
@@ -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)