activerecord 7.1.5.1 → 8.0.2

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 (206) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +369 -2484
  3. data/README.rdoc +15 -15
  4. data/examples/performance.rb +2 -2
  5. data/lib/active_record/association_relation.rb +2 -1
  6. data/lib/active_record/associations/alias_tracker.rb +31 -23
  7. data/lib/active_record/associations/association.rb +43 -12
  8. data/lib/active_record/associations/belongs_to_association.rb +21 -8
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  10. data/lib/active_record/associations/builder/association.rb +7 -6
  11. data/lib/active_record/associations/builder/belongs_to.rb +1 -0
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
  13. data/lib/active_record/associations/builder/has_many.rb +3 -4
  14. data/lib/active_record/associations/builder/has_one.rb +3 -4
  15. data/lib/active_record/associations/collection_association.rb +17 -9
  16. data/lib/active_record/associations/collection_proxy.rb +14 -1
  17. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  18. data/lib/active_record/associations/errors.rb +265 -0
  19. data/lib/active_record/associations/has_many_association.rb +1 -1
  20. data/lib/active_record/associations/has_many_through_association.rb +10 -3
  21. data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
  22. data/lib/active_record/associations/nested_error.rb +47 -0
  23. data/lib/active_record/associations/preloader/association.rb +4 -3
  24. data/lib/active_record/associations/preloader/branch.rb +7 -1
  25. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  26. data/lib/active_record/associations/singular_association.rb +14 -3
  27. data/lib/active_record/associations/through_association.rb +1 -1
  28. data/lib/active_record/associations.rb +92 -295
  29. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  30. data/lib/active_record/attribute_assignment.rb +0 -2
  31. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  32. data/lib/active_record/attribute_methods/primary_key.rb +25 -61
  33. data/lib/active_record/attribute_methods/read.rb +1 -13
  34. data/lib/active_record/attribute_methods/serialization.rb +4 -24
  35. data/lib/active_record/attribute_methods/time_zone_conversion.rb +9 -18
  36. data/lib/active_record/attribute_methods.rb +71 -75
  37. data/lib/active_record/attributes.rb +63 -49
  38. data/lib/active_record/autosave_association.rb +92 -57
  39. data/lib/active_record/base.rb +2 -3
  40. data/lib/active_record/callbacks.rb +1 -1
  41. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +48 -122
  42. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
  43. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -1
  44. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +286 -77
  45. data/lib/active_record/connection_adapters/abstract/database_statements.rb +119 -55
  46. data/lib/active_record/connection_adapters/abstract/query_cache.rb +197 -76
  47. data/lib/active_record/connection_adapters/abstract/quoting.rb +66 -92
  48. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
  49. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +12 -3
  50. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +48 -12
  51. data/lib/active_record/connection_adapters/abstract/transaction.rb +140 -67
  52. data/lib/active_record/connection_adapters/abstract_adapter.rb +85 -90
  53. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +71 -52
  54. data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
  55. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
  56. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
  57. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +56 -45
  58. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +92 -101
  59. data/lib/active_record/connection_adapters/mysql2_adapter.rb +13 -31
  60. data/lib/active_record/connection_adapters/pool_config.rb +14 -13
  61. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +86 -41
  62. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  63. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  64. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  65. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  66. data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
  67. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  68. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
  69. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +36 -20
  70. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +3 -2
  71. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +75 -28
  72. data/lib/active_record/connection_adapters/postgresql_adapter.rb +73 -113
  73. data/lib/active_record/connection_adapters/schema_cache.rb +124 -131
  74. data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
  75. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +81 -97
  76. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -46
  77. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
  78. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
  79. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +29 -0
  80. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +35 -3
  81. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +183 -87
  82. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  83. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +39 -69
  84. data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
  85. data/lib/active_record/connection_adapters.rb +65 -0
  86. data/lib/active_record/connection_handling.rb +74 -37
  87. data/lib/active_record/core.rb +132 -51
  88. data/lib/active_record/counter_cache.rb +19 -10
  89. data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
  90. data/lib/active_record/database_configurations/database_config.rb +23 -4
  91. data/lib/active_record/database_configurations/hash_config.rb +46 -34
  92. data/lib/active_record/database_configurations/url_config.rb +20 -1
  93. data/lib/active_record/database_configurations.rb +1 -1
  94. data/lib/active_record/delegated_type.rb +41 -17
  95. data/lib/active_record/dynamic_matchers.rb +2 -2
  96. data/lib/active_record/encryption/config.rb +3 -1
  97. data/lib/active_record/encryption/encryptable_record.rb +7 -7
  98. data/lib/active_record/encryption/encrypted_attribute_type.rb +33 -4
  99. data/lib/active_record/encryption/encryptor.rb +28 -6
  100. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  101. data/lib/active_record/encryption/key_provider.rb +1 -1
  102. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  103. data/lib/active_record/encryption/message_serializer.rb +4 -0
  104. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  105. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  106. data/lib/active_record/encryption/scheme.rb +8 -1
  107. data/lib/active_record/enum.rb +20 -16
  108. data/lib/active_record/errors.rb +54 -20
  109. data/lib/active_record/explain.rb +13 -24
  110. data/lib/active_record/fixtures.rb +37 -33
  111. data/lib/active_record/future_result.rb +21 -13
  112. data/lib/active_record/gem_version.rb +4 -4
  113. data/lib/active_record/inheritance.rb +4 -2
  114. data/lib/active_record/insert_all.rb +19 -16
  115. data/lib/active_record/integration.rb +4 -1
  116. data/lib/active_record/internal_metadata.rb +48 -34
  117. data/lib/active_record/locking/optimistic.rb +8 -7
  118. data/lib/active_record/log_subscriber.rb +5 -32
  119. data/lib/active_record/message_pack.rb +1 -1
  120. data/lib/active_record/migration/command_recorder.rb +33 -14
  121. data/lib/active_record/migration/compatibility.rb +8 -3
  122. data/lib/active_record/migration/default_strategy.rb +4 -5
  123. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  124. data/lib/active_record/migration.rb +104 -98
  125. data/lib/active_record/model_schema.rb +32 -70
  126. data/lib/active_record/nested_attributes.rb +15 -9
  127. data/lib/active_record/normalization.rb +3 -7
  128. data/lib/active_record/persistence.rb +127 -451
  129. data/lib/active_record/query_cache.rb +19 -8
  130. data/lib/active_record/query_logs.rb +104 -37
  131. data/lib/active_record/query_logs_formatter.rb +17 -28
  132. data/lib/active_record/querying.rb +24 -12
  133. data/lib/active_record/railtie.rb +26 -68
  134. data/lib/active_record/railties/controller_runtime.rb +13 -4
  135. data/lib/active_record/railties/databases.rake +43 -61
  136. data/lib/active_record/reflection.rb +112 -53
  137. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  138. data/lib/active_record/relation/batches.rb +138 -72
  139. data/lib/active_record/relation/calculations.rb +122 -82
  140. data/lib/active_record/relation/delegation.rb +30 -22
  141. data/lib/active_record/relation/finder_methods.rb +32 -18
  142. data/lib/active_record/relation/merger.rb +12 -14
  143. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  144. data/lib/active_record/relation/predicate_builder/association_query_value.rb +10 -2
  145. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
  146. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  147. data/lib/active_record/relation/predicate_builder.rb +16 -3
  148. data/lib/active_record/relation/query_attribute.rb +1 -1
  149. data/lib/active_record/relation/query_methods.rb +317 -101
  150. data/lib/active_record/relation/spawn_methods.rb +3 -19
  151. data/lib/active_record/relation/where_clause.rb +7 -19
  152. data/lib/active_record/relation.rb +561 -119
  153. data/lib/active_record/result.rb +95 -46
  154. data/lib/active_record/runtime_registry.rb +39 -0
  155. data/lib/active_record/sanitization.rb +31 -25
  156. data/lib/active_record/schema.rb +8 -6
  157. data/lib/active_record/schema_dumper.rb +53 -20
  158. data/lib/active_record/schema_migration.rb +31 -14
  159. data/lib/active_record/scoping/named.rb +6 -2
  160. data/lib/active_record/signed_id.rb +24 -4
  161. data/lib/active_record/statement_cache.rb +19 -19
  162. data/lib/active_record/store.rb +7 -3
  163. data/lib/active_record/table_metadata.rb +2 -13
  164. data/lib/active_record/tasks/database_tasks.rb +87 -58
  165. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -3
  166. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  167. data/lib/active_record/tasks/sqlite_database_tasks.rb +4 -3
  168. data/lib/active_record/test_fixtures.rb +98 -89
  169. data/lib/active_record/testing/query_assertions.rb +121 -0
  170. data/lib/active_record/timestamp.rb +2 -2
  171. data/lib/active_record/token_for.rb +22 -12
  172. data/lib/active_record/touch_later.rb +1 -1
  173. data/lib/active_record/transaction.rb +132 -0
  174. data/lib/active_record/transactions.rb +72 -17
  175. data/lib/active_record/translation.rb +0 -2
  176. data/lib/active_record/type/serialized.rb +1 -3
  177. data/lib/active_record/type_caster/connection.rb +4 -4
  178. data/lib/active_record/validations/associated.rb +9 -3
  179. data/lib/active_record/validations/uniqueness.rb +23 -18
  180. data/lib/active_record/validations.rb +4 -1
  181. data/lib/active_record.rb +138 -57
  182. data/lib/arel/alias_predication.rb +1 -1
  183. data/lib/arel/collectors/bind.rb +4 -2
  184. data/lib/arel/collectors/composite.rb +7 -0
  185. data/lib/arel/collectors/sql_string.rb +2 -2
  186. data/lib/arel/collectors/substitute_binds.rb +3 -3
  187. data/lib/arel/nodes/binary.rb +1 -7
  188. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  189. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  190. data/lib/arel/nodes/node.rb +5 -4
  191. data/lib/arel/nodes/sql_literal.rb +8 -1
  192. data/lib/arel/nodes.rb +2 -2
  193. data/lib/arel/predications.rb +1 -1
  194. data/lib/arel/select_manager.rb +1 -1
  195. data/lib/arel/table.rb +3 -7
  196. data/lib/arel/tree_manager.rb +3 -2
  197. data/lib/arel/update_manager.rb +2 -1
  198. data/lib/arel/visitors/dot.rb +1 -0
  199. data/lib/arel/visitors/mysql.rb +9 -4
  200. data/lib/arel/visitors/postgresql.rb +1 -12
  201. data/lib/arel/visitors/sqlite.rb +25 -0
  202. data/lib/arel/visitors/to_sql.rb +29 -16
  203. data/lib/arel.rb +7 -3
  204. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  205. metadata +18 -16
  206. data/lib/active_record/relation/record_fetch_warning.rb +0 -49
@@ -8,7 +8,13 @@ module ActiveRecord
8
8
  # If it's not, it will execute the given block.
9
9
  def cache(&block)
10
10
  if connected? || !configurations.empty?
11
- connection.cache(&block)
11
+ pool = connection_pool
12
+ was_enabled = pool.query_cache_enabled
13
+ begin
14
+ pool.enable_query_cache(&block)
15
+ ensure
16
+ pool.clear_query_cache unless was_enabled
17
+ end
12
18
  else
13
19
  yield
14
20
  end
@@ -16,9 +22,12 @@ module ActiveRecord
16
22
 
17
23
  # Disable the query cache within the block if Active Record is configured.
18
24
  # If it's not, it will execute the given block.
19
- def uncached(&block)
25
+ #
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.)
28
+ def uncached(dirties: true, &block)
20
29
  if connected? || !configurations.empty?
21
- connection.uncached(&block)
30
+ connection_pool.disable_query_cache(dirties: dirties, &block)
22
31
  else
23
32
  yield
24
33
  end
@@ -26,14 +35,16 @@ module ActiveRecord
26
35
  end
27
36
 
28
37
  def self.run
29
- ActiveRecord::Base.connection_handler.each_connection_pool.reject { |p| p.query_cache_enabled }.each { |p| p.enable_query_cache! }
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!
41
+ end
30
42
  end
31
43
 
32
44
  def self.complete(pools)
33
- pools.each { |pool| pool.disable_query_cache! unless pool.discarded? }
34
-
35
- ActiveRecord::Base.connection_handler.each_connection_pool do |pool|
36
- pool.release_connection if pool.active_connection? && !pool.connection.transaction_open?
45
+ pools.each do |pool|
46
+ pool.disable_query_cache!
47
+ pool.clear_query_cache
37
48
  end
38
49
  end
39
50
 
@@ -26,6 +26,7 @@ module ActiveRecord
26
26
  # * +socket+
27
27
  # * +db_host+
28
28
  # * +database+
29
+ # * +source_location+
29
30
  #
30
31
  # Action Controller adds default tags when loaded:
31
32
  #
@@ -71,14 +72,70 @@ module ActiveRecord
71
72
  #
72
73
  # config.active_record.cache_query_log_tags = true
73
74
  module QueryLogs
74
- mattr_accessor :taggings, instance_accessor: false, default: {}
75
- mattr_accessor :tags, instance_accessor: false, default: [ :application ]
76
- mattr_accessor :prepend_comment, instance_accessor: false, default: false
77
- mattr_accessor :cache_query_log_tags, instance_accessor: false, default: false
78
- mattr_accessor :tags_formatter, instance_accessor: false
75
+ class GetKeyHandler # :nodoc:
76
+ def initialize(name)
77
+ @name = name
78
+ end
79
+
80
+ def call(context)
81
+ context[@name]
82
+ end
83
+ end
84
+
85
+ class IdentityHandler # :nodoc:
86
+ def initialize(value)
87
+ @value = value
88
+ end
89
+
90
+ def call(_context)
91
+ @value
92
+ end
93
+ end
94
+
95
+ class ZeroArityHandler # :nodoc:
96
+ def initialize(proc)
97
+ @proc = proc
98
+ end
99
+
100
+ def call(_context)
101
+ @proc.call
102
+ end
103
+ end
104
+
105
+ @taggings = {}.freeze
106
+ @tags = [ :application ].freeze
107
+ @prepend_comment = false
108
+ @cache_query_log_tags = false
109
+ @tags_formatter = false
110
+
79
111
  thread_mattr_accessor :cached_comment, instance_accessor: false
80
112
 
81
113
  class << self
114
+ attr_reader :tags, :taggings, :tags_formatter # :nodoc:
115
+ attr_accessor :prepend_comment, :cache_query_log_tags # :nodoc:
116
+
117
+ def taggings=(taggings) # :nodoc:
118
+ @taggings = taggings.freeze
119
+ @handlers = rebuild_handlers
120
+ end
121
+
122
+ def tags=(tags) # :nodoc:
123
+ @tags = tags.freeze
124
+ @handlers = rebuild_handlers
125
+ end
126
+
127
+ def tags_formatter=(format) # :nodoc:
128
+ @formatter = case format
129
+ when :legacy
130
+ LegacyFormatter
131
+ when :sqlcommenter
132
+ SQLCommenter
133
+ else
134
+ raise ArgumentError, "Formatter is unsupported: #{format}"
135
+ end
136
+ @tags_formatter = format
137
+ end
138
+
82
139
  def call(sql, connection) # :nodoc:
83
140
  comment = self.comment(connection)
84
141
 
@@ -95,22 +152,46 @@ module ActiveRecord
95
152
  self.cached_comment = nil
96
153
  end
97
154
 
98
- # Updates the formatter to be what the passed in format is.
99
- def update_formatter(format)
100
- self.tags_formatter =
101
- case format
102
- when :legacy
103
- LegacyFormatter.new
104
- when :sqlcommenter
105
- SQLCommenter.new
106
- else
107
- raise ArgumentError, "Formatter is unsupported: #{formatter}"
108
- end
155
+ def query_source_location # :nodoc:
156
+ Thread.each_caller_location do |location|
157
+ frame = LogSubscriber.backtrace_cleaner.clean_frame(location)
158
+ return frame if frame
159
+ end
160
+ nil
109
161
  end
110
162
 
111
163
  ActiveSupport::ExecutionContext.after_change { ActiveRecord::QueryLogs.clear_cache }
112
164
 
113
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
176
+ end
177
+ handlers.sort_by! { |(key, _)| key.to_s }
178
+ end
179
+
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
194
+
114
195
  # Returns an SQL comment +String+ containing the query log tags.
115
196
  # Sets and returns a cached comment if <tt>cache_query_log_tags</tt> is +true+.
116
197
  def comment(connection)
@@ -121,10 +202,6 @@ module ActiveRecord
121
202
  end
122
203
  end
123
204
 
124
- def formatter
125
- self.tags_formatter || self.update_formatter(:legacy)
126
- end
127
-
128
205
  def uncached_comment(connection)
129
206
  content = tag_content(connection)
130
207
 
@@ -150,25 +227,15 @@ module ActiveRecord
150
227
  context = ActiveSupport::ExecutionContext.to_h
151
228
  context[:connection] ||= connection
152
229
 
153
- pairs = tags.flat_map { |i| [*i] }.filter_map do |tag|
154
- key, handler = tag
155
- handler ||= taggings[key]
156
-
157
- val = if handler.nil?
158
- context[key]
159
- elsif handler.respond_to?(:call)
160
- if handler.arity == 0
161
- handler.call
162
- else
163
- handler.call(context)
164
- end
165
- else
166
- handler
167
- end
168
- [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?
169
233
  end
170
- self.formatter.format(pairs)
234
+ @formatter.join(pairs)
171
235
  end
172
236
  end
237
+
238
+ @handlers = rebuild_handlers
239
+ self.tags_formatter = :legacy
173
240
  end
174
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
@@ -10,15 +10,16 @@ module ActiveRecord
10
10
  :first_or_create, :first_or_create!, :first_or_initialize,
11
11
  :find_or_create_by, :find_or_create_by!, :find_or_initialize_by,
12
12
  :create_or_find_by, :create_or_find_by!,
13
- :destroy_all, :delete_all, :update_all, :touch_all, :destroy_by, :delete_by,
13
+ :destroy, :destroy_all, :delete, :delete_all, :update_all, :touch_all, :destroy_by, :delete_by,
14
14
  :find_each, :find_in_batches, :in_batches,
15
15
  :select, :reselect, :order, :regroup, :in_order_of, :reorder, :group, :limit, :offset, :joins, :left_joins, :left_outer_joins,
16
16
  :where, :rewhere, :invert_where, :preload, :extract_associated, :eager_load, :includes, :from, :lock, :readonly,
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,
20
+ :pluck, :pick, :ids, :async_ids, :strict_loading, :excluding, :without, :with, :with_recursive,
21
21
  :async_count, :async_average, :async_minimum, :async_maximum, :async_sum, :async_pluck, :async_pick,
22
+ :insert, :insert_all, :insert!, :insert_all!, :upsert, :upsert_all
22
23
  ].freeze # :nodoc:
23
24
  delegate(*QUERYING_METHODS, to: :all)
24
25
 
@@ -47,22 +48,29 @@ module ActiveRecord
47
48
  #
48
49
  # Note that building your own SQL query string from user input may expose your application to
49
50
  # injection attacks (https://guides.rubyonrails.org/security.html#sql-injection).
50
- def find_by_sql(sql, binds = [], preparable: nil, &block)
51
- _load_from_sql(_query_by_sql(sql, binds, preparable: preparable), &block)
51
+ def find_by_sql(sql, binds = [], preparable: nil, allow_retry: false, &block)
52
+ result = with_connection do |c|
53
+ _query_by_sql(c, sql, binds, preparable: preparable, allow_retry: allow_retry)
54
+ end
55
+ _load_from_sql(result, &block)
52
56
  end
53
57
 
54
58
  # Same as <tt>#find_by_sql</tt> but perform the query asynchronously and returns an ActiveRecord::Promise.
55
- def async_find_by_sql(sql, binds = [], preparable: nil, &block)
56
- _query_by_sql(sql, binds, preparable: preparable, async: true).then do |result|
59
+ def async_find_by_sql(sql, binds = [], preparable: nil, allow_retry: false, &block)
60
+ with_connection do |c|
61
+ _query_by_sql(c, sql, binds, preparable: preparable, allow_retry: allow_retry, async: true)
62
+ end.then do |result|
57
63
  _load_from_sql(result, &block)
58
64
  end
59
65
  end
60
66
 
61
- def _query_by_sql(sql, binds = [], preparable: nil, async: false) # :nodoc:
62
- connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable, async: async)
67
+ def _query_by_sql(connection, sql, binds = [], preparable: nil, async: false, allow_retry: false) # :nodoc:
68
+ connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable, async: async, allow_retry: allow_retry)
63
69
  end
64
70
 
65
71
  def _load_from_sql(result_set, &block) # :nodoc:
72
+ return [] if result_set.empty?
73
+
66
74
  column_types = result_set.column_types
67
75
 
68
76
  unless column_types.empty?
@@ -78,10 +86,10 @@ module ActiveRecord
78
86
 
79
87
  message_bus.instrument("instantiation.active_record", payload) do
80
88
  if result_set.includes_column?(inheritance_column)
81
- result_set.map { |record| instantiate(record, column_types, &block) }
89
+ result_set.indexed_rows.map { |record| instantiate(record, column_types, &block) }
82
90
  else
83
91
  # Instantiate a homogeneous set
84
- result_set.map { |record| instantiate_instance_of(self, record, column_types, &block) }
92
+ result_set.indexed_rows.map { |record| instantiate_instance_of(self, record, column_types, &block) }
85
93
  end
86
94
  end
87
95
  end
@@ -99,12 +107,16 @@ module ActiveRecord
99
107
  #
100
108
  # * +sql+ - An SQL statement which should return a count query from the database, see the example above.
101
109
  def count_by_sql(sql)
102
- connection.select_value(sanitize_sql(sql), "#{name} Count").to_i
110
+ with_connection do |c|
111
+ c.select_value(sanitize_sql(sql), "#{name} Count").to_i
112
+ end
103
113
  end
104
114
 
105
115
  # Same as <tt>#count_by_sql</tt> but perform the query asynchronously and returns an ActiveRecord::Promise.
106
116
  def async_count_by_sql(sql)
107
- connection.select_value(sanitize_sql(sql), "#{name} Count", async: true).then(&:to_i)
117
+ with_connection do |c|
118
+ c.select_value(sanitize_sql(sql), "#{name} Count", async: true).then(&:to_i)
119
+ end
108
120
  end
109
121
  end
110
122
  end
@@ -31,7 +31,6 @@ module ActiveRecord
31
31
  config.active_record.check_schema_cache_dump_version = true
32
32
  config.active_record.maintain_test_schema = true
33
33
  config.active_record.has_many_inversing = false
34
- config.active_record.sqlite3_production_warning = true
35
34
  config.active_record.query_log_tags_enabled = false
36
35
  config.active_record.query_log_tags = [ :application ]
37
36
  config.active_record.query_log_tags_format = :legacy
@@ -70,6 +69,7 @@ module ActiveRecord
70
69
  Rails.logger.broadcast_to(console)
71
70
  end
72
71
  ActiveRecord.verbose_query_logs = false
72
+ ActiveRecord::Base.attributes_for_inspect = :all
73
73
  end
74
74
 
75
75
  runner do
@@ -88,7 +88,9 @@ module ActiveRecord
88
88
 
89
89
  initializer "active_record.postgresql_time_zone_aware_types" do
90
90
  ActiveSupport.on_load(:active_record_postgresqladapter) do
91
- ActiveRecord::Base.time_zone_aware_types << :timestamptz
91
+ ActiveSupport.on_load(:active_record) do
92
+ ActiveRecord::Base.time_zone_aware_types << :timestamptz
93
+ end
92
94
  end
93
95
  end
94
96
 
@@ -133,49 +135,11 @@ To keep using the current cache store, you can turn off cache versioning entirel
133
135
  end
134
136
  end
135
137
 
136
- initializer "active_record.use_schema_cache_dump" do
137
- ActiveRecord::ConnectionAdapters::SchemaReflection.use_schema_cache_dump = config.active_record.use_schema_cache_dump
138
- end
139
-
140
- initializer "active_record.check_schema_cache_dump" do
141
- check_schema_cache_dump_version = config.active_record.check_schema_cache_dump_version
142
-
143
- ActiveRecord::ConnectionAdapters::SchemaReflection.check_schema_cache_dump_version = check_schema_cache_dump_version
144
-
145
- if config.active_record.use_schema_cache_dump && !config.active_record.lazily_load_schema_cache
146
- config.after_initialize do |app|
147
- ActiveSupport.on_load(:active_record) do
148
- db_config = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).first
149
- next if db_config.nil?
150
-
151
- filename = ActiveRecord::Tasks::DatabaseTasks.cache_dump_filename(
152
- db_config.name,
153
- schema_cache_path: db_config.schema_cache_path
154
- )
138
+ initializer "active_record.copy_schema_cache_config" do
139
+ active_record_config = config.active_record
155
140
 
156
- cache = ActiveRecord::ConnectionAdapters::SchemaCache._load_from(filename)
157
- next if cache.nil?
158
-
159
- if check_schema_cache_dump_version
160
- current_version = begin
161
- ActiveRecord::Migrator.current_version
162
- rescue ActiveRecordError => error
163
- warn "Failed to validate the schema cache because of #{error.class}: #{error.message}"
164
- nil
165
- end
166
- next if current_version.nil?
167
-
168
- if cache.schema_version != current_version
169
- warn "Ignoring #{filename} because it has expired. The current schema version is #{current_version}, but the one in the schema cache file is #{cache.schema_version}."
170
- next
171
- end
172
- end
173
-
174
- Rails.logger.info("Using schema cache file #{filename}")
175
- connection_pool.schema_reflection.set_schema_cache(cache)
176
- end
177
- end
178
- end
141
+ ActiveRecord::ConnectionAdapters::SchemaReflection.use_schema_cache_dump = active_record_config.use_schema_cache_dump
142
+ ActiveRecord::ConnectionAdapters::SchemaReflection.check_schema_cache_dump_version = active_record_config.check_schema_cache_dump_version
179
143
  end
180
144
 
181
145
  initializer "active_record.define_attribute_methods" do |app|
@@ -198,7 +162,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
198
162
  # Additionally if `check_schema_cache_dump_version` is enabled (which is the default),
199
163
  # loading the schema cache dump trigger a database connection to compare the schema
200
164
  # versions.
201
- # This means the attribute methods will be lazily defined whent the model is accessed,
165
+ # This means the attribute methods will be lazily defined when the model is accessed,
202
166
  # likely as part of the first few requests or jobs. This isn't good for performance
203
167
  # but we unfortunately have to arbitrate between resiliency and performance, and chose
204
168
  # resiliency.
@@ -220,24 +184,6 @@ To keep using the current cache store, you can turn off cache versioning entirel
220
184
  end
221
185
  end
222
186
 
223
- initializer "active_record.warn_on_records_fetched_greater_than" do
224
- if config.active_record.warn_on_records_fetched_greater_than
225
- ActiveSupport.on_load(:active_record) do
226
- require "active_record/relation/record_fetch_warning"
227
- end
228
- end
229
- end
230
-
231
- SQLITE3_PRODUCTION_WARN = "You are running SQLite in production, this is generally not recommended."\
232
- " You can disable this warning by setting \"config.active_record.sqlite3_production_warning=false\"."
233
- initializer "active_record.sqlite3_production_warning" do
234
- if config.active_record.sqlite3_production_warning && Rails.env.production?
235
- ActiveSupport.on_load(:active_record_sqlite3adapter) do
236
- Rails.logger.warn(SQLITE3_PRODUCTION_WARN)
237
- end
238
- end
239
- end
240
-
241
187
  initializer "active_record.sqlite3_adapter_strict_strings_by_default" do
242
188
  config.after_initialize do
243
189
  if config.active_record.sqlite3_adapter_strict_strings_by_default
@@ -248,6 +194,16 @@ To keep using the current cache store, you can turn off cache versioning entirel
248
194
  end
249
195
  end
250
196
 
197
+ initializer "active_record.postgresql_adapter_decode_dates" do
198
+ config.after_initialize do
199
+ if config.active_record.postgresql_adapter_decode_dates
200
+ ActiveSupport.on_load(:active_record_postgresqladapter) do
201
+ self.decode_dates = true
202
+ end
203
+ end
204
+ end
205
+ end
206
+
251
207
  initializer "active_record.set_configs" do |app|
252
208
  configs = app.config.active_record
253
209
 
@@ -273,10 +229,10 @@ To keep using the current cache store, you can turn off cache versioning entirel
273
229
  :query_log_tags,
274
230
  :query_log_tags_format,
275
231
  :cache_query_log_tags,
276
- :sqlite3_production_warning,
277
232
  :sqlite3_adapter_strict_strings_by_default,
278
233
  :check_schema_cache_dump_version,
279
- :use_schema_cache_dump
234
+ :use_schema_cache_dump,
235
+ :postgresql_adapter_decode_dates,
280
236
  )
281
237
 
282
238
  configs_used_in_other_initializers.each do |k, v|
@@ -332,6 +288,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
332
288
  initializer "active_record.set_executor_hooks" do
333
289
  ActiveRecord::QueryCache.install_executor_hooks
334
290
  ActiveRecord::AsynchronousQueriesTracker.install_executor_hooks
291
+ ActiveRecord::ConnectionAdapters::ConnectionPool.install_executor_hooks
335
292
  end
336
293
 
337
294
  initializer "active_record.add_watchable_files" do |app|
@@ -409,12 +366,13 @@ To keep using the current cache store, you can turn off cache versioning entirel
409
366
  config.after_initialize do
410
367
  if app.config.active_record.query_log_tags_enabled
411
368
  ActiveRecord.query_transformers << ActiveRecord::QueryLogs
412
- ActiveRecord::QueryLogs.taggings.merge!(
369
+ ActiveRecord::QueryLogs.taggings = ActiveRecord::QueryLogs.taggings.merge(
413
370
  application: Rails.application.class.name.split("::").first,
414
371
  pid: -> { Process.pid.to_s },
415
372
  socket: ->(context) { context[:connection].pool.db_config.socket },
416
373
  db_host: ->(context) { context[:connection].pool.db_config.host },
417
- database: ->(context) { context[:connection].pool.db_config.database }
374
+ database: ->(context) { context[:connection].pool.db_config.database },
375
+ source_location: -> { QueryLogs.query_source_location }
418
376
  )
419
377
  ActiveRecord.disable_prepared_statements = true
420
378
 
@@ -423,7 +381,7 @@ To keep using the current cache store, you can turn off cache versioning entirel
423
381
  end
424
382
 
425
383
  if app.config.active_record.query_log_tags_format
426
- ActiveRecord::QueryLogs.update_formatter(app.config.active_record.query_log_tags_format)
384
+ ActiveRecord::QueryLogs.tags_formatter = app.config.active_record.query_log_tags_format
427
385
  end
428
386
 
429
387
  if app.config.active_record.cache_query_log_tags
@@ -11,7 +11,14 @@ module ActiveRecord
11
11
  module ClassMethods # :nodoc:
12
12
  def log_process_action(payload)
13
13
  messages, db_runtime = super, payload[:db_runtime]
14
- messages << ("ActiveRecord: %.1fms" % db_runtime.to_f) if db_runtime
14
+
15
+ if db_runtime
16
+ queries_count = payload[:queries_count] || 0
17
+ cached_queries_count = payload[:cached_queries_count] || 0
18
+ messages << ("ActiveRecord: %.1fms (%d %s, %d cached)" % [db_runtime.to_f, queries_count,
19
+ "query".pluralize(queries_count), cached_queries_count])
20
+ end
21
+
15
22
  messages
16
23
  end
17
24
  end
@@ -34,11 +41,11 @@ module ActiveRecord
34
41
 
35
42
  def cleanup_view_runtime
36
43
  if logger && logger.info?
37
- db_rt_before_render = ActiveRecord::RuntimeRegistry.reset
44
+ db_rt_before_render = ActiveRecord::RuntimeRegistry.reset_runtimes
38
45
  self.db_runtime = (db_runtime || 0) + db_rt_before_render
39
46
  runtime = super
40
47
  queries_rt = ActiveRecord::RuntimeRegistry.sql_runtime - ActiveRecord::RuntimeRegistry.async_sql_runtime
41
- db_rt_after_render = ActiveRecord::RuntimeRegistry.reset
48
+ db_rt_after_render = ActiveRecord::RuntimeRegistry.reset_runtimes
42
49
  self.db_runtime += db_rt_after_render
43
50
  runtime - queries_rt
44
51
  else
@@ -49,7 +56,9 @@ module ActiveRecord
49
56
  def append_info_to_payload(payload)
50
57
  super
51
58
 
52
- payload[:db_runtime] = (db_runtime || 0) + ActiveRecord::RuntimeRegistry.reset
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
53
62
  end
54
63
  end
55
64
  end