activerecord 8.0.0 → 8.1.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 (202) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1381 -241
  3. data/README.rdoc +2 -2
  4. data/lib/active_record/association_relation.rb +1 -1
  5. data/lib/active_record/associations/alias_tracker.rb +6 -4
  6. data/lib/active_record/associations/association.rb +35 -17
  7. data/lib/active_record/associations/association_scope.rb +1 -1
  8. data/lib/active_record/associations/belongs_to_association.rb +20 -4
  9. data/lib/active_record/associations/builder/association.rb +16 -5
  10. data/lib/active_record/associations/builder/belongs_to.rb +36 -8
  11. data/lib/active_record/associations/builder/collection_association.rb +7 -3
  12. data/lib/active_record/associations/builder/has_one.rb +1 -1
  13. data/lib/active_record/associations/builder/singular_association.rb +33 -5
  14. data/lib/active_record/associations/collection_association.rb +9 -5
  15. data/lib/active_record/associations/collection_proxy.rb +26 -4
  16. data/lib/active_record/associations/deprecation.rb +88 -0
  17. data/lib/active_record/associations/errors.rb +3 -0
  18. data/lib/active_record/associations/foreign_association.rb +3 -1
  19. data/lib/active_record/associations/has_one_association.rb +1 -0
  20. data/lib/active_record/associations/join_dependency/join_association.rb +25 -27
  21. data/lib/active_record/associations/join_dependency.rb +5 -4
  22. data/lib/active_record/associations/preloader/association.rb +1 -1
  23. data/lib/active_record/associations/preloader/batch.rb +7 -1
  24. data/lib/active_record/associations/preloader/branch.rb +1 -0
  25. data/lib/active_record/associations.rb +159 -21
  26. data/lib/active_record/attribute_methods/primary_key.rb +2 -1
  27. data/lib/active_record/attribute_methods/query.rb +34 -0
  28. data/lib/active_record/attribute_methods/serialization.rb +19 -6
  29. data/lib/active_record/attribute_methods/time_zone_conversion.rb +10 -2
  30. data/lib/active_record/attribute_methods.rb +24 -17
  31. data/lib/active_record/attributes.rb +40 -26
  32. data/lib/active_record/autosave_association.rb +22 -12
  33. data/lib/active_record/base.rb +3 -4
  34. data/lib/active_record/callbacks.rb +1 -2
  35. data/lib/active_record/coders/column_serializer.rb +1 -1
  36. data/lib/active_record/coders/json.rb +16 -6
  37. data/lib/active_record/coders/yaml_column.rb +2 -2
  38. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +19 -18
  39. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -3
  40. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +68 -12
  41. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +474 -112
  42. data/lib/active_record/connection_adapters/abstract/database_statements.rb +68 -41
  43. data/lib/active_record/connection_adapters/abstract/query_cache.rb +36 -9
  44. data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -24
  45. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +7 -2
  46. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +31 -35
  47. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
  48. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +94 -28
  49. data/lib/active_record/connection_adapters/abstract/transaction.rb +25 -3
  50. data/lib/active_record/connection_adapters/abstract_adapter.rb +166 -64
  51. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +133 -40
  52. data/lib/active_record/connection_adapters/column.rb +14 -4
  53. data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
  54. data/lib/active_record/connection_adapters/mysql/quoting.rb +7 -1
  55. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
  56. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +42 -5
  57. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +40 -25
  58. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +66 -15
  59. data/lib/active_record/connection_adapters/mysql2_adapter.rb +9 -3
  60. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  61. data/lib/active_record/connection_adapters/postgresql/column.rb +8 -2
  62. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +29 -33
  63. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -2
  64. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +46 -3
  65. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
  66. data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
  67. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  68. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +8 -6
  69. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -33
  70. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +70 -32
  71. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +99 -54
  72. data/lib/active_record/connection_adapters/postgresql_adapter.rb +40 -12
  73. data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
  74. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +39 -23
  75. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -8
  76. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +15 -3
  77. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +7 -14
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +68 -37
  79. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  80. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +8 -3
  81. data/lib/active_record/connection_adapters/trilogy_adapter.rb +16 -2
  82. data/lib/active_record/connection_adapters.rb +1 -0
  83. data/lib/active_record/connection_handling.rb +15 -10
  84. data/lib/active_record/core.rb +44 -12
  85. data/lib/active_record/counter_cache.rb +36 -9
  86. data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
  87. data/lib/active_record/database_configurations/database_config.rb +5 -1
  88. data/lib/active_record/database_configurations/hash_config.rb +66 -9
  89. data/lib/active_record/database_configurations/url_config.rb +15 -3
  90. data/lib/active_record/database_configurations.rb +7 -3
  91. data/lib/active_record/delegated_type.rb +19 -19
  92. data/lib/active_record/disable_joins_association_relation.rb +5 -1
  93. data/lib/active_record/dynamic_matchers.rb +54 -69
  94. data/lib/active_record/encryption/encryptable_record.rb +5 -5
  95. data/lib/active_record/encryption/encrypted_attribute_type.rb +2 -2
  96. data/lib/active_record/encryption/encrypted_fixtures.rb +12 -7
  97. data/lib/active_record/encryption/encryptor.rb +39 -25
  98. data/lib/active_record/encryption/scheme.rb +1 -1
  99. data/lib/active_record/enum.rb +37 -20
  100. data/lib/active_record/errors.rb +23 -7
  101. data/lib/active_record/explain.rb +1 -1
  102. data/lib/active_record/explain_registry.rb +51 -2
  103. data/lib/active_record/filter_attribute_handler.rb +76 -0
  104. data/lib/active_record/fixture_set/table_row.rb +19 -2
  105. data/lib/active_record/fixtures.rb +2 -2
  106. data/lib/active_record/future_result.rb +5 -3
  107. data/lib/active_record/gem_version.rb +2 -2
  108. data/lib/active_record/inheritance.rb +9 -4
  109. data/lib/active_record/insert_all.rb +14 -9
  110. data/lib/active_record/locking/optimistic.rb +7 -0
  111. data/lib/active_record/locking/pessimistic.rb +5 -0
  112. data/lib/active_record/log_subscriber.rb +30 -22
  113. data/lib/active_record/message_pack.rb +13 -0
  114. data/lib/active_record/middleware/shard_selector.rb +34 -17
  115. data/lib/active_record/migration/command_recorder.rb +32 -6
  116. data/lib/active_record/migration/compatibility.rb +34 -24
  117. data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
  118. data/lib/active_record/migration.rb +31 -21
  119. data/lib/active_record/model_schema.rb +38 -12
  120. data/lib/active_record/nested_attributes.rb +10 -6
  121. data/lib/active_record/persistence.rb +35 -3
  122. data/lib/active_record/query_cache.rb +22 -15
  123. data/lib/active_record/query_logs.rb +7 -7
  124. data/lib/active_record/querying.rb +17 -5
  125. data/lib/active_record/railtie.rb +35 -6
  126. data/lib/active_record/railties/controller_runtime.rb +11 -6
  127. data/lib/active_record/railties/databases.rake +24 -20
  128. data/lib/active_record/railties/job_checkpoints.rb +15 -0
  129. data/lib/active_record/railties/job_runtime.rb +10 -11
  130. data/lib/active_record/reflection.rb +36 -1
  131. data/lib/active_record/relation/batches.rb +25 -11
  132. data/lib/active_record/relation/calculations.rb +62 -41
  133. data/lib/active_record/relation/delegation.rb +0 -1
  134. data/lib/active_record/relation/finder_methods.rb +76 -45
  135. data/lib/active_record/relation/merger.rb +4 -2
  136. data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
  137. data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -9
  138. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +7 -7
  139. data/lib/active_record/relation/predicate_builder.rb +10 -8
  140. data/lib/active_record/relation/query_attribute.rb +4 -2
  141. data/lib/active_record/relation/query_methods.rb +106 -45
  142. data/lib/active_record/relation/spawn_methods.rb +6 -6
  143. data/lib/active_record/relation/where_clause.rb +10 -11
  144. data/lib/active_record/relation.rb +63 -31
  145. data/lib/active_record/result.rb +44 -21
  146. data/lib/active_record/runtime_registry.rb +42 -58
  147. data/lib/active_record/sanitization.rb +2 -0
  148. data/lib/active_record/schema.rb +1 -1
  149. data/lib/active_record/schema_dumper.rb +41 -21
  150. data/lib/active_record/scoping.rb +0 -1
  151. data/lib/active_record/secure_token.rb +3 -3
  152. data/lib/active_record/signed_id.rb +48 -19
  153. data/lib/active_record/statement_cache.rb +16 -12
  154. data/lib/active_record/store.rb +46 -19
  155. data/lib/active_record/structured_event_subscriber.rb +85 -0
  156. data/lib/active_record/table_metadata.rb +5 -20
  157. data/lib/active_record/tasks/abstract_tasks.rb +83 -0
  158. data/lib/active_record/tasks/database_tasks.rb +44 -45
  159. data/lib/active_record/tasks/mysql_database_tasks.rb +4 -56
  160. data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
  161. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -26
  162. data/lib/active_record/test_databases.rb +18 -4
  163. data/lib/active_record/test_fixtures.rb +40 -6
  164. data/lib/active_record/testing/query_assertions.rb +8 -2
  165. data/lib/active_record/timestamp.rb +4 -2
  166. data/lib/active_record/transaction.rb +2 -5
  167. data/lib/active_record/transactions.rb +58 -32
  168. data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
  169. data/lib/active_record/type/internal/timezone.rb +7 -0
  170. data/lib/active_record/type/json.rb +13 -2
  171. data/lib/active_record/type/serialized.rb +16 -4
  172. data/lib/active_record/type/type_map.rb +1 -1
  173. data/lib/active_record/type_caster/connection.rb +2 -1
  174. data/lib/active_record/validations/associated.rb +1 -1
  175. data/lib/active_record.rb +72 -7
  176. data/lib/arel/alias_predication.rb +2 -0
  177. data/lib/arel/collectors/bind.rb +1 -1
  178. data/lib/arel/collectors/sql_string.rb +1 -1
  179. data/lib/arel/collectors/substitute_binds.rb +2 -2
  180. data/lib/arel/crud.rb +8 -11
  181. data/lib/arel/delete_manager.rb +5 -0
  182. data/lib/arel/nodes/binary.rb +1 -1
  183. data/lib/arel/nodes/count.rb +2 -2
  184. data/lib/arel/nodes/delete_statement.rb +4 -2
  185. data/lib/arel/nodes/function.rb +4 -10
  186. data/lib/arel/nodes/named_function.rb +2 -2
  187. data/lib/arel/nodes/node.rb +2 -2
  188. data/lib/arel/nodes/sql_literal.rb +1 -1
  189. data/lib/arel/nodes/update_statement.rb +4 -2
  190. data/lib/arel/nodes.rb +0 -2
  191. data/lib/arel/predications.rb +1 -3
  192. data/lib/arel/select_manager.rb +13 -4
  193. data/lib/arel/update_manager.rb +5 -0
  194. data/lib/arel/visitors/dot.rb +2 -3
  195. data/lib/arel/visitors/postgresql.rb +55 -0
  196. data/lib/arel/visitors/sqlite.rb +55 -8
  197. data/lib/arel/visitors/to_sql.rb +7 -23
  198. data/lib/arel.rb +3 -1
  199. data/lib/rails/generators/active_record/application_record/USAGE +1 -1
  200. metadata +16 -15
  201. data/lib/active_record/explain_subscriber.rb +0 -34
  202. data/lib/active_record/normalization.rb +0 -163
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_record"
4
3
  require "rails"
4
+ require "active_record"
5
5
  require "active_support/core_ext/object/try"
6
6
  require "active_model/railtie"
7
7
 
@@ -23,8 +23,8 @@ module ActiveRecord
23
23
  config.action_dispatch.rescue_responses.merge!(
24
24
  "ActiveRecord::RecordNotFound" => :not_found,
25
25
  "ActiveRecord::StaleObjectError" => :conflict,
26
- "ActiveRecord::RecordInvalid" => :unprocessable_entity,
27
- "ActiveRecord::RecordNotSaved" => :unprocessable_entity
26
+ "ActiveRecord::RecordInvalid" => ActionDispatch::Constants::UNPROCESSABLE_CONTENT,
27
+ "ActiveRecord::RecordNotSaved" => ActionDispatch::Constants::UNPROCESSABLE_CONTENT
28
28
  )
29
29
 
30
30
  config.active_record.use_schema_cache_dump = true
@@ -35,9 +35,12 @@ module ActiveRecord
35
35
  config.active_record.query_log_tags = [ :application ]
36
36
  config.active_record.query_log_tags_format = :legacy
37
37
  config.active_record.cache_query_log_tags = false
38
+ config.active_record.query_log_tags_prepend_comment = false
38
39
  config.active_record.raise_on_assign_to_attr_readonly = false
39
40
  config.active_record.belongs_to_required_validates_foreign_key = true
40
41
  config.active_record.generate_secure_token_on = :create
42
+ config.active_record.use_legacy_signed_id_verifier = :generate_and_verify
43
+ config.active_record.deprecated_associations_options = { mode: :warn, backtrace: false }
41
44
 
42
45
  config.active_record.queues = ActiveSupport::InheritableOptions.new
43
46
 
@@ -229,10 +232,12 @@ To keep using the current cache store, you can turn off cache versioning entirel
229
232
  :query_log_tags,
230
233
  :query_log_tags_format,
231
234
  :cache_query_log_tags,
235
+ :query_log_tags_prepend_comment,
232
236
  :sqlite3_adapter_strict_strings_by_default,
233
237
  :check_schema_cache_dump_version,
234
238
  :use_schema_cache_dump,
235
239
  :postgresql_adapter_decode_dates,
240
+ :use_legacy_signed_id_verifier,
236
241
  )
237
242
 
238
243
  configs_used_in_other_initializers.each do |k, v|
@@ -274,6 +279,13 @@ To keep using the current cache store, you can turn off cache versioning entirel
274
279
  end
275
280
  end
276
281
 
282
+ initializer "active_record.job_checkpoints" do
283
+ require "active_record/railties/job_checkpoints"
284
+ ActiveSupport.on_load(:active_job_continuable) do
285
+ prepend ActiveRecord::Railties::JobCheckpoints
286
+ end
287
+ end
288
+
277
289
  initializer "active_record.set_reloader_hooks" do
278
290
  ActiveSupport.on_load(:active_record) do
279
291
  ActiveSupport::Reloader.before_class_unload do
@@ -319,9 +331,22 @@ To keep using the current cache store, you can turn off cache versioning entirel
319
331
  end
320
332
  end
321
333
 
322
- initializer "active_record.set_signed_id_verifier_secret" do
323
- ActiveSupport.on_load(:active_record) do
324
- self.signed_id_verifier_secret ||= -> { Rails.application.key_generator.generate_key("active_record/signed_id") }
334
+ initializer "active_record.filter_attributes_as_log_parameters" do |app|
335
+ ActiveRecord::FilterAttributeHandler.new(app).enable
336
+ end
337
+
338
+ initializer "active_record.configure_message_verifiers" do |app|
339
+ ActiveRecord.message_verifiers = app.message_verifiers
340
+
341
+ use_legacy_signed_id_verifier = app.config.active_record.use_legacy_signed_id_verifier
342
+ legacy_options = { digest: "SHA256", serializer: JSON, url_safe: true }
343
+
344
+ if use_legacy_signed_id_verifier == :generate_and_verify
345
+ app.message_verifiers.prepend { |salt| legacy_options if salt == "active_record/signed_id" }
346
+ elsif use_legacy_signed_id_verifier == :verify
347
+ app.message_verifiers.rotate { |salt| legacy_options if salt == "active_record/signed_id" }
348
+ elsif use_legacy_signed_id_verifier
349
+ raise ArgumentError, "Unrecognized value for config.active_record.use_legacy_signed_id_verifier: #{use_legacy_signed_id_verifier.inspect}"
325
350
  end
326
351
  end
327
352
 
@@ -387,6 +412,10 @@ To keep using the current cache store, you can turn off cache versioning entirel
387
412
  if app.config.active_record.cache_query_log_tags
388
413
  ActiveRecord::QueryLogs.cache_query_log_tags = true
389
414
  end
415
+
416
+ if app.config.active_record.query_log_tags_prepend_comment
417
+ ActiveRecord::QueryLogs.prepend_comment = true
418
+ end
390
419
  end
391
420
  end
392
421
  end
@@ -41,11 +41,14 @@ module ActiveRecord
41
41
 
42
42
  def cleanup_view_runtime
43
43
  if logger && logger.info?
44
- db_rt_before_render = ActiveRecord::RuntimeRegistry.reset_runtimes
44
+ runtime_stats = ActiveRecord::RuntimeRegistry.stats
45
+ db_rt_before_render = runtime_stats.reset_runtimes
45
46
  self.db_runtime = (db_runtime || 0) + db_rt_before_render
47
+
46
48
  runtime = super
47
- queries_rt = ActiveRecord::RuntimeRegistry.sql_runtime - ActiveRecord::RuntimeRegistry.async_sql_runtime
48
- db_rt_after_render = ActiveRecord::RuntimeRegistry.reset_runtimes
49
+
50
+ queries_rt = runtime_stats.sql_runtime - runtime_stats.async_sql_runtime
51
+ db_rt_after_render = runtime_stats.reset_runtimes
49
52
  self.db_runtime += db_rt_after_render
50
53
  runtime - queries_rt
51
54
  else
@@ -56,9 +59,11 @@ module ActiveRecord
56
59
  def append_info_to_payload(payload)
57
60
  super
58
61
 
59
- payload[:db_runtime] = (db_runtime || 0) + ActiveRecord::RuntimeRegistry.reset_runtimes
60
- payload[:queries_count] = ActiveRecord::RuntimeRegistry.reset_queries_count
61
- payload[:cached_queries_count] = ActiveRecord::RuntimeRegistry.reset_cached_queries_count
62
+ runtime_stats = ActiveRecord::RuntimeRegistry.stats
63
+ payload[:db_runtime] = (db_runtime || 0) + runtime_stats.sql_runtime
64
+ payload[:queries_count] = runtime_stats.queries_count
65
+ payload[:cached_queries_count] = runtime_stats.cached_queries_count
66
+ runtime_stats.reset
62
67
  end
63
68
  end
64
69
  end
@@ -160,9 +160,21 @@ db_namespace = namespace :db do
160
160
  end
161
161
  end
162
162
 
163
- # desc 'Resets your database using your migrations for the current environment'
163
+ desc "Resets your database using your migrations for the current environment"
164
164
  task reset: ["db:drop", "db:create", "db:schema:dump", "db:migrate"]
165
165
 
166
+ namespace :reset do
167
+ ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |name|
168
+ desc "Drop and recreate the #{name} database using migrations"
169
+ task name => :load_config do
170
+ db_namespace["drop:#{name}"].invoke
171
+ db_namespace["create:#{name}"].invoke
172
+ db_namespace["schema:dump:#{name}"].invoke
173
+ db_namespace["migrate:#{name}"].invoke
174
+ end
175
+ end
176
+ end
177
+
166
178
  desc 'Run the "up" for a given migration VERSION.'
167
179
  task up: :load_config do
168
180
  ActiveRecord::Tasks::DatabaseTasks.raise_for_multi_db(command: "db:migrate:up")
@@ -333,7 +345,7 @@ db_namespace = namespace :db do
333
345
  pending_migrations << pool.migration_context.open.pending_migrations
334
346
  end
335
347
 
336
- pending_migrations = pending_migrations.flatten!
348
+ pending_migrations.flatten!
337
349
 
338
350
  if pending_migrations.any?
339
351
  puts "You have #{pending_migrations.size} pending #{pending_migrations.size > 1 ? 'migrations:' : 'migration:'}"
@@ -447,28 +459,23 @@ db_namespace = namespace :db do
447
459
  namespace :schema do
448
460
  desc "Create a database schema file (either db/schema.rb or db/structure.sql, depending on `ENV['SCHEMA_FORMAT']` or `config.active_record.schema_format`)"
449
461
  task dump: :load_config do
450
- ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each do |pool|
451
- db_config = pool.db_config
452
- schema_format = ENV.fetch("SCHEMA_FORMAT", ActiveRecord.schema_format).to_sym
453
- ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config, schema_format)
454
- end
462
+ ActiveRecord::Tasks::DatabaseTasks.dump_all
455
463
 
456
464
  db_namespace["schema:dump"].reenable
457
465
  end
458
466
 
459
467
  desc "Load a database schema file (either db/schema.rb or db/structure.sql, depending on `ENV['SCHEMA_FORMAT']` or `config.active_record.schema_format`) into the database"
460
468
  task load: [:load_config, :check_protected_environments] do
461
- ActiveRecord::Tasks::DatabaseTasks.load_schema_current(ActiveRecord.schema_format, ENV["SCHEMA"])
469
+ ActiveRecord::Tasks::DatabaseTasks.load_schema_current(ENV["SCHEMA_FORMAT"], ENV["SCHEMA"])
462
470
  end
463
471
 
464
472
  namespace :dump do
465
473
  ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |name|
466
- desc "Create a database schema file (either db/schema.rb or db/structure.sql, depending on `ENV['SCHEMA_FORMAT']` or `config.active_record.schema_format`) for #{name} database"
474
+ desc "Create a database schema file (either db/schema.rb or db/structure.sql, depending on configuration) for #{name} database"
467
475
  task name => :load_config do
468
476
  ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(name: name) do |pool|
469
477
  db_config = pool.db_config
470
- schema_format = ENV.fetch("SCHEMA_FORMAT", ActiveRecord.schema_format).to_sym
471
- ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config, schema_format)
478
+ ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config, ENV["SCHEMA_FORMAT"] || db_config.schema_format)
472
479
  end
473
480
 
474
481
  db_namespace["schema:dump:#{name}"].reenable
@@ -478,12 +485,11 @@ db_namespace = namespace :db do
478
485
 
479
486
  namespace :load do
480
487
  ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |name|
481
- desc "Load a database schema file (either db/schema.rb or db/structure.sql, depending on `ENV['SCHEMA_FORMAT']` or `config.active_record.schema_format`) into the #{name} database"
482
- task name => "db:test:purge:#{name}" do
488
+ desc "Load a database schema file (either db/schema.rb or db/structure.sql, depending on configuration) into the #{name} database"
489
+ task name => [:load_config, :check_protected_environments] do
483
490
  ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(name: name) do |pool|
484
491
  db_config = pool.db_config
485
- schema_format = ENV.fetch("SCHEMA_FORMAT", ActiveRecord.schema_format).to_sym
486
- ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config, schema_format)
492
+ ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config, ENV["SCHEMA_FORMAT"] || db_config.schema_format)
487
493
  end
488
494
  end
489
495
  end
@@ -527,13 +533,12 @@ db_namespace = namespace :db do
527
533
  end
528
534
 
529
535
  namespace :test do
530
- # desc "Recreate the test database from an existent schema file (schema.rb or structure.sql, depending on `ENV['SCHEMA_FORMAT']` or `config.active_record.schema_format`)"
536
+ # desc "Recreate the test database from an existent schema file (schema.rb or structure.sql, depending on configuration)"
531
537
  task load_schema: %w(db:test:purge) do
532
538
  ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(env: "test") do |pool|
533
539
  db_config = pool.db_config
534
540
  ActiveRecord::Schema.verbose = false
535
- schema_format = ENV.fetch("SCHEMA_FORMAT", ActiveRecord.schema_format).to_sym
536
- ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config, schema_format)
541
+ ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config, ENV["SCHEMA_FORMAT"] || db_config.schema_format)
537
542
  end
538
543
  end
539
544
 
@@ -558,8 +563,7 @@ db_namespace = namespace :db do
558
563
  ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(env: "test", name: name) do |pool|
559
564
  db_config = pool.db_config
560
565
  ActiveRecord::Schema.verbose = false
561
- schema_format = ENV.fetch("SCHEMA_FORMAT", ActiveRecord.schema_format).to_sym
562
- ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config, schema_format)
566
+ ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config, ENV["SCHEMA_FORMAT"] || db_config.schema_format)
563
567
  end
564
568
  end
565
569
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Railties # :nodoc:
5
+ module JobCheckpoints # :nodoc:
6
+ def checkpoint!
7
+ if ActiveRecord.all_open_transactions.any?
8
+ raise ActiveJob::Continuation::CheckpointError, "Cannot checkpoint job with open transactions"
9
+ else
10
+ super
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -5,19 +5,18 @@ require "active_record/runtime_registry"
5
5
  module ActiveRecord
6
6
  module Railties # :nodoc:
7
7
  module JobRuntime # :nodoc:
8
- private
9
- def instrument(operation, payload = {}, &block)
10
- if operation == :perform && block
11
- super(operation, payload) do
12
- db_runtime_before_perform = ActiveRecord::RuntimeRegistry.sql_runtime
13
- result = block.call
14
- payload[:db_runtime] = ActiveRecord::RuntimeRegistry.sql_runtime - db_runtime_before_perform
15
- result
16
- end
17
- else
18
- super
8
+ def instrument(operation, payload = {}, &block) # :nodoc:
9
+ if operation == :perform && block
10
+ super(operation, payload) do
11
+ db_runtime_before_perform = ActiveRecord::RuntimeRegistry.stats.sql_runtime
12
+ result = block.call
13
+ payload[:db_runtime] = ActiveRecord::RuntimeRegistry.stats.sql_runtime - db_runtime_before_perform
14
+ result
19
15
  end
16
+ else
17
+ super
20
18
  end
19
+ end
21
20
  end
22
21
  end
23
22
  end
@@ -250,7 +250,7 @@ module ActiveRecord
250
250
  counter_cache[:column] || -"#{active_record.name.demodulize.underscore.pluralize}_count"
251
251
  end
252
252
  else
253
- -((counter_cache && -counter_cache[:column]) || "#{name}_count")
253
+ -((counter_cache && counter_cache[:column]) || "#{name}_count")
254
254
  end
255
255
  end
256
256
  end
@@ -516,6 +516,8 @@ module ActiveRecord
516
516
 
517
517
  def initialize(name, scope, options, active_record)
518
518
  super
519
+
520
+ @validated = false
519
521
  @type = -(options[:foreign_type]&.to_s || "#{options[:as]}_type") if options[:as]
520
522
  @foreign_type = -(options[:foreign_type]&.to_s || "#{name}_type") if options[:polymorphic]
521
523
  @join_table = nil
@@ -534,6 +536,8 @@ module ActiveRecord
534
536
  options[:query_constraints] = options.delete(:foreign_key)
535
537
  end
536
538
 
539
+ @deprecated = !!options[:deprecated]
540
+
537
541
  ensure_option_not_given_as_class!(:class_name)
538
542
  end
539
543
 
@@ -616,6 +620,8 @@ module ActiveRecord
616
620
  end
617
621
 
618
622
  def check_validity!
623
+ return if @validated
624
+
619
625
  check_validity_of_inverse!
620
626
 
621
627
  if !polymorphic? && (klass.composite_primary_key? || active_record.composite_primary_key?)
@@ -625,6 +631,8 @@ module ActiveRecord
625
631
  raise CompositePrimaryKeyMismatchError.new(self)
626
632
  end
627
633
  end
634
+
635
+ @validated = true
628
636
  end
629
637
 
630
638
  def check_eager_loadable!
@@ -742,6 +750,10 @@ module ActiveRecord
742
750
  Array(options[:extend])
743
751
  end
744
752
 
753
+ def deprecated?
754
+ @deprecated
755
+ end
756
+
745
757
  private
746
758
  # Attempts to find the inverse association name automatically.
747
759
  # If it cannot find a suitable inverse association name, it returns
@@ -975,6 +987,8 @@ module ActiveRecord
975
987
 
976
988
  def initialize(delegate_reflection)
977
989
  super()
990
+
991
+ @validated = false
978
992
  @delegate_reflection = delegate_reflection
979
993
  @klass = delegate_reflection.options[:anonymous_class]
980
994
  @source_reflection_name = delegate_reflection.options[:source]
@@ -1138,6 +1152,8 @@ module ActiveRecord
1138
1152
  end
1139
1153
 
1140
1154
  def check_validity!
1155
+ return if @validated
1156
+
1141
1157
  if through_reflection.nil?
1142
1158
  raise HasManyThroughAssociationNotFoundError.new(active_record, self)
1143
1159
  end
@@ -1175,6 +1191,8 @@ module ActiveRecord
1175
1191
  end
1176
1192
 
1177
1193
  check_validity_of_inverse!
1194
+
1195
+ @validated = true
1178
1196
  end
1179
1197
 
1180
1198
  def constraints
@@ -1195,6 +1213,10 @@ module ActiveRecord
1195
1213
  collect_join_reflections(seed + [self])
1196
1214
  end
1197
1215
 
1216
+ def deprecated_nested_reflections
1217
+ @deprecated_nested_reflections ||= collect_deprecated_nested_reflections
1218
+ end
1219
+
1198
1220
  protected
1199
1221
  def actual_source_reflection # FIXME: this is a horrible name
1200
1222
  source_reflection.actual_source_reflection
@@ -1219,6 +1241,19 @@ module ActiveRecord
1219
1241
  options[:source_type] || source_reflection.class_name
1220
1242
  end
1221
1243
 
1244
+ def collect_deprecated_nested_reflections
1245
+ result = []
1246
+ [through_reflection, source_reflection].each do |reflection|
1247
+ result << reflection if reflection.deprecated?
1248
+ # Both the through and the source reflections could be through
1249
+ # themselves. Nesting can go an arbitrary number of levels down.
1250
+ if reflection.through_reflection?
1251
+ result.concat(reflection.deprecated_nested_reflections)
1252
+ end
1253
+ end
1254
+ result
1255
+ end
1256
+
1222
1257
  delegate_methods = AssociationReflection.public_instance_methods -
1223
1258
  public_instance_methods
1224
1259
 
@@ -435,35 +435,49 @@ module ActiveRecord
435
435
  if load
436
436
  records = batch_relation.records
437
437
  values = records.pluck(*cursor)
438
- yielded_relation = where(cursor => values)
438
+ values_size = values.size
439
+ values_last = values.last
440
+ yielded_relation = rewhere(cursor => values)
439
441
  yielded_relation.load_records(records)
440
442
  elsif (empty_scope && use_ranges != false) || use_ranges
441
- values = batch_relation.pluck(*cursor)
443
+ # Efficiently peak at the last value for the next batch using offset and limit.
444
+ values_size = remaining ? [batch_limit, remaining].min : batch_limit
445
+ values_last = batch_relation.offset(values_size - 1).pick(*cursor)
446
+
447
+ # If the last value is not found using offset, there is at most one more batch of size < batch_limit.
448
+ # Retry by getting the whole list of remaining values so that we have the exact size and last value.
449
+ unless values_last
450
+ values = batch_relation.pluck(*cursor)
451
+ values_size = values.size
452
+ values_last = values.last
453
+ end
442
454
 
443
- finish = values.last
444
- if finish
445
- yielded_relation = apply_finish_limit(batch_relation, cursor, finish, batch_orders)
455
+ # Finally, build the yielded relation if at least one value found.
456
+ if values_last
457
+ yielded_relation = apply_finish_limit(batch_relation, cursor, values_last, batch_orders)
446
458
  yielded_relation = yielded_relation.except(:limit, :order)
447
459
  yielded_relation.skip_query_cache!(false)
448
460
  end
449
461
  else
450
462
  values = batch_relation.pluck(*cursor)
451
- yielded_relation = where(cursor => values)
463
+ values_size = values.size
464
+ values_last = values.last
465
+ yielded_relation = rewhere(cursor => values)
452
466
  end
453
467
 
454
- break if values.empty?
468
+ break if values_size == 0
455
469
 
456
- if values.flatten.any?(nil)
470
+ if [values_last].flatten.any?(nil)
457
471
  raise ArgumentError, "Not all of the batch cursor columns were included in the custom select clause "\
458
472
  "or some columns contain nil."
459
473
  end
460
474
 
461
475
  yield yielded_relation
462
476
 
463
- break if values.length < batch_limit
477
+ break if values_size < batch_limit
464
478
 
465
479
  if limit_value
466
- remaining -= values.length
480
+ remaining -= values_size
467
481
 
468
482
  if remaining == 0
469
483
  # Saves a useless iteration when the limit is a multiple of the
@@ -481,7 +495,7 @@ module ActiveRecord
481
495
  end
482
496
  operators << (last_order == :desc ? :lt : :gt)
483
497
 
484
- cursor_value = values.last
498
+ cursor_value = values_last
485
499
  batch_relation = batch_condition(relation, cursor, cursor_value, operators)
486
500
  end
487
501