activerecord 6.1.6 → 7.0.4

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (243) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1314 -975
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/aggregations.rb +1 -1
  5. data/lib/active_record/association_relation.rb +0 -10
  6. data/lib/active_record/associations/association.rb +33 -17
  7. data/lib/active_record/associations/association_scope.rb +1 -3
  8. data/lib/active_record/associations/belongs_to_association.rb +15 -4
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  10. data/lib/active_record/associations/builder/association.rb +8 -2
  11. data/lib/active_record/associations/builder/belongs_to.rb +19 -6
  12. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  13. data/lib/active_record/associations/builder/has_many.rb +3 -2
  14. data/lib/active_record/associations/builder/has_one.rb +2 -1
  15. data/lib/active_record/associations/builder/singular_association.rb +2 -2
  16. data/lib/active_record/associations/collection_association.rb +19 -21
  17. data/lib/active_record/associations/collection_proxy.rb +10 -5
  18. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  19. data/lib/active_record/associations/has_many_association.rb +8 -5
  20. data/lib/active_record/associations/has_many_through_association.rb +2 -1
  21. data/lib/active_record/associations/has_one_association.rb +10 -7
  22. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  23. data/lib/active_record/associations/join_dependency.rb +23 -15
  24. data/lib/active_record/associations/preloader/association.rb +186 -52
  25. data/lib/active_record/associations/preloader/batch.rb +48 -0
  26. data/lib/active_record/associations/preloader/branch.rb +147 -0
  27. data/lib/active_record/associations/preloader/through_association.rb +49 -13
  28. data/lib/active_record/associations/preloader.rb +39 -113
  29. data/lib/active_record/associations/singular_association.rb +8 -2
  30. data/lib/active_record/associations/through_association.rb +3 -3
  31. data/lib/active_record/associations.rb +124 -95
  32. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  33. data/lib/active_record/attribute_assignment.rb +1 -1
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  35. data/lib/active_record/attribute_methods/dirty.rb +49 -16
  36. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  37. data/lib/active_record/attribute_methods/query.rb +2 -2
  38. data/lib/active_record/attribute_methods/read.rb +7 -5
  39. data/lib/active_record/attribute_methods/serialization.rb +57 -19
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +8 -3
  41. data/lib/active_record/attribute_methods/write.rb +7 -10
  42. data/lib/active_record/attribute_methods.rb +14 -15
  43. data/lib/active_record/attributes.rb +24 -35
  44. data/lib/active_record/autosave_association.rb +8 -23
  45. data/lib/active_record/base.rb +19 -1
  46. data/lib/active_record/callbacks.rb +2 -2
  47. data/lib/active_record/coders/yaml_column.rb +10 -2
  48. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  50. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -561
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +42 -72
  56. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
  57. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +38 -13
  58. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +80 -24
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +149 -74
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +105 -81
  63. data/lib/active_record/connection_adapters/column.rb +4 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -24
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +37 -21
  66. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -1
  68. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  69. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  70. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -1
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
  72. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  75. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  76. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  79. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  80. data/lib/active_record/connection_adapters/postgresql/quoting.rb +51 -51
  81. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -0
  82. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  83. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
  84. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  85. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +37 -19
  86. data/lib/active_record/connection_adapters/postgresql_adapter.rb +208 -107
  87. data/lib/active_record/connection_adapters/schema_cache.rb +39 -38
  88. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
  89. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +28 -16
  90. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +17 -15
  91. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +96 -32
  92. data/lib/active_record/connection_adapters.rb +6 -5
  93. data/lib/active_record/connection_handling.rb +49 -55
  94. data/lib/active_record/core.rb +124 -134
  95. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  96. data/lib/active_record/database_configurations/database_config.rb +12 -9
  97. data/lib/active_record/database_configurations/hash_config.rb +63 -5
  98. data/lib/active_record/database_configurations/url_config.rb +2 -2
  99. data/lib/active_record/database_configurations.rb +15 -32
  100. data/lib/active_record/delegated_type.rb +53 -12
  101. data/lib/active_record/destroy_association_async_job.rb +1 -1
  102. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  103. data/lib/active_record/dynamic_matchers.rb +1 -1
  104. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  105. data/lib/active_record/encryption/cipher.rb +53 -0
  106. data/lib/active_record/encryption/config.rb +44 -0
  107. data/lib/active_record/encryption/configurable.rb +67 -0
  108. data/lib/active_record/encryption/context.rb +35 -0
  109. data/lib/active_record/encryption/contexts.rb +72 -0
  110. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  111. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  112. data/lib/active_record/encryption/encryptable_record.rb +206 -0
  113. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  114. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  115. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  116. data/lib/active_record/encryption/encryptor.rb +155 -0
  117. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  118. data/lib/active_record/encryption/errors.rb +15 -0
  119. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  120. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  121. data/lib/active_record/encryption/key.rb +28 -0
  122. data/lib/active_record/encryption/key_generator.rb +42 -0
  123. data/lib/active_record/encryption/key_provider.rb +46 -0
  124. data/lib/active_record/encryption/message.rb +33 -0
  125. data/lib/active_record/encryption/message_serializer.rb +90 -0
  126. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  127. data/lib/active_record/encryption/properties.rb +76 -0
  128. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  129. data/lib/active_record/encryption/scheme.rb +99 -0
  130. data/lib/active_record/encryption.rb +55 -0
  131. data/lib/active_record/enum.rb +50 -43
  132. data/lib/active_record/errors.rb +67 -4
  133. data/lib/active_record/explain_registry.rb +11 -6
  134. data/lib/active_record/fixture_set/file.rb +15 -1
  135. data/lib/active_record/fixture_set/table_row.rb +41 -6
  136. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  137. data/lib/active_record/fixtures.rb +20 -23
  138. data/lib/active_record/future_result.rb +139 -0
  139. data/lib/active_record/gem_version.rb +4 -4
  140. data/lib/active_record/inheritance.rb +55 -17
  141. data/lib/active_record/insert_all.rb +80 -14
  142. data/lib/active_record/integration.rb +4 -3
  143. data/lib/active_record/internal_metadata.rb +1 -5
  144. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  145. data/lib/active_record/locking/optimistic.rb +10 -9
  146. data/lib/active_record/locking/pessimistic.rb +10 -4
  147. data/lib/active_record/log_subscriber.rb +23 -7
  148. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  149. data/lib/active_record/middleware/database_selector.rb +18 -6
  150. data/lib/active_record/middleware/shard_selector.rb +60 -0
  151. data/lib/active_record/migration/command_recorder.rb +7 -7
  152. data/lib/active_record/migration/compatibility.rb +84 -2
  153. data/lib/active_record/migration/join_table.rb +1 -1
  154. data/lib/active_record/migration.rb +114 -83
  155. data/lib/active_record/model_schema.rb +58 -59
  156. data/lib/active_record/nested_attributes.rb +13 -12
  157. data/lib/active_record/no_touching.rb +3 -3
  158. data/lib/active_record/null_relation.rb +2 -6
  159. data/lib/active_record/persistence.rb +228 -60
  160. data/lib/active_record/query_cache.rb +2 -2
  161. data/lib/active_record/query_logs.rb +138 -0
  162. data/lib/active_record/querying.rb +16 -6
  163. data/lib/active_record/railtie.rb +136 -22
  164. data/lib/active_record/railties/controller_runtime.rb +1 -1
  165. data/lib/active_record/railties/databases.rake +78 -136
  166. data/lib/active_record/readonly_attributes.rb +11 -0
  167. data/lib/active_record/reflection.rb +73 -50
  168. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  169. data/lib/active_record/relation/batches.rb +6 -6
  170. data/lib/active_record/relation/calculations.rb +43 -38
  171. data/lib/active_record/relation/delegation.rb +7 -7
  172. data/lib/active_record/relation/finder_methods.rb +31 -35
  173. data/lib/active_record/relation/merger.rb +20 -13
  174. data/lib/active_record/relation/predicate_builder.rb +1 -6
  175. data/lib/active_record/relation/query_attribute.rb +5 -11
  176. data/lib/active_record/relation/query_methods.rb +276 -67
  177. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  178. data/lib/active_record/relation/spawn_methods.rb +2 -2
  179. data/lib/active_record/relation/where_clause.rb +10 -19
  180. data/lib/active_record/relation.rb +189 -88
  181. data/lib/active_record/result.rb +17 -7
  182. data/lib/active_record/runtime_registry.rb +9 -13
  183. data/lib/active_record/sanitization.rb +17 -12
  184. data/lib/active_record/schema.rb +38 -23
  185. data/lib/active_record/schema_dumper.rb +25 -19
  186. data/lib/active_record/schema_migration.rb +4 -4
  187. data/lib/active_record/scoping/default.rb +60 -13
  188. data/lib/active_record/scoping/named.rb +3 -11
  189. data/lib/active_record/scoping.rb +64 -34
  190. data/lib/active_record/serialization.rb +6 -1
  191. data/lib/active_record/signed_id.rb +3 -3
  192. data/lib/active_record/store.rb +7 -2
  193. data/lib/active_record/suppressor.rb +11 -15
  194. data/lib/active_record/tasks/database_tasks.rb +127 -60
  195. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  196. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -13
  197. data/lib/active_record/test_databases.rb +1 -1
  198. data/lib/active_record/test_fixtures.rb +16 -9
  199. data/lib/active_record/timestamp.rb +3 -4
  200. data/lib/active_record/transactions.rb +9 -14
  201. data/lib/active_record/translation.rb +3 -3
  202. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  203. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  204. data/lib/active_record/type/internal/timezone.rb +2 -2
  205. data/lib/active_record/type/serialized.rb +1 -1
  206. data/lib/active_record/type/type_map.rb +17 -20
  207. data/lib/active_record/type.rb +1 -2
  208. data/lib/active_record/validations/associated.rb +4 -4
  209. data/lib/active_record/validations/presence.rb +2 -2
  210. data/lib/active_record/validations/uniqueness.rb +4 -4
  211. data/lib/active_record/version.rb +1 -1
  212. data/lib/active_record.rb +217 -27
  213. data/lib/arel/attributes/attribute.rb +0 -8
  214. data/lib/arel/crud.rb +28 -22
  215. data/lib/arel/delete_manager.rb +18 -4
  216. data/lib/arel/filter_predications.rb +9 -0
  217. data/lib/arel/insert_manager.rb +2 -3
  218. data/lib/arel/nodes/casted.rb +1 -1
  219. data/lib/arel/nodes/delete_statement.rb +12 -13
  220. data/lib/arel/nodes/filter.rb +10 -0
  221. data/lib/arel/nodes/function.rb +1 -0
  222. data/lib/arel/nodes/insert_statement.rb +2 -2
  223. data/lib/arel/nodes/select_core.rb +2 -2
  224. data/lib/arel/nodes/select_statement.rb +2 -2
  225. data/lib/arel/nodes/update_statement.rb +8 -3
  226. data/lib/arel/nodes.rb +1 -0
  227. data/lib/arel/predications.rb +11 -3
  228. data/lib/arel/select_manager.rb +10 -4
  229. data/lib/arel/table.rb +0 -1
  230. data/lib/arel/tree_manager.rb +0 -12
  231. data/lib/arel/update_manager.rb +18 -4
  232. data/lib/arel/visitors/dot.rb +80 -90
  233. data/lib/arel/visitors/mysql.rb +8 -2
  234. data/lib/arel/visitors/postgresql.rb +0 -10
  235. data/lib/arel/visitors/to_sql.rb +58 -2
  236. data/lib/arel.rb +2 -1
  237. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  238. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  239. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  240. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  241. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  242. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  243. metadata +55 -11
@@ -76,6 +76,11 @@ module ActiveRecord
76
76
  other.is_a?(WhereClause) &&
77
77
  predicates == other.predicates
78
78
  end
79
+ alias :eql? :==
80
+
81
+ def hash
82
+ [self.class, predicates].hash
83
+ end
79
84
 
80
85
  def invert
81
86
  if predicates.size == 1
@@ -158,21 +163,8 @@ module ActiveRecord
158
163
  attr = extract_attribute(node) || begin
159
164
  node.left if equality_node?(node) && node.left.is_a?(Arel::Predications)
160
165
  end
161
- next false unless attr
162
-
163
- ref = referenced_columns[attr]
164
- next false unless ref
165
-
166
- if equality_node?(node) && equality_node?(ref) || node == ref
167
- true
168
- else
169
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
170
- Merging (#{node.to_sql}) and (#{ref.to_sql}) no longer maintain
171
- both conditions, and will be replaced by the latter in Rails 7.0.
172
- To migrate to Rails 7.0's behavior, use `relation.merge(other, rewhere: true)`.
173
- MSG
174
- false
175
- end
166
+
167
+ attr && referenced_columns[attr]
176
168
  end
177
169
  end
178
170
 
@@ -227,11 +219,10 @@ module ActiveRecord
227
219
  end
228
220
 
229
221
  def extract_node_value(node)
230
- case node
231
- when Array
232
- node.map { |v| extract_node_value(v) }
233
- when Arel::Nodes::BindParam, Arel::Nodes::Casted, Arel::Nodes::Quoted
222
+ if node.respond_to?(:value_before_type_cast)
234
223
  node.value_before_type_cast
224
+ elsif Array === node
225
+ node.map { |v| extract_node_value(v) }
235
226
  end
236
227
  end
237
228
  end
@@ -11,7 +11,7 @@ module ActiveRecord
11
11
  :reverse_order, :distinct, :create_with, :skip_query_cache]
12
12
 
13
13
  CLAUSE_METHODS = [:where, :having, :from]
14
- INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :group, :having]
14
+ INVALID_METHODS_FOR_DELETE_ALL = [:distinct]
15
15
 
16
16
  VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS + CLAUSE_METHODS
17
17
 
@@ -31,6 +31,8 @@ module ActiveRecord
31
31
  @loaded = false
32
32
  @predicate_builder = predicate_builder
33
33
  @delegate_to_klass = false
34
+ @future_result = nil
35
+ @records = nil
34
36
  end
35
37
 
36
38
  def initialize_copy(other)
@@ -38,11 +40,6 @@ module ActiveRecord
38
40
  reset
39
41
  end
40
42
 
41
- def arel_attribute(name) # :nodoc:
42
- table[name]
43
- end
44
- deprecate :arel_attribute
45
-
46
43
  def bind_attribute(name, value) # :nodoc:
47
44
  if reflection = klass._reflect_on_association(name)
48
45
  name = reflection.foreign_key
@@ -67,8 +64,12 @@ module ActiveRecord
67
64
  # user = users.new { |user| user.name = 'Oscar' }
68
65
  # user.name # => Oscar
69
66
  def new(attributes = nil, &block)
70
- block = current_scope_restoring_block(&block)
71
- scoping { _new(attributes, &block) }
67
+ if attributes.is_a?(Array)
68
+ attributes.collect { |attr| new(attr, &block) }
69
+ else
70
+ block = current_scope_restoring_block(&block)
71
+ scoping { _new(attributes, &block) }
72
+ end
72
73
  end
73
74
  alias build new
74
75
 
@@ -148,7 +149,7 @@ module ActiveRecord
148
149
  # above can be alternatively written this way:
149
150
  #
150
151
  # # Find the first user named "Scarlett" or create a new one with a
151
- # # different last name.
152
+ # # particular last name.
152
153
  # User.find_or_create_by(first_name: 'Scarlett') do |user|
153
154
  # user.last_name = 'Johansson'
154
155
  # end
@@ -175,7 +176,7 @@ module ActiveRecord
175
176
  find_by(attributes) || create!(attributes, &block)
176
177
  end
177
178
 
178
- # Attempts to create a record with the given attributes in a table that has a unique constraint
179
+ # Attempts to create a record with the given attributes in a table that has a unique database constraint
179
180
  # on one or several of its columns. If a row already exists with one or several of these
180
181
  # unique constraints, the exception such an insertion would normally raise is caught,
181
182
  # and the existing record with those attributes is found using #find_by!.
@@ -186,7 +187,7 @@ module ActiveRecord
186
187
  #
187
188
  # There are several drawbacks to #create_or_find_by, though:
188
189
  #
189
- # * The underlying table must have the relevant columns defined with unique constraints.
190
+ # * The underlying table must have the relevant columns defined with unique database constraints.
190
191
  # * A unique constraint violation may be triggered by only one, or at least less than all,
191
192
  # of the given attributes. This means that the subsequent #find_by! may fail to find a
192
193
  # matching record, which will then raise an <tt>ActiveRecord::RecordNotFound</tt> exception,
@@ -257,13 +258,20 @@ module ActiveRecord
257
258
 
258
259
  # Returns size of the records.
259
260
  def size
260
- loaded? ? @records.length : count(:all)
261
+ if loaded?
262
+ records.length
263
+ else
264
+ count(:all)
265
+ end
261
266
  end
262
267
 
263
268
  # Returns true if there are no records.
264
269
  def empty?
265
- return @records.empty? if loaded?
266
- !exists?
270
+ if loaded?
271
+ records.empty?
272
+ else
273
+ !exists?
274
+ end
267
275
  end
268
276
 
269
277
  # Returns true if there are no records.
@@ -281,13 +289,15 @@ module ActiveRecord
281
289
  # Returns true if there is exactly one record.
282
290
  def one?
283
291
  return super if block_given?
284
- limit_value ? records.one? : size == 1
292
+ return records.one? if loaded?
293
+ limited_count == 1
285
294
  end
286
295
 
287
296
  # Returns true if there is more than one record.
288
297
  def many?
289
298
  return super if block_given?
290
- limit_value ? records.many? : size > 1
299
+ return records.many? if loaded?
300
+ limited_count > 1
291
301
  end
292
302
 
293
303
  # Returns a stable cache key that can be used to identify this query.
@@ -344,7 +354,7 @@ module ActiveRecord
344
354
  def compute_cache_version(timestamp_column) # :nodoc:
345
355
  timestamp_column = timestamp_column.to_s
346
356
 
347
- if loaded? || distinct_value
357
+ if loaded?
348
358
  size = records.size
349
359
  if size > 0
350
360
  timestamp = records.map { |record| record.read_attribute(timestamp_column) }.max
@@ -357,6 +367,7 @@ module ActiveRecord
357
367
 
358
368
  if collection.has_limit_or_offset?
359
369
  query = collection.select("#{column} AS collection_cache_key_timestamp")
370
+ query._select!(table[Arel.star]) if distinct_value && collection.select_values.empty?
360
371
  subquery_alias = "subquery_for_cache_key"
361
372
  subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
362
373
  arel = query.build_subquery(subquery_alias, select_values % subquery_column)
@@ -377,7 +388,7 @@ module ActiveRecord
377
388
  end
378
389
 
379
390
  if timestamp
380
- "#{size}-#{timestamp.utc.to_s(cache_timestamp_format)}"
391
+ "#{size}-#{timestamp.utc.to_fs(cache_timestamp_format)}"
381
392
  else
382
393
  "#{size}"
383
394
  end
@@ -400,15 +411,28 @@ module ActiveRecord
400
411
  # end
401
412
  # # => SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1
402
413
  #
414
+ # If <tt>all_queries: true</tt> is passed, scoping will apply to all queries
415
+ # for the relation including +update+ and +delete+ on instances.
416
+ # Once +all_queries+ is set to true it cannot be set to false in a
417
+ # nested block.
418
+ #
403
419
  # Please check unscoped if you want to remove all previous scopes (including
404
420
  # the default_scope) during the execution of a block.
405
- def scoping
406
- already_in_scope? ? yield : _scoping(self) { yield }
421
+ def scoping(all_queries: nil, &block)
422
+ registry = klass.scope_registry
423
+ if global_scope?(registry) && all_queries == false
424
+ raise ArgumentError, "Scoping is set to apply to all queries and cannot be unset in a nested block."
425
+ elsif already_in_scope?(registry)
426
+ yield
427
+ else
428
+ _scoping(self, registry, all_queries, &block)
429
+ end
407
430
  end
408
431
 
409
- def _exec_scope(*args, &block) # :nodoc:
432
+ def _exec_scope(...) # :nodoc:
410
433
  @delegate_to_klass = true
411
- _scoping(nil) { instance_exec(*args, &block) || self }
434
+ registry = klass.scope_registry
435
+ _scoping(nil, registry) { instance_exec(...) || self }
412
436
  ensure
413
437
  @delegate_to_klass = false
414
438
  end
@@ -440,17 +464,6 @@ module ActiveRecord
440
464
  def update_all(updates)
441
465
  raise ArgumentError, "Empty list of attributes to change" if updates.blank?
442
466
 
443
- arel = eager_loading? ? apply_join_dependency.arel : build_arel
444
- arel.source.left = table
445
-
446
- stmt = Arel::UpdateManager.new
447
- stmt.table(arel.source)
448
- stmt.key = table[primary_key]
449
- stmt.take(arel.limit)
450
- stmt.offset(arel.offset)
451
- stmt.order(*arel.orders)
452
- stmt.wheres = arel.constraints
453
-
454
467
  if updates.is_a?(Hash)
455
468
  if klass.locking_enabled? &&
456
469
  !updates.key?(klass.locking_column) &&
@@ -458,11 +471,17 @@ module ActiveRecord
458
471
  attr = table[klass.locking_column]
459
472
  updates[attr.name] = _increment_attribute(attr)
460
473
  end
461
- stmt.set _substitute_values(updates)
474
+ values = _substitute_values(updates)
462
475
  else
463
- stmt.set Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
476
+ values = Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
464
477
  end
465
478
 
479
+ arel = eager_loading? ? apply_join_dependency.arel : build_arel
480
+ arel.source.left = table
481
+
482
+ group_values_arel_columns = arel_columns(group_values.uniq)
483
+ having_clause_ast = having_clause.ast unless having_clause.empty?
484
+ stmt = arel.compile_update(values, table[primary_key], having_clause_ast, group_values_arel_columns)
466
485
  klass.connection.update(stmt, "#{klass} Update All").tap { reset }
467
486
  end
468
487
 
@@ -474,6 +493,14 @@ module ActiveRecord
474
493
  end
475
494
  end
476
495
 
496
+ def update!(id = :all, attributes) # :nodoc:
497
+ if id == :all
498
+ each { |record| record.update!(attributes) }
499
+ else
500
+ klass.update!(id, attributes)
501
+ end
502
+ end
503
+
477
504
  # Updates the counters of the records in the current relation.
478
505
  #
479
506
  # ==== Parameters
@@ -583,15 +610,11 @@ module ActiveRecord
583
610
  arel = eager_loading? ? apply_join_dependency.arel : build_arel
584
611
  arel.source.left = table
585
612
 
586
- stmt = Arel::DeleteManager.new
587
- stmt.from(arel.source)
588
- stmt.key = table[primary_key]
589
- stmt.take(arel.limit)
590
- stmt.offset(arel.offset)
591
- stmt.order(*arel.orders)
592
- stmt.wheres = arel.constraints
613
+ group_values_arel_columns = arel_columns(group_values.uniq)
614
+ having_clause_ast = having_clause.ast unless having_clause.empty?
615
+ stmt = arel.compile_delete(table[primary_key], having_clause_ast, group_values_arel_columns)
593
616
 
594
- klass.connection.delete(stmt, "#{klass} Destroy").tap { reset }
617
+ klass.connection.delete(stmt, "#{klass} Delete All").tap { reset }
595
618
  end
596
619
 
597
620
  # Finds and destroys all records matching the specified conditions.
@@ -620,6 +643,47 @@ module ActiveRecord
620
643
  where(*args).delete_all
621
644
  end
622
645
 
646
+ # Schedule the query to be performed from a background thread pool.
647
+ #
648
+ # Post.where(published: true).load_async # => #<ActiveRecord::Relation>
649
+ #
650
+ # When the +Relation+ is iterated, if the background query wasn't executed yet,
651
+ # it will be performed by the foreground thread.
652
+ #
653
+ # Note that {config.active_record.async_query_executor}[https://guides.rubyonrails.org/configuring.html#config-active-record-async-query-executor] must be configured
654
+ # for queries to actually be executed concurrently. Otherwise it defaults to
655
+ # executing them in the foreground.
656
+ #
657
+ # +load_async+ will also fall back to executing in the foreground in the test environment when transactional
658
+ # fixtures are enabled.
659
+ #
660
+ # If the query was actually executed in the background, the Active Record logs will show
661
+ # it by prefixing the log line with <tt>ASYNC</tt>:
662
+ #
663
+ # ASYNC Post Load (0.0ms) (db time 2ms) SELECT "posts".* FROM "posts" LIMIT 100
664
+ def load_async
665
+ return load if !connection.async_enabled?
666
+
667
+ unless loaded?
668
+ result = exec_main_query(async: connection.current_transaction.closed?)
669
+
670
+ if result.is_a?(Array)
671
+ @records = result
672
+ else
673
+ @future_result = result
674
+ end
675
+ @loaded = true
676
+ end
677
+
678
+ self
679
+ end
680
+
681
+ # Returns <tt>true</tt> if the relation was scheduled on the background
682
+ # thread pool.
683
+ def scheduled?
684
+ !!@future_result
685
+ end
686
+
623
687
  # Causes the records to be loaded from the database if they have not
624
688
  # been loaded already. You can use this if for some reason you need
625
689
  # to explicitly load some records before actually using them. The
@@ -627,7 +691,7 @@ module ActiveRecord
627
691
  #
628
692
  # Post.where(published: true).load # => #<ActiveRecord::Relation>
629
693
  def load(&block)
630
- unless loaded?
694
+ if !loaded? || scheduled?
631
695
  @records = exec_queries(&block)
632
696
  @loaded = true
633
697
  end
@@ -642,11 +706,14 @@ module ActiveRecord
642
706
  end
643
707
 
644
708
  def reset
709
+ @future_result&.cancel
710
+ @future_result = nil
645
711
  @delegate_to_klass = false
646
712
  @to_sql = @arel = @loaded = @should_eager_load = nil
647
713
  @offsets = @take = nil
648
714
  @cache_keys = nil
649
- @records = [].freeze
715
+ @cache_versions = nil
716
+ @records = nil
650
717
  self
651
718
  end
652
719
 
@@ -655,16 +722,14 @@ module ActiveRecord
655
722
  # User.where(name: 'Oscar').to_sql
656
723
  # # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
657
724
  def to_sql
658
- @to_sql ||= begin
659
- if eager_loading?
660
- apply_join_dependency do |relation, join_dependency|
661
- relation = join_dependency.apply_column_aliases(relation)
662
- relation.to_sql
663
- end
664
- else
665
- conn = klass.connection
666
- conn.unprepared_statement { conn.to_sql(arel) }
725
+ @to_sql ||= if eager_loading?
726
+ apply_join_dependency do |relation, join_dependency|
727
+ relation = join_dependency.apply_column_aliases(relation)
728
+ relation.to_sql
667
729
  end
730
+ else
731
+ conn = klass.connection
732
+ conn.unprepared_statement { conn.to_sql(arel) }
668
733
  end
669
734
  end
670
735
 
@@ -672,7 +737,7 @@ module ActiveRecord
672
737
  #
673
738
  # User.where(name: 'Oscar').where_values_hash
674
739
  # # => {name: "Oscar"}
675
- def where_values_hash(relation_table_name = klass.table_name)
740
+ def where_values_hash(relation_table_name = klass.table_name) # :nodoc:
676
741
  where_clause.to_h(relation_table_name)
677
742
  end
678
743
 
@@ -692,7 +757,7 @@ module ActiveRecord
692
757
  # Joins that are also marked for preloading. In which case we should just eager load them.
693
758
  # Note that this is a naive implementation because we could have strings and symbols which
694
759
  # represent the same association, but that aren't matched by this. Also, we could have
695
- # nested hashes which partially match, e.g. { a: :b } & { a: [:b, :c] }
760
+ # nested hashes which partially match, e.g. <tt>{ a: :b } & { a: [:b, :c] }</tt>
696
761
  def joined_includes_values
697
762
  includes_values & joins_values
698
763
  end
@@ -722,6 +787,10 @@ module ActiveRecord
722
787
  @values.dup
723
788
  end
724
789
 
790
+ def values_for_queries # :nodoc:
791
+ @values.except(:extending, :skip_query_cache, :strict_loading)
792
+ end
793
+
725
794
  def inspect
726
795
  subject = loaded? ? records : annotate("loading for inspect")
727
796
  entries = subject.take([limit_value, 11].compact.min).map!(&:inspect)
@@ -756,11 +825,9 @@ module ActiveRecord
756
825
  def preload_associations(records) # :nodoc:
757
826
  preload = preload_values
758
827
  preload += includes_values unless eager_loading?
759
- preloader = nil
760
828
  scope = strict_loading_value ? StrictLoadingScope : nil
761
829
  preload.each do |associations|
762
- preloader ||= build_preloader
763
- preloader.preload records, associations, scope
830
+ ActiveRecord::Associations::Preloader.new(records: records, associations: associations, scope: scope).call
764
831
  end
765
832
  end
766
833
 
@@ -775,8 +842,12 @@ module ActiveRecord
775
842
  end
776
843
 
777
844
  private
778
- def already_in_scope?
779
- @delegate_to_klass && klass.current_scope(true)
845
+ def already_in_scope?(registry)
846
+ @delegate_to_klass && registry.current_scope(klass, true)
847
+ end
848
+
849
+ def global_scope?(registry)
850
+ registry.global_current_scope(klass, true)
780
851
  end
781
852
 
782
853
  def current_scope_restoring_block(&block)
@@ -799,11 +870,20 @@ module ActiveRecord
799
870
  klass.create!(attributes, &block)
800
871
  end
801
872
 
802
- def _scoping(scope)
803
- previous, klass.current_scope = klass.current_scope(true), scope
873
+ def _scoping(scope, registry, all_queries = false)
874
+ previous = registry.current_scope(klass, true)
875
+ registry.set_current_scope(klass, scope)
876
+
877
+ if all_queries
878
+ previous_global = registry.global_current_scope(klass, true)
879
+ registry.set_global_current_scope(klass, scope)
880
+ end
804
881
  yield
805
882
  ensure
806
- klass.current_scope = previous
883
+ registry.set_current_scope(klass, previous)
884
+ if all_queries
885
+ registry.set_global_current_scope(klass, previous_global)
886
+ end
807
887
  end
808
888
 
809
889
  def _substitute_values(values)
@@ -826,44 +906,61 @@ module ActiveRecord
826
906
 
827
907
  def exec_queries(&block)
828
908
  skip_query_cache_if_necessary do
829
- records =
830
- if where_clause.contradiction?
831
- []
832
- elsif eager_loading?
833
- apply_join_dependency do |relation, join_dependency|
834
- if relation.null_relation?
835
- []
836
- else
837
- relation = join_dependency.apply_column_aliases(relation)
838
- rows = connection.select_all(relation.arel, "SQL")
839
- join_dependency.instantiate(rows, strict_loading_value, &block)
840
- end.freeze
841
- end
842
- else
843
- klass.find_by_sql(arel, &block).freeze
844
- end
909
+ rows = if scheduled?
910
+ future = @future_result
911
+ @future_result = nil
912
+ future.result
913
+ else
914
+ exec_main_query
915
+ end
845
916
 
917
+ records = instantiate_records(rows, &block)
846
918
  preload_associations(records) unless skip_preloading_value
847
919
 
848
920
  records.each(&:readonly!) if readonly_value
849
- records.each(&:strict_loading!) if strict_loading_value
921
+ records.each { |record| record.strict_loading!(strict_loading_value) } unless strict_loading_value.nil?
850
922
 
851
923
  records
852
924
  end
853
925
  end
854
926
 
855
- def skip_query_cache_if_necessary
856
- if skip_query_cache_value
857
- uncached do
858
- yield
927
+ def exec_main_query(async: false)
928
+ skip_query_cache_if_necessary do
929
+ if where_clause.contradiction?
930
+ [].freeze
931
+ elsif eager_loading?
932
+ apply_join_dependency do |relation, join_dependency|
933
+ if relation.null_relation?
934
+ [].freeze
935
+ else
936
+ relation = join_dependency.apply_column_aliases(relation)
937
+ @_join_dependency = join_dependency
938
+ connection.select_all(relation.arel, "SQL", async: async)
939
+ end
940
+ end
941
+ else
942
+ klass._query_by_sql(arel, async: async)
859
943
  end
944
+ end
945
+ end
946
+
947
+ def instantiate_records(rows, &block)
948
+ return [].freeze if rows.empty?
949
+ if eager_loading?
950
+ records = @_join_dependency.instantiate(rows, strict_loading_value, &block).freeze
951
+ @_join_dependency = nil
952
+ records
860
953
  else
861
- yield
954
+ klass._load_from_sql(rows, &block).freeze
862
955
  end
863
956
  end
864
957
 
865
- def build_preloader
866
- ActiveRecord::Associations::Preloader.new
958
+ def skip_query_cache_if_necessary(&block)
959
+ if skip_query_cache_value
960
+ uncached(&block)
961
+ else
962
+ yield
963
+ end
867
964
  end
868
965
 
869
966
  def references_eager_loaded_tables?
@@ -889,5 +986,9 @@ module ActiveRecord
889
986
  # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
890
987
  string.scan(/[a-zA-Z_][.\w]+(?=.?\.)/).map!(&:downcase) - ["raw_sql_"]
891
988
  end
989
+
990
+ def limited_count
991
+ limit_value ? count : limit(2).count
992
+ end
892
993
  end
893
994
  end
@@ -36,6 +36,10 @@ module ActiveRecord
36
36
 
37
37
  attr_reader :columns, :rows, :column_types
38
38
 
39
+ def self.empty # :nodoc:
40
+ EMPTY
41
+ end
42
+
39
43
  def initialize(columns, rows, column_types = {})
40
44
  @columns = columns
41
45
  @rows = rows
@@ -43,6 +47,9 @@ module ActiveRecord
43
47
  @column_types = column_types
44
48
  end
45
49
 
50
+ EMPTY = new([].freeze, [].freeze, {}.freeze)
51
+ private_constant :EMPTY
52
+
46
53
  # Returns true if this result set includes the column named +name+
47
54
  def includes_column?(name)
48
55
  @columns.include? name
@@ -57,19 +64,14 @@ module ActiveRecord
57
64
  # row as parameter.
58
65
  #
59
66
  # Returns an +Enumerator+ if no block is given.
60
- def each
67
+ def each(&block)
61
68
  if block_given?
62
- hash_rows.each { |row| yield row }
69
+ hash_rows.each(&block)
63
70
  else
64
71
  hash_rows.to_enum { @rows.size }
65
72
  end
66
73
  end
67
74
 
68
- alias :map! :map
69
- alias :collect! :map
70
- deprecate "map!": :map
71
- deprecate "collect!": :map
72
-
73
75
  # Returns true if there are no records, otherwise false.
74
76
  def empty?
75
77
  rows.empty?
@@ -91,6 +93,14 @@ module ActiveRecord
91
93
  n ? hash_rows.last(n) : hash_rows.last
92
94
  end
93
95
 
96
+ def result # :nodoc:
97
+ self
98
+ end
99
+
100
+ def cancel # :nodoc:
101
+ self
102
+ end
103
+
94
104
  def cast_values(type_overrides = {}) # :nodoc:
95
105
  if columns.one?
96
106
  # Separated to avoid allocating an array per row
@@ -1,24 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/per_thread_registry"
4
-
5
3
  module ActiveRecord
6
4
  # This is a thread locals registry for Active Record. For example:
7
5
  #
8
- # ActiveRecord::RuntimeRegistry.connection_handler
9
- #
10
- # returns the connection handler local to the current thread.
6
+ # ActiveRecord::RuntimeRegistry.sql_runtime
11
7
  #
12
- # See the documentation of ActiveSupport::PerThreadRegistry
13
- # for further details.
14
- class RuntimeRegistry # :nodoc:
15
- extend ActiveSupport::PerThreadRegistry
8
+ # returns the connection handler local to the current unit of execution (either thread of fiber).
9
+ module RuntimeRegistry # :nodoc:
10
+ extend self
16
11
 
17
- attr_accessor :sql_runtime
12
+ def sql_runtime
13
+ ActiveSupport::IsolatedExecutionState[:active_record_sql_runtime]
14
+ end
18
15
 
19
- [:sql_runtime].each do |val|
20
- class_eval %{ def self.#{val}; instance.#{val}; end }, __FILE__, __LINE__
21
- class_eval %{ def self.#{val}=(x); instance.#{val}=x; end }, __FILE__, __LINE__
16
+ def sql_runtime=(runtime)
17
+ ActiveSupport::IsolatedExecutionState[:active_record_sql_runtime] = runtime
22
18
  end
23
19
  end
24
20
  end