activerecord 8.0.3 → 8.1.0

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 (160) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +538 -512
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/association_relation.rb +1 -1
  5. data/lib/active_record/associations/association.rb +1 -1
  6. data/lib/active_record/associations/belongs_to_association.rb +2 -0
  7. data/lib/active_record/associations/builder/association.rb +16 -5
  8. data/lib/active_record/associations/builder/belongs_to.rb +17 -4
  9. data/lib/active_record/associations/builder/collection_association.rb +7 -3
  10. data/lib/active_record/associations/builder/has_one.rb +1 -1
  11. data/lib/active_record/associations/builder/singular_association.rb +33 -5
  12. data/lib/active_record/associations/collection_proxy.rb +22 -4
  13. data/lib/active_record/associations/deprecation.rb +88 -0
  14. data/lib/active_record/associations/errors.rb +3 -0
  15. data/lib/active_record/associations/join_dependency.rb +2 -0
  16. data/lib/active_record/associations/preloader/branch.rb +1 -0
  17. data/lib/active_record/associations.rb +159 -21
  18. data/lib/active_record/attribute_methods/serialization.rb +16 -3
  19. data/lib/active_record/attribute_methods/time_zone_conversion.rb +10 -2
  20. data/lib/active_record/attributes.rb +3 -0
  21. data/lib/active_record/autosave_association.rb +1 -1
  22. data/lib/active_record/base.rb +0 -2
  23. data/lib/active_record/coders/json.rb +14 -5
  24. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +1 -3
  25. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -3
  26. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +51 -12
  27. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +405 -72
  28. data/lib/active_record/connection_adapters/abstract/database_statements.rb +55 -40
  29. data/lib/active_record/connection_adapters/abstract/query_cache.rb +19 -3
  30. data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -24
  31. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +7 -2
  32. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +26 -34
  33. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
  34. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +85 -22
  35. data/lib/active_record/connection_adapters/abstract/transaction.rb +25 -3
  36. data/lib/active_record/connection_adapters/abstract_adapter.rb +86 -20
  37. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +43 -13
  38. data/lib/active_record/connection_adapters/column.rb +17 -4
  39. data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
  40. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
  41. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +42 -5
  42. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +26 -4
  43. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +27 -22
  44. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -2
  45. data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
  46. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +17 -15
  47. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -2
  48. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
  49. data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
  50. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +8 -6
  51. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +8 -21
  52. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +67 -31
  53. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +81 -48
  54. data/lib/active_record/connection_adapters/postgresql_adapter.rb +23 -7
  55. data/lib/active_record/connection_adapters/schema_cache.rb +2 -2
  56. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +37 -25
  57. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -8
  58. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -13
  59. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +56 -32
  60. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +4 -3
  61. data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -1
  62. data/lib/active_record/connection_adapters.rb +1 -0
  63. data/lib/active_record/connection_handling.rb +14 -9
  64. data/lib/active_record/core.rb +5 -4
  65. data/lib/active_record/counter_cache.rb +33 -8
  66. data/lib/active_record/database_configurations/database_config.rb +5 -1
  67. data/lib/active_record/database_configurations/hash_config.rb +53 -9
  68. data/lib/active_record/database_configurations/url_config.rb +13 -3
  69. data/lib/active_record/database_configurations.rb +7 -3
  70. data/lib/active_record/delegated_type.rb +1 -1
  71. data/lib/active_record/dynamic_matchers.rb +54 -69
  72. data/lib/active_record/encryption/encryptable_record.rb +4 -4
  73. data/lib/active_record/encryption/encrypted_attribute_type.rb +1 -1
  74. data/lib/active_record/encryption/encryptor.rb +12 -0
  75. data/lib/active_record/encryption/scheme.rb +1 -1
  76. data/lib/active_record/enum.rb +24 -8
  77. data/lib/active_record/errors.rb +20 -4
  78. data/lib/active_record/explain.rb +1 -1
  79. data/lib/active_record/explain_registry.rb +51 -2
  80. data/lib/active_record/filter_attribute_handler.rb +73 -0
  81. data/lib/active_record/fixtures.rb +2 -2
  82. data/lib/active_record/gem_version.rb +2 -2
  83. data/lib/active_record/inheritance.rb +1 -1
  84. data/lib/active_record/insert_all.rb +12 -7
  85. data/lib/active_record/locking/optimistic.rb +7 -0
  86. data/lib/active_record/locking/pessimistic.rb +5 -0
  87. data/lib/active_record/log_subscriber.rb +2 -6
  88. data/lib/active_record/middleware/shard_selector.rb +34 -17
  89. data/lib/active_record/migration/command_recorder.rb +14 -1
  90. data/lib/active_record/migration/compatibility.rb +34 -24
  91. data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
  92. data/lib/active_record/migration.rb +26 -16
  93. data/lib/active_record/model_schema.rb +36 -10
  94. data/lib/active_record/nested_attributes.rb +2 -0
  95. data/lib/active_record/persistence.rb +34 -3
  96. data/lib/active_record/query_cache.rb +22 -15
  97. data/lib/active_record/query_logs.rb +3 -7
  98. data/lib/active_record/railtie.rb +32 -3
  99. data/lib/active_record/railties/controller_runtime.rb +11 -6
  100. data/lib/active_record/railties/databases.rake +15 -3
  101. data/lib/active_record/railties/job_checkpoints.rb +15 -0
  102. data/lib/active_record/railties/job_runtime.rb +10 -11
  103. data/lib/active_record/reflection.rb +35 -0
  104. data/lib/active_record/relation/batches.rb +25 -11
  105. data/lib/active_record/relation/calculations.rb +20 -9
  106. data/lib/active_record/relation/delegation.rb +0 -1
  107. data/lib/active_record/relation/finder_methods.rb +27 -11
  108. data/lib/active_record/relation/predicate_builder/association_query_value.rb +9 -9
  109. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +7 -7
  110. data/lib/active_record/relation/predicate_builder.rb +9 -7
  111. data/lib/active_record/relation/query_attribute.rb +3 -1
  112. data/lib/active_record/relation/query_methods.rb +40 -29
  113. data/lib/active_record/relation/where_clause.rb +1 -8
  114. data/lib/active_record/relation.rb +24 -12
  115. data/lib/active_record/result.rb +44 -21
  116. data/lib/active_record/runtime_registry.rb +41 -58
  117. data/lib/active_record/sanitization.rb +2 -0
  118. data/lib/active_record/schema_dumper.rb +12 -10
  119. data/lib/active_record/scoping.rb +0 -1
  120. data/lib/active_record/signed_id.rb +43 -15
  121. data/lib/active_record/statement_cache.rb +13 -9
  122. data/lib/active_record/store.rb +44 -19
  123. data/lib/active_record/structured_event_subscriber.rb +85 -0
  124. data/lib/active_record/table_metadata.rb +5 -20
  125. data/lib/active_record/tasks/abstract_tasks.rb +76 -0
  126. data/lib/active_record/tasks/database_tasks.rb +25 -34
  127. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -40
  128. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -39
  129. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -26
  130. data/lib/active_record/test_databases.rb +14 -4
  131. data/lib/active_record/test_fixtures.rb +27 -2
  132. data/lib/active_record/testing/query_assertions.rb +8 -2
  133. data/lib/active_record/timestamp.rb +4 -2
  134. data/lib/active_record/transaction.rb +2 -5
  135. data/lib/active_record/transactions.rb +32 -10
  136. data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
  137. data/lib/active_record/type/internal/timezone.rb +7 -0
  138. data/lib/active_record/type/json.rb +15 -2
  139. data/lib/active_record/type/serialized.rb +11 -4
  140. data/lib/active_record/type/type_map.rb +1 -1
  141. data/lib/active_record/type_caster/connection.rb +2 -1
  142. data/lib/active_record/validations/associated.rb +1 -1
  143. data/lib/active_record.rb +65 -3
  144. data/lib/arel/alias_predication.rb +2 -0
  145. data/lib/arel/crud.rb +6 -11
  146. data/lib/arel/nodes/count.rb +2 -2
  147. data/lib/arel/nodes/function.rb +4 -10
  148. data/lib/arel/nodes/named_function.rb +2 -2
  149. data/lib/arel/nodes/node.rb +1 -1
  150. data/lib/arel/nodes.rb +0 -2
  151. data/lib/arel/select_manager.rb +7 -2
  152. data/lib/arel/visitors/dot.rb +0 -3
  153. data/lib/arel/visitors/postgresql.rb +55 -0
  154. data/lib/arel/visitors/sqlite.rb +55 -8
  155. data/lib/arel/visitors/to_sql.rb +3 -21
  156. data/lib/arel.rb +3 -1
  157. data/lib/rails/generators/active_record/application_record/USAGE +1 -1
  158. metadata +14 -10
  159. data/lib/active_record/explain_subscriber.rb +0 -34
  160. data/lib/active_record/normalization.rb +0 -163
@@ -48,7 +48,7 @@ module ActiveRecord
48
48
  # way of creating a namespace for tables in a shared database. By default, the prefix is the
49
49
  # empty string.
50
50
  #
51
- # If you are organising your models within modules you can add a prefix to the models within
51
+ # If you are organizing your models within modules you can add a prefix to the models within
52
52
  # a namespace by defining a singleton method in the parent module called table_name_prefix which
53
53
  # returns your chosen prefix.
54
54
 
@@ -65,7 +65,7 @@ module ActiveRecord
65
65
  # Works like +table_name_prefix=+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
66
66
  # "people_basecamp"). By default, the suffix is the empty string.
67
67
  #
68
- # If you are organising your models within modules, you can add a suffix to the models within
68
+ # If you are organizing your models within modules, you can add a suffix to the models within
69
69
  # a namespace by defining a singleton method in the parent module called table_name_suffix which
70
70
  # returns your chosen suffix.
71
71
 
@@ -113,17 +113,19 @@ module ActiveRecord
113
113
  # :singleton-method: implicit_order_column
114
114
  # :call-seq: implicit_order_column
115
115
  #
116
- # The name of the column records are ordered by if no explicit order clause
116
+ # The name of the column(s) records are ordered by if no explicit order clause
117
117
  # is used during an ordered finder call. If not set the primary key is used.
118
118
 
119
119
  ##
120
120
  # :singleton-method: implicit_order_column=
121
121
  # :call-seq: implicit_order_column=(column_name)
122
122
  #
123
- # Sets the column to sort records by when no explicit order clause is used
124
- # during an ordered finder call. Useful when the primary key is not an
125
- # auto-incrementing integer, for example when it's a UUID. Records are subsorted
126
- # by the primary key if it exists to ensure deterministic results.
123
+ # Sets the column(s) to sort records by when no explicit order clause is used
124
+ # during an ordered finder call. Useful for models where the primary key isn't an
125
+ # auto-incrementing integer (such as UUID).
126
+ #
127
+ # By default, records are subsorted by primary key to ensure deterministic results.
128
+ # To disable this subsort behavior, set `implicit_order_column` to `["column_name", nil]`.
127
129
 
128
130
  ##
129
131
  # :singleton-method: immutable_strings_by_default=
@@ -179,6 +181,7 @@ module ActiveRecord
179
181
  self.protected_environments = ["production"]
180
182
 
181
183
  self.ignored_columns = [].freeze
184
+ self.only_columns = [].freeze
182
185
 
183
186
  delegate :type_for_attribute, :column_for_attribute, to: :class
184
187
 
@@ -332,6 +335,12 @@ module ActiveRecord
332
335
  @ignored_columns || superclass.ignored_columns
333
336
  end
334
337
 
338
+ # The list of columns names the model should allow. Only columns are used to define
339
+ # attribute accessors, and are referenced in SQL queries.
340
+ def only_columns
341
+ @only_columns || superclass.only_columns
342
+ end
343
+
335
344
  # Sets the columns names the model should ignore. Ignored columns won't have attribute
336
345
  # accessors defined, and won't be referenced in SQL queries.
337
346
  #
@@ -364,10 +373,17 @@ module ActiveRecord
364
373
  # user = Project.create!(name: "First Project")
365
374
  # user.category # => raises NoMethodError
366
375
  def ignored_columns=(columns)
376
+ check_model_columns(@only_columns.present?)
367
377
  reload_schema_from_cache
368
378
  @ignored_columns = columns.map(&:to_s).freeze
369
379
  end
370
380
 
381
+ def only_columns=(columns)
382
+ check_model_columns(@ignored_columns.present?)
383
+ reload_schema_from_cache
384
+ @only_columns = columns.map(&:to_s).freeze
385
+ end
386
+
371
387
  def sequence_name
372
388
  if base_class?
373
389
  @sequence_name ||= reset_sequence_name
@@ -501,7 +517,7 @@ module ActiveRecord
501
517
  # when just after creating a table you want to populate it with some default
502
518
  # values, e.g.:
503
519
  #
504
- # class CreateJobLevels < ActiveRecord::Migration[8.0]
520
+ # class CreateJobLevels < ActiveRecord::Migration[8.1]
505
521
  # def up
506
522
  # create_table :job_levels do |t|
507
523
  # t.integer :id
@@ -577,6 +593,7 @@ module ActiveRecord
577
593
  child_class.reload_schema_from_cache(false)
578
594
  child_class.class_eval do
579
595
  @ignored_columns = nil
596
+ @only_columns = nil
580
597
  end
581
598
  end
582
599
 
@@ -590,7 +607,11 @@ module ActiveRecord
590
607
  end
591
608
 
592
609
  columns_hash = schema_cache.columns_hash(table_name)
593
- columns_hash = columns_hash.except(*ignored_columns) unless ignored_columns.empty?
610
+ if only_columns.present?
611
+ columns_hash = columns_hash.slice(*only_columns)
612
+ elsif ignored_columns.present?
613
+ columns_hash = columns_hash.except(*ignored_columns)
614
+ end
594
615
  @columns_hash = columns_hash.freeze
595
616
 
596
617
  _default_attributes # Precompute to cache DB-dependent attribute types
@@ -620,7 +641,8 @@ module ActiveRecord
620
641
  end
621
642
 
622
643
  def type_for_column(connection, column)
623
- type = connection.lookup_cast_type_from_column(column)
644
+ # TODO: Remove the need for a connection after we release 8.1.
645
+ type = column.fetch_cast_type(connection)
624
646
 
625
647
  if immutable_strings_by_default && type.respond_to?(:to_immutable_string)
626
648
  type = type.to_immutable_string
@@ -628,6 +650,10 @@ module ActiveRecord
628
650
 
629
651
  type
630
652
  end
653
+
654
+ def check_model_columns(columns_present)
655
+ raise ArgumentError, "You can not use both only_columns and ignored_columns in the same model." if columns_present
656
+ end
631
657
  end
632
658
  end
633
659
  end
@@ -387,6 +387,8 @@ module ActiveRecord
387
387
  generated_association_methods.module_eval <<-eoruby, __FILE__, __LINE__ + 1
388
388
  silence_redefinition_of_method :#{association_name}_attributes=
389
389
  def #{association_name}_attributes=(attributes)
390
+ association = association(:#{association_name})
391
+ deprecated_associations_api_guard(association, __method__)
390
392
  assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes)
391
393
  end
392
394
  eoruby
@@ -492,6 +492,7 @@ module ActiveRecord
492
492
  becoming.instance_variable_set(:@attributes, @attributes)
493
493
  becoming.instance_variable_set(:@mutations_from_database, @mutations_from_database ||= nil)
494
494
  becoming.instance_variable_set(:@new_record, new_record?)
495
+ becoming.instance_variable_set(:@previously_new_record, previously_new_record?)
495
496
  becoming.instance_variable_set(:@destroyed, destroyed?)
496
497
  becoming.errors.copy!(errors)
497
498
  end
@@ -581,8 +582,8 @@ module ActiveRecord
581
582
  end
582
583
 
583
584
  # Equivalent to <code>update_columns(name => value)</code>.
584
- def update_column(name, value)
585
- update_columns(name => value)
585
+ def update_column(name, value, touch: nil)
586
+ update_columns(name => value, touch: touch)
586
587
  end
587
588
 
588
589
  # Updates the attributes directly in the database issuing an UPDATE SQL
@@ -596,11 +597,25 @@ module ActiveRecord
596
597
  #
597
598
  # * \Validations are skipped.
598
599
  # * \Callbacks are skipped.
599
- # * +updated_at+/+updated_on+ are not updated.
600
+ # * +updated_at+/+updated_on+ are updated if the +touch+ option is set to +true+.
600
601
  # * However, attributes are serialized with the same rules as ActiveRecord::Relation#update_all
601
602
  #
602
603
  # This method raises an ActiveRecord::ActiveRecordError when called on new
603
604
  # objects, or when at least one of the attributes is marked as readonly.
605
+ #
606
+ # ==== Parameters
607
+ #
608
+ # * <tt>:touch</tt> option - Touch the timestamp columns when updating.
609
+ # * If attribute names are passed, they are updated along with +updated_at+/+updated_on+ attributes.
610
+ #
611
+ # ==== Examples
612
+ #
613
+ # # Update a single attribute.
614
+ # user.update_columns(last_request_at: Time.current)
615
+ #
616
+ # # Update with touch option.
617
+ # user.update_columns(last_request_at: Time.current, touch: true)
618
+
604
619
  def update_columns(attributes)
605
620
  raise ActiveRecordError, "cannot update a new record" if new_record?
606
621
  raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
@@ -612,6 +627,15 @@ module ActiveRecord
612
627
  verify_readonly_attribute(name) || name
613
628
  end
614
629
 
630
+ touch = attributes.delete("touch")
631
+ if touch
632
+ names = touch if touch != true
633
+ names = Array.wrap(names)
634
+ options = names.extract_options!
635
+ touch_updates = self.class.touch_attributes_with_time(*names, **options)
636
+ attributes.with_defaults!(touch_updates) unless touch_updates.empty?
637
+ end
638
+
615
639
  update_constraints = _query_constraints_hash
616
640
  attributes = attributes.each_with_object({}) do |(k, v), h|
617
641
  h[k] = @attributes.write_cast_value(k, v)
@@ -640,8 +664,15 @@ module ActiveRecord
640
664
  # This means that any other modified attributes will still be dirty.
641
665
  # Validations and callbacks are skipped. Supports the +touch+ option from
642
666
  # +update_counters+, see that for more.
667
+ #
668
+ # This method raises an ActiveRecord::ActiveRecordError when called on new
669
+ # objects, or when at least one of the attributes is marked as readonly.
670
+ #
643
671
  # Returns +self+.
644
672
  def increment!(attribute, by = 1, touch: nil)
673
+ raise ActiveRecordError, "cannot update a new record" if new_record?
674
+ raise ActiveRecordError, "cannot update a destroyed record" if destroyed?
675
+
645
676
  increment(attribute, by)
646
677
  change = public_send(attribute) - (public_send(:"#{attribute}_in_database") || 0)
647
678
  self.class.update_counters(id, attribute => change, touch: touch)
@@ -3,6 +3,7 @@
3
3
  module ActiveRecord
4
4
  # = Active Record Query Cache
5
5
  class QueryCache
6
+ # ActiveRecord::Base extends this module, so these methods are available in models.
6
7
  module ClassMethods
7
8
  # Enable the query cache within the block if Active Record is configured.
8
9
  # If it's not, it will execute the given block.
@@ -20,11 +21,15 @@ module ActiveRecord
20
21
  end
21
22
  end
22
23
 
23
- # Disable the query cache within the block if Active Record is configured.
24
- # If it's not, it will execute the given block.
24
+ # Runs the block with the query cache disabled.
25
+ #
26
+ # If the query cache was enabled before the block was executed, it is
27
+ # enabled again after it.
25
28
  #
26
- # Set <tt>dirties: false</tt> to prevent query caches on all connections from being cleared by write operations.
27
- # (By default, write operations dirty all connections' query caches in case they are replicas whose cache would now be outdated.)
29
+ # Set <tt>dirties: false</tt> to prevent query caches on all connections
30
+ # from being cleared by write operations. (By default, write operations
31
+ # dirty all connections' query caches in case they are replicas whose
32
+ # cache would now be outdated.)
28
33
  def uncached(dirties: true, &block)
29
34
  if connected? || !configurations.empty?
30
35
  connection_pool.disable_query_cache(dirties: dirties, &block)
@@ -34,22 +39,24 @@ module ActiveRecord
34
39
  end
35
40
  end
36
41
 
37
- def self.run
38
- ActiveRecord::Base.connection_handler.each_connection_pool.reject(&:query_cache_enabled).each do |pool|
39
- next if pool.db_config&.query_cache == false
40
- pool.enable_query_cache!
42
+ module ExecutorHooks # :nodoc:
43
+ def self.run
44
+ ActiveRecord::Base.connection_handler.each_connection_pool.reject(&:query_cache_enabled).each do |pool|
45
+ next if pool.db_config&.query_cache == false
46
+ pool.enable_query_cache!
47
+ end
41
48
  end
42
- end
43
49
 
44
- def self.complete(pools)
45
- pools.each do |pool|
46
- pool.disable_query_cache!
47
- pool.clear_query_cache
50
+ def self.complete(pools)
51
+ pools.each do |pool|
52
+ pool.disable_query_cache!
53
+ pool.clear_query_cache
54
+ end
48
55
  end
49
56
  end
50
57
 
51
- def self.install_executor_hooks(executor = ActiveSupport::Executor)
52
- executor.register_hook(self)
58
+ def self.install_executor_hooks(executor = ActiveSupport::Executor) # :nodoc:
59
+ executor.register_hook(ExecutorHooks)
53
60
  end
54
61
  end
55
62
  end
@@ -69,7 +69,7 @@ module ActiveRecord
69
69
  #
70
70
  # Tag comments can be prepended to the query:
71
71
  #
72
- # ActiveRecord::QueryLogs.prepend_comment = true
72
+ # config.active_record.query_log_tags_prepend_comment = true
73
73
  #
74
74
  # For applications where the content will not change during the lifetime of
75
75
  # the request or job execution, the tags can be cached for reuse in every query:
@@ -157,11 +157,7 @@ module ActiveRecord
157
157
  end
158
158
 
159
159
  def query_source_location # :nodoc:
160
- Thread.each_caller_location do |location|
161
- frame = LogSubscriber.backtrace_cleaner.clean_frame(location)
162
- return frame if frame
163
- end
164
- nil
160
+ LogSubscriber.backtrace_cleaner.first_clean_frame
165
161
  end
166
162
 
167
163
  ActiveSupport::ExecutionContext.after_change { ActiveRecord::QueryLogs.clear_cache }
@@ -215,7 +211,7 @@ module ActiveRecord
215
211
  end
216
212
 
217
213
  def escape_sql_comment(content)
218
- # Sanitize a string to appear within a SQL comment
214
+ # Sanitize a string to appear within an SQL comment
219
215
  # For compatibility, this also surrounding "/*+", "/*", and "*/"
220
216
  # characters, possibly with single surrounding space.
221
217
  # Then follows that by replacing any internal "*/" or "/ *" with
@@ -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
@@ -163,6 +163,18 @@ db_namespace = namespace :db do
163
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:'}"
@@ -459,7 +471,7 @@ db_namespace = namespace :db do
459
471
 
460
472
  namespace :dump do
461
473
  ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |name|
462
- 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"
463
475
  task name => :load_config do
464
476
  ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(name: name) do |pool|
465
477
  db_config = pool.db_config
@@ -474,7 +486,7 @@ db_namespace = namespace :db do
474
486
  namespace :load do
475
487
  ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |name|
476
488
  desc "Load a database schema file (either db/schema.rb or db/structure.sql, depending on configuration) into the #{name} database"
477
- task name => "db:test:purge:#{name}" do
489
+ task name => [:load_config, :check_protected_environments] do
478
490
  ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(name: name) do |pool|
479
491
  db_config = pool.db_config
480
492
  ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config, ENV["SCHEMA_FORMAT"] || db_config.schema_format)
@@ -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
@@ -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