activerecord 6.1.4.1 → 7.0.2.3

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 (240) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1203 -922
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/active_record/aggregations.rb +1 -1
  6. data/lib/active_record/association_relation.rb +0 -10
  7. data/lib/active_record/associations/association.rb +33 -17
  8. data/lib/active_record/associations/association_scope.rb +1 -3
  9. data/lib/active_record/associations/belongs_to_association.rb +15 -4
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  11. data/lib/active_record/associations/builder/association.rb +8 -2
  12. data/lib/active_record/associations/builder/belongs_to.rb +19 -6
  13. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  14. data/lib/active_record/associations/builder/has_many.rb +3 -2
  15. data/lib/active_record/associations/builder/has_one.rb +2 -1
  16. data/lib/active_record/associations/builder/singular_association.rb +2 -2
  17. data/lib/active_record/associations/collection_association.rb +34 -27
  18. data/lib/active_record/associations/collection_proxy.rb +8 -3
  19. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  20. data/lib/active_record/associations/has_many_association.rb +1 -1
  21. data/lib/active_record/associations/has_many_through_association.rb +2 -1
  22. data/lib/active_record/associations/has_one_association.rb +10 -7
  23. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  24. data/lib/active_record/associations/join_dependency.rb +6 -2
  25. data/lib/active_record/associations/preloader/association.rb +187 -55
  26. data/lib/active_record/associations/preloader/batch.rb +48 -0
  27. data/lib/active_record/associations/preloader/branch.rb +147 -0
  28. data/lib/active_record/associations/preloader/through_association.rb +49 -13
  29. data/lib/active_record/associations/preloader.rb +39 -113
  30. data/lib/active_record/associations/singular_association.rb +8 -2
  31. data/lib/active_record/associations/through_association.rb +3 -3
  32. data/lib/active_record/associations.rb +119 -90
  33. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  34. data/lib/active_record/attribute_assignment.rb +1 -1
  35. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  36. data/lib/active_record/attribute_methods/dirty.rb +49 -16
  37. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  38. data/lib/active_record/attribute_methods/query.rb +2 -2
  39. data/lib/active_record/attribute_methods/read.rb +7 -5
  40. data/lib/active_record/attribute_methods/serialization.rb +66 -12
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  42. data/lib/active_record/attribute_methods/write.rb +7 -10
  43. data/lib/active_record/attribute_methods.rb +13 -14
  44. data/lib/active_record/attributes.rb +24 -35
  45. data/lib/active_record/autosave_association.rb +8 -23
  46. data/lib/active_record/base.rb +19 -1
  47. data/lib/active_record/callbacks.rb +2 -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 +78 -22
  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 +97 -81
  63. data/lib/active_record/connection_adapters/column.rb +4 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +38 -24
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +35 -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 +5 -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 +21 -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 +50 -50
  81. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -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 +35 -19
  86. data/lib/active_record/connection_adapters/postgresql_adapter.rb +207 -107
  87. data/lib/active_record/connection_adapters/schema_cache.rb +29 -4
  88. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +27 -19
  89. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +28 -16
  90. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +16 -14
  91. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +89 -30
  92. data/lib/active_record/connection_adapters.rb +6 -5
  93. data/lib/active_record/connection_handling.rb +47 -53
  94. data/lib/active_record/core.rb +122 -132
  95. data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -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 +16 -32
  100. data/lib/active_record/delegated_type.rb +52 -11
  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 +61 -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 +208 -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 +49 -42
  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 +17 -20
  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 +3 -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 +9 -3
  147. data/lib/active_record/log_subscriber.rb +14 -3
  148. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  149. data/lib/active_record/middleware/database_selector.rb +8 -3
  150. data/lib/active_record/middleware/shard_selector.rb +60 -0
  151. data/lib/active_record/migration/command_recorder.rb +4 -4
  152. data/lib/active_record/migration/compatibility.rb +107 -3
  153. data/lib/active_record/migration/join_table.rb +1 -1
  154. data/lib/active_record/migration.rb +109 -79
  155. data/lib/active_record/model_schema.rb +45 -58
  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 +219 -52
  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 +15 -5
  163. data/lib/active_record/railtie.rb +127 -17
  164. data/lib/active_record/railties/controller_runtime.rb +1 -1
  165. data/lib/active_record/railties/databases.rake +66 -129
  166. data/lib/active_record/readonly_attributes.rb +11 -0
  167. data/lib/active_record/reflection.rb +67 -50
  168. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  169. data/lib/active_record/relation/batches.rb +3 -3
  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 +249 -61
  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 +184 -84
  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 +11 -7
  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 +61 -12
  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 +1 -1
  191. data/lib/active_record/signed_id.rb +1 -1
  192. data/lib/active_record/suppressor.rb +11 -15
  193. data/lib/active_record/tasks/database_tasks.rb +120 -58
  194. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  195. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -12
  196. data/lib/active_record/test_databases.rb +1 -1
  197. data/lib/active_record/test_fixtures.rb +4 -4
  198. data/lib/active_record/timestamp.rb +3 -4
  199. data/lib/active_record/transactions.rb +9 -14
  200. data/lib/active_record/translation.rb +2 -2
  201. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  202. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  203. data/lib/active_record/type/internal/timezone.rb +2 -2
  204. data/lib/active_record/type/serialized.rb +1 -1
  205. data/lib/active_record/type/type_map.rb +17 -20
  206. data/lib/active_record/type.rb +1 -2
  207. data/lib/active_record/validations/associated.rb +1 -1
  208. data/lib/active_record/validations/uniqueness.rb +1 -1
  209. data/lib/active_record.rb +204 -28
  210. data/lib/arel/attributes/attribute.rb +0 -8
  211. data/lib/arel/crud.rb +28 -22
  212. data/lib/arel/delete_manager.rb +18 -4
  213. data/lib/arel/filter_predications.rb +9 -0
  214. data/lib/arel/insert_manager.rb +2 -3
  215. data/lib/arel/nodes/casted.rb +1 -1
  216. data/lib/arel/nodes/delete_statement.rb +12 -13
  217. data/lib/arel/nodes/filter.rb +10 -0
  218. data/lib/arel/nodes/function.rb +1 -0
  219. data/lib/arel/nodes/insert_statement.rb +2 -2
  220. data/lib/arel/nodes/select_core.rb +2 -2
  221. data/lib/arel/nodes/select_statement.rb +2 -2
  222. data/lib/arel/nodes/update_statement.rb +8 -3
  223. data/lib/arel/nodes.rb +1 -0
  224. data/lib/arel/predications.rb +11 -3
  225. data/lib/arel/select_manager.rb +10 -4
  226. data/lib/arel/table.rb +0 -1
  227. data/lib/arel/tree_manager.rb +0 -12
  228. data/lib/arel/update_manager.rb +18 -4
  229. data/lib/arel/visitors/dot.rb +80 -90
  230. data/lib/arel/visitors/mysql.rb +8 -2
  231. data/lib/arel/visitors/postgresql.rb +0 -10
  232. data/lib/arel/visitors/to_sql.rb +58 -2
  233. data/lib/arel.rb +2 -1
  234. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  235. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  236. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  237. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  238. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  239. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  240. metadata +56 -11
@@ -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
432
  def _exec_scope(*args, &block) # :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(*args, &block) || 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 fallback 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,13 @@ 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
+ @records = nil
650
716
  self
651
717
  end
652
718
 
@@ -655,16 +721,14 @@ module ActiveRecord
655
721
  # User.where(name: 'Oscar').to_sql
656
722
  # # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
657
723
  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) }
724
+ @to_sql ||= if eager_loading?
725
+ apply_join_dependency do |relation, join_dependency|
726
+ relation = join_dependency.apply_column_aliases(relation)
727
+ relation.to_sql
667
728
  end
729
+ else
730
+ conn = klass.connection
731
+ conn.unprepared_statement { conn.to_sql(arel) }
668
732
  end
669
733
  end
670
734
 
@@ -722,6 +786,10 @@ module ActiveRecord
722
786
  @values.dup
723
787
  end
724
788
 
789
+ def values_for_queries # :nodoc:
790
+ @values.except(:extending, :skip_query_cache, :strict_loading)
791
+ end
792
+
725
793
  def inspect
726
794
  subject = loaded? ? records : annotate("loading for inspect")
727
795
  entries = subject.take([limit_value, 11].compact.min).map!(&:inspect)
@@ -756,11 +824,9 @@ module ActiveRecord
756
824
  def preload_associations(records) # :nodoc:
757
825
  preload = preload_values
758
826
  preload += includes_values unless eager_loading?
759
- preloader = nil
760
827
  scope = strict_loading_value ? StrictLoadingScope : nil
761
828
  preload.each do |associations|
762
- preloader ||= build_preloader
763
- preloader.preload records, associations, scope
829
+ ActiveRecord::Associations::Preloader.new(records: records, associations: associations, scope: scope).call
764
830
  end
765
831
  end
766
832
 
@@ -775,8 +841,12 @@ module ActiveRecord
775
841
  end
776
842
 
777
843
  private
778
- def already_in_scope?
779
- @delegate_to_klass && klass.current_scope(true)
844
+ def already_in_scope?(registry)
845
+ @delegate_to_klass && registry.current_scope(klass, true)
846
+ end
847
+
848
+ def global_scope?(registry)
849
+ registry.global_current_scope(klass, true)
780
850
  end
781
851
 
782
852
  def current_scope_restoring_block(&block)
@@ -799,11 +869,20 @@ module ActiveRecord
799
869
  klass.create!(attributes, &block)
800
870
  end
801
871
 
802
- def _scoping(scope)
803
- previous, klass.current_scope = klass.current_scope(true), scope
872
+ def _scoping(scope, registry, all_queries = false)
873
+ previous = registry.current_scope(klass, true)
874
+ registry.set_current_scope(klass, scope)
875
+
876
+ if all_queries
877
+ previous_global = registry.global_current_scope(klass, true)
878
+ registry.set_global_current_scope(klass, scope)
879
+ end
804
880
  yield
805
881
  ensure
806
- klass.current_scope = previous
882
+ registry.set_current_scope(klass, previous)
883
+ if all_queries
884
+ registry.set_global_current_scope(klass, previous_global)
885
+ end
807
886
  end
808
887
 
809
888
  def _substitute_values(values)
@@ -826,23 +905,15 @@ module ActiveRecord
826
905
 
827
906
  def exec_queries(&block)
828
907
  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
908
+ rows = if scheduled?
909
+ future = @future_result
910
+ @future_result = nil
911
+ future.result
912
+ else
913
+ exec_main_query
914
+ end
845
915
 
916
+ records = instantiate_records(rows, &block)
846
917
  preload_associations(records) unless skip_preloading_value
847
918
 
848
919
  records.each(&:readonly!) if readonly_value
@@ -852,18 +923,43 @@ module ActiveRecord
852
923
  end
853
924
  end
854
925
 
855
- def skip_query_cache_if_necessary
856
- if skip_query_cache_value
857
- uncached do
858
- yield
926
+ def exec_main_query(async: false)
927
+ skip_query_cache_if_necessary do
928
+ if where_clause.contradiction?
929
+ [].freeze
930
+ elsif eager_loading?
931
+ apply_join_dependency do |relation, join_dependency|
932
+ if relation.null_relation?
933
+ [].freeze
934
+ else
935
+ relation = join_dependency.apply_column_aliases(relation)
936
+ @_join_dependency = join_dependency
937
+ connection.select_all(relation.arel, "SQL", async: async)
938
+ end
939
+ end
940
+ else
941
+ klass._query_by_sql(arel, async: async)
859
942
  end
943
+ end
944
+ end
945
+
946
+ def instantiate_records(rows, &block)
947
+ return [].freeze if rows.empty?
948
+ if eager_loading?
949
+ records = @_join_dependency.instantiate(rows, strict_loading_value, &block).freeze
950
+ @_join_dependency = nil
951
+ records
860
952
  else
861
- yield
953
+ klass._load_from_sql(rows, &block).freeze
862
954
  end
863
955
  end
864
956
 
865
- def build_preloader
866
- ActiveRecord::Associations::Preloader.new
957
+ def skip_query_cache_if_necessary(&block)
958
+ if skip_query_cache_value
959
+ uncached(&block)
960
+ else
961
+ yield
962
+ end
867
963
  end
868
964
 
869
965
  def references_eager_loaded_tables?
@@ -889,5 +985,9 @@ module ActiveRecord
889
985
  # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
890
986
  string.scan(/[a-zA-Z_][.\w]+(?=.?\.)/).map!(&:downcase) - ["raw_sql_"]
891
987
  end
988
+
989
+ def limited_count
990
+ limit_value ? count : limit(2).count
991
+ end
892
992
  end
893
993
  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
@@ -54,7 +54,7 @@ module ActiveRecord
54
54
  # Accepts an array, or string of SQL conditions and sanitizes
55
55
  # them into a valid SQL fragment for an ORDER clause.
56
56
  #
57
- # sanitize_sql_for_order(["field(id, ?)", [1,3,2]])
57
+ # sanitize_sql_for_order([Arel.sql("field(id, ?)"), [1,3,2]])
58
58
  # # => "field(id, 1,3,2)"
59
59
  #
60
60
  # sanitize_sql_for_order("id ASC")
@@ -137,14 +137,18 @@ module ActiveRecord
137
137
  def disallow_raw_sql!(args, permit: connection.column_name_matcher) # :nodoc:
138
138
  unexpected = nil
139
139
  args.each do |arg|
140
- next if arg.is_a?(Symbol) || Arel.arel_node?(arg) || permit.match?(arg.to_s)
140
+ next if arg.is_a?(Symbol) || Arel.arel_node?(arg) || permit.match?(arg.to_s.strip)
141
141
  (unexpected ||= []) << arg
142
142
  end
143
143
 
144
144
  if unexpected
145
145
  raise(ActiveRecord::UnknownAttributeReference,
146
- "Query method called with non-attribute argument(s): " +
147
- unexpected.map(&:inspect).join(", ")
146
+ "Dangerous query method (method whose arguments are used as raw " \
147
+ "SQL) called with non-attribute argument(s): " \
148
+ "#{unexpected.map(&:inspect).join(", ")}." \
149
+ "This method should not be called with user-provided values, such as request " \
150
+ "parameters or model attributes. Known-safe values can be passed " \
151
+ "by wrapping them in Arel.sql()."
148
152
  )
149
153
  end
150
154
  end
@@ -183,13 +187,13 @@ module ActiveRecord
183
187
  if value.respond_to?(:map) && !value.acts_like?(:string)
184
188
  values = value.map { |v| v.respond_to?(:id_for_database) ? v.id_for_database : v }
185
189
  if values.empty?
186
- c.quote(nil)
190
+ c.quote_bound_value(nil)
187
191
  else
188
- values.map! { |v| c.quote(v) }.join(",")
192
+ values.map! { |v| c.quote_bound_value(v) }.join(",")
189
193
  end
190
194
  else
191
195
  value = value.id_for_database if value.respond_to?(:id_for_database)
192
- c.quote(value)
196
+ c.quote_bound_value(value)
193
197
  end
194
198
  end
195
199