activerecord 7.2.4 → 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 (210) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1013 -878
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/association_relation.rb +2 -1
  5. data/lib/active_record/associations/association.rb +69 -27
  6. data/lib/active_record/associations/association_scope.rb +1 -1
  7. data/lib/active_record/associations/belongs_to_association.rb +2 -2
  8. data/lib/active_record/associations/builder/association.rb +23 -11
  9. data/lib/active_record/associations/builder/belongs_to.rb +36 -8
  10. data/lib/active_record/associations/builder/collection_association.rb +7 -3
  11. data/lib/active_record/associations/builder/has_one.rb +1 -1
  12. data/lib/active_record/associations/builder/singular_association.rb +33 -5
  13. data/lib/active_record/associations/collection_association.rb +7 -3
  14. data/lib/active_record/associations/collection_proxy.rb +26 -4
  15. data/lib/active_record/associations/deprecation.rb +88 -0
  16. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  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_many_through_association.rb +3 -2
  20. data/lib/active_record/associations/has_one_association.rb +1 -0
  21. data/lib/active_record/associations/join_dependency.rb +3 -2
  22. data/lib/active_record/associations/preloader/association.rb +3 -3
  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/singular_association.rb +8 -3
  26. data/lib/active_record/associations.rb +192 -24
  27. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  28. data/lib/active_record/attribute_methods/primary_key.rb +4 -8
  29. data/lib/active_record/attribute_methods/query.rb +34 -0
  30. data/lib/active_record/attribute_methods/serialization.rb +18 -5
  31. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -14
  32. data/lib/active_record/attributes.rb +3 -0
  33. data/lib/active_record/autosave_association.rb +69 -27
  34. data/lib/active_record/base.rb +1 -2
  35. data/lib/active_record/callbacks.rb +1 -2
  36. data/lib/active_record/coders/column_serializer.rb +1 -1
  37. data/lib/active_record/coders/json.rb +16 -6
  38. data/lib/active_record/coders/yaml_column.rb +2 -2
  39. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +35 -28
  40. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
  41. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +68 -13
  42. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +429 -93
  43. data/lib/active_record/connection_adapters/abstract/database_statements.rb +148 -74
  44. data/lib/active_record/connection_adapters/abstract/query_cache.rb +27 -5
  45. data/lib/active_record/connection_adapters/abstract/quoting.rb +16 -25
  46. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
  47. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +32 -35
  48. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
  49. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +124 -34
  50. data/lib/active_record/connection_adapters/abstract/transaction.rb +40 -8
  51. data/lib/active_record/connection_adapters/abstract_adapter.rb +129 -63
  52. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +132 -65
  53. data/lib/active_record/connection_adapters/column.rb +14 -4
  54. data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
  55. data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
  56. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
  57. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
  58. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +79 -66
  59. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +89 -94
  60. data/lib/active_record/connection_adapters/mysql2_adapter.rb +2 -10
  61. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  62. data/lib/active_record/connection_adapters/postgresql/column.rb +8 -2
  63. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +79 -61
  64. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
  65. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  66. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +46 -3
  67. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
  68. data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
  69. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  70. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
  71. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +14 -33
  72. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +71 -32
  73. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +156 -68
  74. data/lib/active_record/connection_adapters/postgresql_adapter.rb +78 -105
  75. data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
  76. data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
  77. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +90 -98
  78. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -8
  79. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
  80. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +27 -2
  81. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +15 -16
  82. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +104 -37
  83. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  84. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +41 -66
  85. data/lib/active_record/connection_adapters/trilogy_adapter.rb +15 -18
  86. data/lib/active_record/connection_adapters.rb +1 -56
  87. data/lib/active_record/connection_handling.rb +25 -2
  88. data/lib/active_record/core.rb +33 -17
  89. data/lib/active_record/counter_cache.rb +35 -8
  90. data/lib/active_record/database_configurations/database_config.rb +9 -1
  91. data/lib/active_record/database_configurations/hash_config.rb +74 -9
  92. data/lib/active_record/database_configurations/url_config.rb +15 -3
  93. data/lib/active_record/database_configurations.rb +7 -3
  94. data/lib/active_record/delegated_type.rb +1 -1
  95. data/lib/active_record/disable_joins_association_relation.rb +5 -1
  96. data/lib/active_record/dynamic_matchers.rb +54 -69
  97. data/lib/active_record/encryption/config.rb +3 -1
  98. data/lib/active_record/encryption/encryptable_record.rb +8 -8
  99. data/lib/active_record/encryption/encrypted_attribute_type.rb +11 -2
  100. data/lib/active_record/encryption/encrypted_fixtures.rb +12 -7
  101. data/lib/active_record/encryption/encryptor.rb +28 -8
  102. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  103. data/lib/active_record/encryption/scheme.rb +9 -2
  104. data/lib/active_record/enum.rb +33 -30
  105. data/lib/active_record/errors.rb +33 -9
  106. data/lib/active_record/explain.rb +1 -1
  107. data/lib/active_record/explain_registry.rb +51 -2
  108. data/lib/active_record/filter_attribute_handler.rb +76 -0
  109. data/lib/active_record/fixtures.rb +2 -4
  110. data/lib/active_record/future_result.rb +15 -9
  111. data/lib/active_record/gem_version.rb +2 -2
  112. data/lib/active_record/inheritance.rb +9 -4
  113. data/lib/active_record/insert_all.rb +14 -9
  114. data/lib/active_record/locking/optimistic.rb +8 -1
  115. data/lib/active_record/locking/pessimistic.rb +5 -0
  116. data/lib/active_record/log_subscriber.rb +31 -29
  117. data/lib/active_record/message_pack.rb +13 -0
  118. data/lib/active_record/middleware/shard_selector.rb +34 -17
  119. data/lib/active_record/migration/command_recorder.rb +56 -13
  120. data/lib/active_record/migration/compatibility.rb +37 -24
  121. data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
  122. data/lib/active_record/migration.rb +48 -42
  123. data/lib/active_record/model_schema.rb +40 -15
  124. data/lib/active_record/nested_attributes.rb +14 -12
  125. data/lib/active_record/persistence.rb +163 -133
  126. data/lib/active_record/query_cache.rb +22 -15
  127. data/lib/active_record/query_logs.rb +100 -52
  128. data/lib/active_record/query_logs_formatter.rb +17 -28
  129. data/lib/active_record/querying.rb +21 -9
  130. data/lib/active_record/railtie.rb +35 -30
  131. data/lib/active_record/railties/controller_runtime.rb +11 -6
  132. data/lib/active_record/railties/databases.rake +26 -38
  133. data/lib/active_record/railties/job_checkpoints.rb +15 -0
  134. data/lib/active_record/railties/job_runtime.rb +10 -11
  135. data/lib/active_record/reflection.rb +54 -22
  136. data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
  137. data/lib/active_record/relation/batches.rb +147 -73
  138. data/lib/active_record/relation/calculations.rb +60 -43
  139. data/lib/active_record/relation/delegation.rb +25 -15
  140. data/lib/active_record/relation/finder_methods.rb +73 -43
  141. data/lib/active_record/relation/merger.rb +12 -10
  142. data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
  143. data/lib/active_record/relation/predicate_builder/association_query_value.rb +9 -9
  144. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
  145. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  146. data/lib/active_record/relation/predicate_builder.rb +23 -8
  147. data/lib/active_record/relation/query_attribute.rb +3 -1
  148. data/lib/active_record/relation/query_methods.rb +200 -96
  149. data/lib/active_record/relation/spawn_methods.rb +7 -7
  150. data/lib/active_record/relation/where_clause.rb +2 -9
  151. data/lib/active_record/relation.rb +127 -87
  152. data/lib/active_record/result.rb +109 -24
  153. data/lib/active_record/runtime_registry.rb +42 -58
  154. data/lib/active_record/sanitization.rb +9 -6
  155. data/lib/active_record/schema.rb +1 -1
  156. data/lib/active_record/schema_dumper.rb +17 -10
  157. data/lib/active_record/schema_migration.rb +2 -1
  158. data/lib/active_record/scoping/named.rb +5 -2
  159. data/lib/active_record/scoping.rb +0 -1
  160. data/lib/active_record/signed_id.rb +44 -16
  161. data/lib/active_record/statement_cache.rb +24 -20
  162. data/lib/active_record/store.rb +53 -22
  163. data/lib/active_record/structured_event_subscriber.rb +85 -0
  164. data/lib/active_record/table_metadata.rb +6 -23
  165. data/lib/active_record/tasks/abstract_tasks.rb +83 -0
  166. data/lib/active_record/tasks/database_tasks.rb +85 -85
  167. data/lib/active_record/tasks/mysql_database_tasks.rb +4 -58
  168. data/lib/active_record/tasks/postgresql_database_tasks.rb +7 -40
  169. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -28
  170. data/lib/active_record/test_databases.rb +18 -4
  171. data/lib/active_record/test_fixtures.rb +52 -6
  172. data/lib/active_record/testing/query_assertions.rb +8 -2
  173. data/lib/active_record/timestamp.rb +4 -2
  174. data/lib/active_record/token_for.rb +1 -1
  175. data/lib/active_record/transaction.rb +2 -5
  176. data/lib/active_record/transactions.rb +56 -32
  177. data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
  178. data/lib/active_record/type/internal/timezone.rb +7 -0
  179. data/lib/active_record/type/json.rb +13 -2
  180. data/lib/active_record/type/serialized.rb +16 -4
  181. data/lib/active_record/type/type_map.rb +1 -1
  182. data/lib/active_record/type_caster/connection.rb +2 -1
  183. data/lib/active_record/validations/associated.rb +1 -1
  184. data/lib/active_record/validations/uniqueness.rb +8 -8
  185. data/lib/active_record.rb +85 -50
  186. data/lib/arel/alias_predication.rb +2 -0
  187. data/lib/arel/collectors/bind.rb +2 -2
  188. data/lib/arel/collectors/sql_string.rb +1 -1
  189. data/lib/arel/collectors/substitute_binds.rb +2 -2
  190. data/lib/arel/crud.rb +6 -11
  191. data/lib/arel/nodes/binary.rb +1 -1
  192. data/lib/arel/nodes/count.rb +2 -2
  193. data/lib/arel/nodes/function.rb +4 -10
  194. data/lib/arel/nodes/named_function.rb +2 -2
  195. data/lib/arel/nodes/node.rb +2 -2
  196. data/lib/arel/nodes/sql_literal.rb +1 -1
  197. data/lib/arel/nodes.rb +0 -2
  198. data/lib/arel/predications.rb +1 -3
  199. data/lib/arel/select_manager.rb +7 -2
  200. data/lib/arel/table.rb +3 -7
  201. data/lib/arel/visitors/dot.rb +0 -3
  202. data/lib/arel/visitors/postgresql.rb +55 -0
  203. data/lib/arel/visitors/sqlite.rb +55 -8
  204. data/lib/arel/visitors/to_sql.rb +4 -22
  205. data/lib/arel.rb +3 -1
  206. data/lib/rails/generators/active_record/application_record/USAGE +1 -1
  207. metadata +15 -12
  208. data/lib/active_record/explain_subscriber.rb +0 -34
  209. data/lib/active_record/normalization.rb +0 -163
  210. data/lib/active_record/relation/record_fetch_warning.rb +0 -52
@@ -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,21 +69,77 @@ 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:
76
76
  #
77
77
  # config.active_record.cache_query_log_tags = true
78
78
  module QueryLogs
79
- mattr_accessor :taggings, instance_accessor: false, default: {}
80
- mattr_accessor :tags, instance_accessor: false, default: [ :application ]
81
- mattr_accessor :prepend_comment, instance_accessor: false, default: false
82
- mattr_accessor :cache_query_log_tags, instance_accessor: false, default: false
83
- mattr_accessor :tags_formatter, instance_accessor: false
79
+ class GetKeyHandler # :nodoc:
80
+ def initialize(name)
81
+ @name = name
82
+ end
83
+
84
+ def call(context)
85
+ context[@name]
86
+ end
87
+ end
88
+
89
+ class IdentityHandler # :nodoc:
90
+ def initialize(value)
91
+ @value = value
92
+ end
93
+
94
+ def call(_context)
95
+ @value
96
+ end
97
+ end
98
+
99
+ class ZeroArityHandler # :nodoc:
100
+ def initialize(proc)
101
+ @proc = proc
102
+ end
103
+
104
+ def call(_context)
105
+ @proc.call
106
+ end
107
+ end
108
+
109
+ @taggings = {}.freeze
110
+ @tags = [ :application ].freeze
111
+ @prepend_comment = false
112
+ @cache_query_log_tags = false
113
+ @tags_formatter = false
114
+
84
115
  thread_mattr_accessor :cached_comment, instance_accessor: false
85
116
 
86
117
  class << self
118
+ attr_reader :tags, :taggings, :tags_formatter # :nodoc:
119
+ attr_accessor :prepend_comment, :cache_query_log_tags # :nodoc:
120
+
121
+ def taggings=(taggings) # :nodoc:
122
+ @taggings = taggings.freeze
123
+ @handlers = rebuild_handlers
124
+ end
125
+
126
+ def tags=(tags) # :nodoc:
127
+ @tags = tags.freeze
128
+ @handlers = rebuild_handlers
129
+ end
130
+
131
+ def tags_formatter=(format) # :nodoc:
132
+ @formatter = case format
133
+ when :legacy
134
+ LegacyFormatter
135
+ when :sqlcommenter
136
+ SQLCommenter
137
+ else
138
+ raise ArgumentError, "Formatter is unsupported: #{format}"
139
+ end
140
+ @tags_formatter = format
141
+ end
142
+
87
143
  def call(sql, connection) # :nodoc:
88
144
  comment = self.comment(connection)
89
145
 
@@ -100,36 +156,42 @@ module ActiveRecord
100
156
  self.cached_comment = nil
101
157
  end
102
158
 
103
- # Updates the formatter to be what the passed in format is.
104
- def update_formatter(format)
105
- self.tags_formatter =
106
- case format
107
- when :legacy
108
- LegacyFormatter.new
109
- when :sqlcommenter
110
- SQLCommenter.new
111
- else
112
- raise ArgumentError, "Formatter is unsupported: #{formatter}"
113
- end
159
+ def query_source_location # :nodoc:
160
+ LogSubscriber.backtrace_cleaner.first_clean_frame
114
161
  end
115
162
 
116
- if Thread.respond_to?(:each_caller_location)
117
- def query_source_location # :nodoc:
118
- Thread.each_caller_location do |location|
119
- frame = LogSubscriber.backtrace_cleaner.clean_frame(location)
120
- return frame if frame
163
+ ActiveSupport::ExecutionContext.after_change { ActiveRecord::QueryLogs.clear_cache }
164
+
165
+ private
166
+ def rebuild_handlers
167
+ handlers = []
168
+ @tags.each do |i|
169
+ if i.is_a?(Hash)
170
+ i.each do |k, v|
171
+ handlers << [k, build_handler(k, v)]
172
+ end
173
+ else
174
+ handlers << [i, build_handler(i)]
175
+ end
121
176
  end
122
- nil
123
- end
124
- else
125
- def query_source_location # :nodoc:
126
- LogSubscriber.backtrace_cleaner.clean(caller_locations(1).each).first
177
+ handlers.sort_by! { |(key, _)| key.to_s }
127
178
  end
128
- end
129
179
 
130
- ActiveSupport::ExecutionContext.after_change { ActiveRecord::QueryLogs.clear_cache }
180
+ def build_handler(name, handler = nil)
181
+ handler ||= @taggings[name]
182
+ if handler.nil?
183
+ GetKeyHandler.new(name)
184
+ elsif handler.respond_to?(:call)
185
+ if handler.arity == 0
186
+ ZeroArityHandler.new(handler)
187
+ else
188
+ handler
189
+ end
190
+ else
191
+ IdentityHandler.new(handler)
192
+ end
193
+ end
131
194
 
132
- private
133
195
  # Returns an SQL comment +String+ containing the query log tags.
134
196
  # Sets and returns a cached comment if <tt>cache_query_log_tags</tt> is +true+.
135
197
  def comment(connection)
@@ -140,10 +202,6 @@ module ActiveRecord
140
202
  end
141
203
  end
142
204
 
143
- def formatter
144
- self.tags_formatter || self.update_formatter(:legacy)
145
- end
146
-
147
205
  def uncached_comment(connection)
148
206
  content = tag_content(connection)
149
207
 
@@ -153,7 +211,7 @@ module ActiveRecord
153
211
  end
154
212
 
155
213
  def escape_sql_comment(content)
156
- # Sanitize a string to appear within a SQL comment
214
+ # Sanitize a string to appear within an SQL comment
157
215
  # For compatibility, this also surrounding "/*+", "/*", and "*/"
158
216
  # characters, possibly with single surrounding space.
159
217
  # Then follows that by replacing any internal "*/" or "/ *" with
@@ -169,25 +227,15 @@ module ActiveRecord
169
227
  context = ActiveSupport::ExecutionContext.to_h
170
228
  context[:connection] ||= connection
171
229
 
172
- pairs = tags.flat_map { |i| [*i] }.filter_map do |tag|
173
- key, handler = tag
174
- handler ||= taggings[key]
175
-
176
- val = if handler.nil?
177
- context[key]
178
- elsif handler.respond_to?(:call)
179
- if handler.arity == 0
180
- handler.call
181
- else
182
- handler.call(context)
183
- end
184
- else
185
- handler
186
- end
187
- [key, val] unless val.nil?
230
+ pairs = @handlers.filter_map do |(key, handler)|
231
+ val = handler.call(context)
232
+ @formatter.format(key, val) unless val.nil?
188
233
  end
189
- self.formatter.format(pairs)
234
+ @formatter.join(pairs)
190
235
  end
191
236
  end
237
+
238
+ @handlers = rebuild_handlers
239
+ self.tags_formatter = :legacy
192
240
  end
193
241
  end
@@ -2,40 +2,29 @@
2
2
 
3
3
  module ActiveRecord
4
4
  module QueryLogs
5
- class LegacyFormatter # :nodoc:
6
- def initialize
7
- @key_value_separator = ":"
8
- end
9
-
10
- # Formats the key value pairs into a string.
11
- def format(pairs)
12
- pairs.map! do |key, value|
13
- "#{key}#{key_value_separator}#{format_value(value)}"
14
- end.join(",")
15
- end
16
-
17
- private
18
- attr_reader :key_value_separator
19
-
20
- def format_value(value)
21
- value
5
+ module LegacyFormatter # :nodoc:
6
+ class << self
7
+ # Formats the key value pairs into a string.
8
+ def format(key, value)
9
+ "#{key}:#{value}"
22
10
  end
23
- end
24
11
 
25
- class SQLCommenter < LegacyFormatter # :nodoc:
26
- def initialize
27
- @key_value_separator = "="
12
+ def join(pairs)
13
+ pairs.join(",")
14
+ end
28
15
  end
16
+ end
29
17
 
30
- def format(pairs)
31
- pairs.sort_by! { |pair| pair.first.to_s }
32
- super
33
- end
18
+ class SQLCommenter # :nodoc:
19
+ class << self
20
+ def format(key, value)
21
+ "#{key}='#{ERB::Util.url_encode(value)}'"
22
+ end
34
23
 
35
- private
36
- def format_value(value)
37
- "'#{ERB::Util.url_encode(value)}'"
24
+ def join(pairs)
25
+ pairs.join(",")
38
26
  end
27
+ end
39
28
  end
40
29
  end
41
30
  end
@@ -17,12 +17,24 @@ module ActiveRecord
17
17
  :and, :or, :annotate, :optimizer_hints, :extending,
18
18
  :having, :create_with, :distinct, :references, :none, :unscope, :merge, :except, :only,
19
19
  :count, :average, :minimum, :maximum, :sum, :calculate,
20
- :pluck, :pick, :ids, :async_ids, :strict_loading, :excluding, :without, :with, :with_recursive,
20
+ :pluck, :pick, :ids, :async_ids, :strict_loading, :excluding, :without, :with_recursive,
21
21
  :async_count, :async_average, :async_minimum, :async_maximum, :async_sum, :async_pluck, :async_pick,
22
22
  :insert, :insert_all, :insert!, :insert_all!, :upsert, :upsert_all
23
23
  ].freeze # :nodoc:
24
24
  delegate(*QUERYING_METHODS, to: :all)
25
25
 
26
+ # Add a Common Table Expression (CTE) that you can then reference within another SELECT statement.
27
+ #
28
+ # See ActiveRecord::QueryMethods#with for more information.
29
+ #
30
+ # When given a block, and the Object#with core extension is loaded, this
31
+ # delegates to it instead, temporarily setting the given attributes on the
32
+ # class for the duration of the block and restoring them afterwards.
33
+ def with(...)
34
+ return super if block_given? && defined?(super)
35
+ all.with(...)
36
+ end
37
+
26
38
  # Executes a custom SQL query against your database and returns all the results. The results will
27
39
  # be returned as an array, with the requested columns encapsulated as attributes of the model you call
28
40
  # this method from. For example, if you call <tt>Product.find_by_sql</tt>, then the results will be returned in
@@ -56,12 +68,10 @@ module ActiveRecord
56
68
  end
57
69
 
58
70
  # Same as #find_by_sql but perform the query asynchronously and returns an ActiveRecord::Promise.
59
- def async_find_by_sql(sql, binds = [], preparable: nil, &block)
60
- result = with_connection do |c|
61
- _query_by_sql(c, sql, binds, preparable: preparable, async: true)
62
- end
63
-
64
- result.then do |result|
71
+ def async_find_by_sql(sql, binds = [], preparable: nil, allow_retry: false, &block)
72
+ with_connection do |c|
73
+ _query_by_sql(c, sql, binds, preparable: preparable, allow_retry: allow_retry, async: true)
74
+ end.then do |result|
65
75
  _load_from_sql(result, &block)
66
76
  end
67
77
  end
@@ -71,6 +81,8 @@ module ActiveRecord
71
81
  end
72
82
 
73
83
  def _load_from_sql(result_set, &block) # :nodoc:
84
+ return [] if result_set.empty?
85
+
74
86
  column_types = result_set.column_types
75
87
 
76
88
  unless column_types.empty?
@@ -86,10 +98,10 @@ module ActiveRecord
86
98
 
87
99
  message_bus.instrument("instantiation.active_record", payload) do
88
100
  if result_set.includes_column?(inheritance_column)
89
- result_set.map { |record| instantiate(record, column_types, &block) }
101
+ result_set.indexed_rows.map { |record| instantiate(record, column_types, &block) }
90
102
  else
91
103
  # Instantiate a homogeneous set
92
- result_set.map { |record| instantiate_instance_of(self, record, column_types, &block) }
104
+ result_set.indexed_rows.map { |record| instantiate_instance_of(self, record, column_types, &block) }
93
105
  end
94
106
  end
95
107
  end
@@ -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
 
@@ -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
 
@@ -184,30 +187,6 @@ To keep using the current cache store, you can turn off cache versioning entirel
184
187
  end
185
188
  end
186
189
 
187
- initializer "active_record.warn_on_records_fetched_greater_than" do
188
- if config.active_record.warn_on_records_fetched_greater_than
189
- ActiveRecord.deprecator.warn <<~MSG.squish
190
- `config.active_record.warn_on_records_fetched_greater_than` is deprecated and will be
191
- removed in Rails 8.0.
192
- Please subscribe to `sql.active_record` notifications and access the row count field to
193
- detect large result set sizes.
194
- MSG
195
- ActiveSupport.on_load(:active_record) do
196
- require "active_record/relation/record_fetch_warning"
197
- end
198
- end
199
- end
200
-
201
- initializer "active_record.sqlite3_deprecated_warning" do
202
- if config.active_record.key?(:sqlite3_production_warning)
203
- config.active_record.delete(:sqlite3_production_warning)
204
- ActiveRecord.deprecator.warn <<~MSG.squish
205
- The `config.active_record.sqlite3_production_warning` configuration no longer has any effect
206
- and can be safely removed.
207
- MSG
208
- end
209
- end
210
-
211
190
  initializer "active_record.sqlite3_adapter_strict_strings_by_default" do
212
191
  config.after_initialize do
213
192
  if config.active_record.sqlite3_adapter_strict_strings_by_default
@@ -253,10 +232,12 @@ To keep using the current cache store, you can turn off cache versioning entirel
253
232
  :query_log_tags,
254
233
  :query_log_tags_format,
255
234
  :cache_query_log_tags,
235
+ :query_log_tags_prepend_comment,
256
236
  :sqlite3_adapter_strict_strings_by_default,
257
237
  :check_schema_cache_dump_version,
258
238
  :use_schema_cache_dump,
259
239
  :postgresql_adapter_decode_dates,
240
+ :use_legacy_signed_id_verifier,
260
241
  )
261
242
 
262
243
  configs_used_in_other_initializers.each do |k, v|
@@ -298,6 +279,13 @@ To keep using the current cache store, you can turn off cache versioning entirel
298
279
  end
299
280
  end
300
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
+
301
289
  initializer "active_record.set_reloader_hooks" do
302
290
  ActiveSupport.on_load(:active_record) do
303
291
  ActiveSupport::Reloader.before_class_unload do
@@ -343,9 +331,22 @@ To keep using the current cache store, you can turn off cache versioning entirel
343
331
  end
344
332
  end
345
333
 
346
- initializer "active_record.set_signed_id_verifier_secret" do
347
- ActiveSupport.on_load(:active_record) do
348
- 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}"
349
350
  end
350
351
  end
351
352
 
@@ -390,7 +391,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
390
391
  config.after_initialize do
391
392
  if app.config.active_record.query_log_tags_enabled
392
393
  ActiveRecord.query_transformers << ActiveRecord::QueryLogs
393
- ActiveRecord::QueryLogs.taggings.merge!(
394
+ ActiveRecord::QueryLogs.taggings = ActiveRecord::QueryLogs.taggings.merge(
394
395
  application: Rails.application.class.name.split("::").first,
395
396
  pid: -> { Process.pid.to_s },
396
397
  socket: ->(context) { context[:connection].pool.db_config.socket },
@@ -405,12 +406,16 @@ To keep using the current cache store, you can turn off cache versioning entirel
405
406
  end
406
407
 
407
408
  if app.config.active_record.query_log_tags_format
408
- ActiveRecord::QueryLogs.update_formatter(app.config.active_record.query_log_tags_format)
409
+ ActiveRecord::QueryLogs.tags_formatter = app.config.active_record.query_log_tags_format
409
410
  end
410
411
 
411
412
  if app.config.active_record.cache_query_log_tags
412
413
  ActiveRecord::QueryLogs.cache_query_log_tags = true
413
414
  end
415
+
416
+ if app.config.active_record.query_log_tags_prepend_comment
417
+ ActiveRecord::QueryLogs.prepend_comment = true
418
+ end
414
419
  end
415
420
  end
416
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