activerecord 6.1.3.2 → 7.0.0.alpha2

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 (229) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +734 -1058
  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 +35 -7
  8. data/lib/active_record/associations/association_scope.rb +1 -3
  9. data/lib/active_record/associations/belongs_to_association.rb +16 -6
  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 +1 -1
  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 +24 -25
  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/preloader/association.rb +161 -49
  25. data/lib/active_record/associations/preloader/batch.rb +51 -0
  26. data/lib/active_record/associations/preloader/branch.rb +147 -0
  27. data/lib/active_record/associations/preloader/through_association.rb +37 -11
  28. data/lib/active_record/associations/preloader.rb +46 -110
  29. data/lib/active_record/associations/singular_association.rb +8 -2
  30. data/lib/active_record/associations/through_association.rb +1 -1
  31. data/lib/active_record/associations.rb +76 -81
  32. data/lib/active_record/asynchronous_queries_tracker.rb +57 -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 +41 -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 +66 -12
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  41. data/lib/active_record/attribute_methods/write.rb +7 -10
  42. data/lib/active_record/attribute_methods.rb +6 -9
  43. data/lib/active_record/attributes.rb +24 -35
  44. data/lib/active_record/autosave_association.rb +3 -18
  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 +11 -1
  48. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +312 -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 +31 -558
  52. data/lib/active_record/connection_adapters/abstract/database_statements.rb +45 -21
  53. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  54. data/lib/active_record/connection_adapters/abstract/quoting.rb +14 -7
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +5 -18
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -9
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +60 -16
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +17 -6
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +115 -69
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +96 -81
  61. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +6 -2
  62. data/lib/active_record/connection_adapters/mysql/database_statements.rb +33 -21
  63. data/lib/active_record/connection_adapters/mysql/quoting.rb +16 -1
  64. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -0
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  66. data/lib/active_record/connection_adapters/pool_config.rb +1 -3
  67. data/lib/active_record/connection_adapters/pool_manager.rb +5 -1
  68. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
  69. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  70. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  72. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  73. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +28 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  76. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  77. data/lib/active_record/connection_adapters/postgresql/quoting.rb +6 -6
  78. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
  79. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +5 -1
  80. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +12 -12
  81. data/lib/active_record/connection_adapters/postgresql_adapter.rb +157 -100
  82. data/lib/active_record/connection_adapters/schema_cache.rb +35 -4
  83. data/lib/active_record/connection_adapters/sql_type_metadata.rb +0 -2
  84. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +23 -17
  85. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -2
  86. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -30
  87. data/lib/active_record/connection_adapters.rb +8 -5
  88. data/lib/active_record/connection_handling.rb +20 -38
  89. data/lib/active_record/core.rb +129 -117
  90. data/lib/active_record/database_configurations/database_config.rb +12 -0
  91. data/lib/active_record/database_configurations/hash_config.rb +27 -1
  92. data/lib/active_record/database_configurations/url_config.rb +2 -2
  93. data/lib/active_record/database_configurations.rb +18 -9
  94. data/lib/active_record/delegated_type.rb +33 -11
  95. data/lib/active_record/destroy_association_async_job.rb +1 -1
  96. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  97. data/lib/active_record/dynamic_matchers.rb +1 -1
  98. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  99. data/lib/active_record/encryption/cipher.rb +53 -0
  100. data/lib/active_record/encryption/config.rb +44 -0
  101. data/lib/active_record/encryption/configurable.rb +61 -0
  102. data/lib/active_record/encryption/context.rb +35 -0
  103. data/lib/active_record/encryption/contexts.rb +72 -0
  104. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  105. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  106. data/lib/active_record/encryption/encryptable_record.rb +208 -0
  107. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  108. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  109. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  110. data/lib/active_record/encryption/encryptor.rb +155 -0
  111. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  112. data/lib/active_record/encryption/errors.rb +15 -0
  113. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  114. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +29 -0
  115. data/lib/active_record/encryption/key.rb +28 -0
  116. data/lib/active_record/encryption/key_generator.rb +42 -0
  117. data/lib/active_record/encryption/key_provider.rb +46 -0
  118. data/lib/active_record/encryption/message.rb +33 -0
  119. data/lib/active_record/encryption/message_serializer.rb +80 -0
  120. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  121. data/lib/active_record/encryption/properties.rb +76 -0
  122. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  123. data/lib/active_record/encryption/scheme.rb +99 -0
  124. data/lib/active_record/encryption.rb +55 -0
  125. data/lib/active_record/enum.rb +44 -46
  126. data/lib/active_record/errors.rb +66 -3
  127. data/lib/active_record/fixture_set/file.rb +15 -1
  128. data/lib/active_record/fixture_set/table_row.rb +40 -5
  129. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  130. data/lib/active_record/fixtures.rb +16 -11
  131. data/lib/active_record/future_result.rb +139 -0
  132. data/lib/active_record/gem_version.rb +4 -4
  133. data/lib/active_record/inheritance.rb +55 -17
  134. data/lib/active_record/insert_all.rb +39 -6
  135. data/lib/active_record/integration.rb +1 -1
  136. data/lib/active_record/internal_metadata.rb +3 -5
  137. data/lib/active_record/legacy_yaml_adapter.rb +1 -1
  138. data/lib/active_record/locking/optimistic.rb +10 -9
  139. data/lib/active_record/log_subscriber.rb +6 -2
  140. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  141. data/lib/active_record/middleware/database_selector.rb +8 -3
  142. data/lib/active_record/migration/command_recorder.rb +4 -4
  143. data/lib/active_record/migration/compatibility.rb +83 -1
  144. data/lib/active_record/migration/join_table.rb +1 -1
  145. data/lib/active_record/migration.rb +109 -79
  146. data/lib/active_record/model_schema.rb +46 -32
  147. data/lib/active_record/nested_attributes.rb +3 -3
  148. data/lib/active_record/no_touching.rb +2 -2
  149. data/lib/active_record/null_relation.rb +2 -6
  150. data/lib/active_record/persistence.rb +134 -45
  151. data/lib/active_record/query_cache.rb +2 -2
  152. data/lib/active_record/query_logs.rb +203 -0
  153. data/lib/active_record/querying.rb +15 -5
  154. data/lib/active_record/railtie.rb +117 -17
  155. data/lib/active_record/railties/controller_runtime.rb +1 -1
  156. data/lib/active_record/railties/databases.rake +83 -58
  157. data/lib/active_record/readonly_attributes.rb +11 -0
  158. data/lib/active_record/reflection.rb +45 -44
  159. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  160. data/lib/active_record/relation/batches.rb +3 -3
  161. data/lib/active_record/relation/calculations.rb +42 -25
  162. data/lib/active_record/relation/delegation.rb +6 -6
  163. data/lib/active_record/relation/finder_methods.rb +32 -23
  164. data/lib/active_record/relation/merger.rb +20 -13
  165. data/lib/active_record/relation/predicate_builder.rb +1 -6
  166. data/lib/active_record/relation/query_attribute.rb +5 -11
  167. data/lib/active_record/relation/query_methods.rb +233 -50
  168. data/lib/active_record/relation/record_fetch_warning.rb +2 -2
  169. data/lib/active_record/relation/spawn_methods.rb +2 -2
  170. data/lib/active_record/relation/where_clause.rb +22 -15
  171. data/lib/active_record/relation.rb +170 -87
  172. data/lib/active_record/result.rb +17 -2
  173. data/lib/active_record/runtime_registry.rb +2 -4
  174. data/lib/active_record/sanitization.rb +11 -7
  175. data/lib/active_record/schema_dumper.rb +3 -3
  176. data/lib/active_record/schema_migration.rb +0 -4
  177. data/lib/active_record/scoping/default.rb +62 -15
  178. data/lib/active_record/scoping/named.rb +3 -11
  179. data/lib/active_record/scoping.rb +40 -22
  180. data/lib/active_record/serialization.rb +1 -1
  181. data/lib/active_record/signed_id.rb +1 -1
  182. data/lib/active_record/statement_cache.rb +2 -2
  183. data/lib/active_record/tasks/database_tasks.rb +107 -23
  184. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  185. data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -11
  186. data/lib/active_record/test_databases.rb +1 -1
  187. data/lib/active_record/test_fixtures.rb +45 -4
  188. data/lib/active_record/timestamp.rb +3 -4
  189. data/lib/active_record/transactions.rb +9 -14
  190. data/lib/active_record/translation.rb +2 -2
  191. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  192. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  193. data/lib/active_record/type/internal/timezone.rb +2 -2
  194. data/lib/active_record/type/serialized.rb +1 -1
  195. data/lib/active_record/type/type_map.rb +17 -20
  196. data/lib/active_record/type.rb +1 -2
  197. data/lib/active_record/validations/associated.rb +1 -1
  198. data/lib/active_record/validations/numericality.rb +1 -1
  199. data/lib/active_record.rb +170 -2
  200. data/lib/arel/attributes/attribute.rb +0 -8
  201. data/lib/arel/collectors/bind.rb +2 -2
  202. data/lib/arel/collectors/composite.rb +3 -3
  203. data/lib/arel/collectors/sql_string.rb +1 -1
  204. data/lib/arel/collectors/substitute_binds.rb +1 -1
  205. data/lib/arel/crud.rb +18 -22
  206. data/lib/arel/delete_manager.rb +2 -4
  207. data/lib/arel/insert_manager.rb +2 -3
  208. data/lib/arel/nodes/casted.rb +1 -1
  209. data/lib/arel/nodes/delete_statement.rb +8 -13
  210. data/lib/arel/nodes/homogeneous_in.rb +4 -0
  211. data/lib/arel/nodes/insert_statement.rb +2 -2
  212. data/lib/arel/nodes/select_core.rb +2 -2
  213. data/lib/arel/nodes/select_statement.rb +2 -2
  214. data/lib/arel/nodes/update_statement.rb +3 -2
  215. data/lib/arel/predications.rb +3 -3
  216. data/lib/arel/select_manager.rb +10 -4
  217. data/lib/arel/table.rb +0 -1
  218. data/lib/arel/tree_manager.rb +0 -12
  219. data/lib/arel/update_manager.rb +2 -4
  220. data/lib/arel/visitors/dot.rb +80 -90
  221. data/lib/arel/visitors/mysql.rb +6 -1
  222. data/lib/arel/visitors/postgresql.rb +0 -10
  223. data/lib/arel/visitors/to_sql.rb +44 -3
  224. data/lib/arel.rb +1 -1
  225. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  226. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  227. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  228. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  229. metadata +55 -16
@@ -17,8 +17,8 @@ module ActiveRecord
17
17
  QueryRegistry.reset
18
18
 
19
19
  super.tap do |records|
20
- if logger && warn_on_records_fetched_greater_than
21
- if records.length > warn_on_records_fetched_greater_than
20
+ if logger && ActiveRecord.warn_on_records_fetched_greater_than
21
+ if records.length > ActiveRecord.warn_on_records_fetched_greater_than
22
22
  logger.warn "Query fetched #{records.size} #{@klass} records: #{QueryRegistry.queries.join(";")}"
23
23
  end
24
24
  end
@@ -7,8 +7,8 @@ require "active_record/relation/merger"
7
7
  module ActiveRecord
8
8
  module SpawnMethods
9
9
  # This is overridden by Associations::CollectionProxy
10
- def spawn #:nodoc:
11
- already_in_scope? ? klass.all : clone
10
+ def spawn # :nodoc:
11
+ already_in_scope?(klass.scope_registry) ? klass.all : clone
12
12
  end
13
13
 
14
14
  # Merges in the conditions from <tt>other</tt>, if <tt>other</tt> is an ActiveRecord::Relation.
@@ -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
@@ -103,28 +108,31 @@ module ActiveRecord
103
108
  end
104
109
 
105
110
  def extract_attributes
106
- predicates.each_with_object([]) do |node, attrs|
107
- attr = extract_attribute(node) || begin
108
- node.left if node.equality? && node.left.is_a?(Arel::Predications)
109
- end
110
- attrs << attr if attr
111
- end
111
+ attrs = []
112
+ each_attributes { |attr, _| attrs << attr }
113
+ attrs
112
114
  end
113
115
 
114
116
  protected
115
117
  attr_reader :predicates
116
118
 
117
119
  def referenced_columns
118
- predicates.each_with_object({}) do |node, hash|
120
+ hash = {}
121
+ each_attributes { |attr, node| hash[attr] = node }
122
+ hash
123
+ end
124
+
125
+ private
126
+ def each_attributes
127
+ predicates.each do |node|
119
128
  attr = extract_attribute(node) || begin
120
129
  node.left if equality_node?(node) && node.left.is_a?(Arel::Predications)
121
130
  end
122
131
 
123
- hash[attr] = node if attr
132
+ yield attr, node if attr
124
133
  end
125
134
  end
126
135
 
127
- private
128
136
  def extract_attribute(node)
129
137
  attr_node = nil
130
138
  Arel.fetch_attribute(node) do |attr|
@@ -165,8 +173,8 @@ module ActiveRecord
165
173
  else
166
174
  ActiveSupport::Deprecation.warn(<<-MSG.squish)
167
175
  Merging (#{node.to_sql}) and (#{ref.to_sql}) no longer maintain
168
- both conditions, and will be replaced by the latter in Rails 6.2.
169
- To migrate to Rails 6.2's behavior, use `relation.merge(other, rewhere: true)`.
176
+ both conditions, and will be replaced by the latter in Rails 7.0.
177
+ To migrate to Rails 7.0's behavior, use `relation.merge(other, rewhere: true)`.
170
178
  MSG
171
179
  false
172
180
  end
@@ -224,11 +232,10 @@ module ActiveRecord
224
232
  end
225
233
 
226
234
  def extract_node_value(node)
227
- case node
228
- when Array
229
- node.map { |v| extract_node_value(v) }
230
- when Arel::Nodes::BindParam, Arel::Nodes::Casted, Arel::Nodes::Quoted
235
+ if node.respond_to?(:value_before_type_cast)
231
236
  node.value_before_type_cast
237
+ elsif Array === node
238
+ node.map { |v| extract_node_value(v) }
232
239
  end
233
240
  end
234
241
  end
@@ -31,6 +31,9 @@ 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
36
+ @limited_count = nil
34
37
  end
35
38
 
36
39
  def initialize_copy(other)
@@ -67,8 +70,12 @@ module ActiveRecord
67
70
  # user = users.new { |user| user.name = 'Oscar' }
68
71
  # user.name # => Oscar
69
72
  def new(attributes = nil, &block)
70
- block = current_scope_restoring_block(&block)
71
- scoping { _new(attributes, &block) }
73
+ if attributes.is_a?(Array)
74
+ attributes.collect { |attr| new(attr, &block) }
75
+ else
76
+ block = current_scope_restoring_block(&block)
77
+ scoping { _new(attributes, &block) }
78
+ end
72
79
  end
73
80
  alias build new
74
81
 
@@ -148,7 +155,7 @@ module ActiveRecord
148
155
  # above can be alternatively written this way:
149
156
  #
150
157
  # # Find the first user named "Scarlett" or create a new one with a
151
- # # different last name.
158
+ # # particular last name.
152
159
  # User.find_or_create_by(first_name: 'Scarlett') do |user|
153
160
  # user.last_name = 'Johansson'
154
161
  # end
@@ -175,7 +182,7 @@ module ActiveRecord
175
182
  find_by(attributes) || create!(attributes, &block)
176
183
  end
177
184
 
178
- # Attempts to create a record with the given attributes in a table that has a unique constraint
185
+ # Attempts to create a record with the given attributes in a table that has a unique database constraint
179
186
  # on one or several of its columns. If a row already exists with one or several of these
180
187
  # unique constraints, the exception such an insertion would normally raise is caught,
181
188
  # and the existing record with those attributes is found using #find_by!.
@@ -186,7 +193,7 @@ module ActiveRecord
186
193
  #
187
194
  # There are several drawbacks to #create_or_find_by, though:
188
195
  #
189
- # * The underlying table must have the relevant columns defined with unique constraints.
196
+ # * The underlying table must have the relevant columns defined with unique database constraints.
190
197
  # * A unique constraint violation may be triggered by only one, or at least less than all,
191
198
  # of the given attributes. This means that the subsequent #find_by! may fail to find a
192
199
  # matching record, which will then raise an <tt>ActiveRecord::RecordNotFound</tt> exception,
@@ -257,13 +264,20 @@ module ActiveRecord
257
264
 
258
265
  # Returns size of the records.
259
266
  def size
260
- loaded? ? @records.length : count(:all)
267
+ if loaded?
268
+ records.length
269
+ else
270
+ count(:all)
271
+ end
261
272
  end
262
273
 
263
274
  # Returns true if there are no records.
264
275
  def empty?
265
- return @records.empty? if loaded?
266
- !exists?
276
+ if loaded?
277
+ records.empty?
278
+ else
279
+ !exists?
280
+ end
267
281
  end
268
282
 
269
283
  # Returns true if there are no records.
@@ -281,13 +295,15 @@ module ActiveRecord
281
295
  # Returns true if there is exactly one record.
282
296
  def one?
283
297
  return super if block_given?
284
- limit_value ? records.one? : size == 1
298
+ return records.one? if limit_value || loaded?
299
+ limited_count == 1
285
300
  end
286
301
 
287
302
  # Returns true if there is more than one record.
288
303
  def many?
289
304
  return super if block_given?
290
- limit_value ? records.many? : size > 1
305
+ return records.many? if limit_value || loaded?
306
+ limited_count > 1
291
307
  end
292
308
 
293
309
  # Returns a stable cache key that can be used to identify this query.
@@ -344,7 +360,7 @@ module ActiveRecord
344
360
  def compute_cache_version(timestamp_column) # :nodoc:
345
361
  timestamp_column = timestamp_column.to_s
346
362
 
347
- if loaded? || distinct_value
363
+ if loaded?
348
364
  size = records.size
349
365
  if size > 0
350
366
  timestamp = records.map { |record| record.read_attribute(timestamp_column) }.max
@@ -357,6 +373,7 @@ module ActiveRecord
357
373
 
358
374
  if collection.has_limit_or_offset?
359
375
  query = collection.select("#{column} AS collection_cache_key_timestamp")
376
+ query._select!(table[Arel.star]) if distinct_value && collection.select_values.empty?
360
377
  subquery_alias = "subquery_for_cache_key"
361
378
  subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
362
379
  arel = query.build_subquery(subquery_alias, select_values % subquery_column)
@@ -400,15 +417,28 @@ module ActiveRecord
400
417
  # end
401
418
  # # => SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1
402
419
  #
420
+ # If <tt>all_queries: true</tt> is passed, scoping will apply to all queries
421
+ # for the relation including +update+ and +delete+ on instances.
422
+ # Once +all_queries+ is set to true it cannot be set to false in a
423
+ # nested block.
424
+ #
403
425
  # Please check unscoped if you want to remove all previous scopes (including
404
426
  # the default_scope) during the execution of a block.
405
- def scoping
406
- already_in_scope? ? yield : _scoping(self) { yield }
427
+ def scoping(all_queries: nil, &block)
428
+ registry = klass.scope_registry
429
+ if global_scope?(registry) && all_queries == false
430
+ raise ArgumentError, "Scoping is set to apply to all queries and cannot be unset in a nested block."
431
+ elsif already_in_scope?(registry)
432
+ yield
433
+ else
434
+ _scoping(self, registry, all_queries, &block)
435
+ end
407
436
  end
408
437
 
409
438
  def _exec_scope(*args, &block) # :nodoc:
410
439
  @delegate_to_klass = true
411
- _scoping(nil) { instance_exec(*args, &block) || self }
440
+ registry = klass.scope_registry
441
+ _scoping(nil, registry) { instance_exec(*args, &block) || self }
412
442
  ensure
413
443
  @delegate_to_klass = false
414
444
  end
@@ -440,19 +470,6 @@ module ActiveRecord
440
470
  def update_all(updates)
441
471
  raise ArgumentError, "Empty list of attributes to change" if updates.blank?
442
472
 
443
- if eager_loading?
444
- relation = apply_join_dependency
445
- return relation.update_all(updates)
446
- end
447
-
448
- stmt = Arel::UpdateManager.new
449
- stmt.table(arel.join_sources.empty? ? table : arel.source)
450
- stmt.key = table[primary_key]
451
- stmt.take(arel.limit)
452
- stmt.offset(arel.offset)
453
- stmt.order(*arel.orders)
454
- stmt.wheres = arel.constraints
455
-
456
473
  if updates.is_a?(Hash)
457
474
  if klass.locking_enabled? &&
458
475
  !updates.key?(klass.locking_column) &&
@@ -460,12 +477,17 @@ module ActiveRecord
460
477
  attr = table[klass.locking_column]
461
478
  updates[attr.name] = _increment_attribute(attr)
462
479
  end
463
- stmt.set _substitute_values(updates)
480
+ values = _substitute_values(updates)
464
481
  else
465
- stmt.set Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
482
+ values = Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
466
483
  end
467
484
 
468
- @klass.connection.update stmt, "#{@klass} Update All"
485
+ arel = eager_loading? ? apply_join_dependency.arel : build_arel
486
+ arel.source.left = table
487
+
488
+ stmt = arel.compile_update(values, table[primary_key])
489
+
490
+ klass.connection.update(stmt, "#{klass} Update All").tap { reset }
469
491
  end
470
492
 
471
493
  def update(id = :all, attributes) # :nodoc:
@@ -476,6 +498,14 @@ module ActiveRecord
476
498
  end
477
499
  end
478
500
 
501
+ def update!(id = :all, attributes) # :nodoc:
502
+ if id == :all
503
+ each { |record| record.update!(attributes) }
504
+ else
505
+ klass.update!(id, attributes)
506
+ end
507
+ end
508
+
479
509
  # Updates the counters of the records in the current relation.
480
510
  #
481
511
  # ==== Parameters
@@ -582,23 +612,12 @@ module ActiveRecord
582
612
  raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
583
613
  end
584
614
 
585
- if eager_loading?
586
- relation = apply_join_dependency
587
- return relation.delete_all
588
- end
615
+ arel = eager_loading? ? apply_join_dependency.arel : build_arel
616
+ arel.source.left = table
589
617
 
590
- stmt = Arel::DeleteManager.new
591
- stmt.from(arel.join_sources.empty? ? table : arel.source)
592
- stmt.key = table[primary_key]
593
- stmt.take(arel.limit)
594
- stmt.offset(arel.offset)
595
- stmt.order(*arel.orders)
596
- stmt.wheres = arel.constraints
618
+ stmt = arel.compile_delete(table[primary_key])
597
619
 
598
- affected = @klass.connection.delete(stmt, "#{@klass} Destroy")
599
-
600
- reset
601
- affected
620
+ klass.connection.delete(stmt, "#{klass} Delete All").tap { reset }
602
621
  end
603
622
 
604
623
  # Finds and destroys all records matching the specified conditions.
@@ -627,6 +646,32 @@ module ActiveRecord
627
646
  where(*args).delete_all
628
647
  end
629
648
 
649
+ # Schedule the query to be performed from a background thread pool.
650
+ #
651
+ # Post.where(published: true).load_async # => #<ActiveRecord::Relation>
652
+ def load_async
653
+ return load if !connection.async_enabled?
654
+
655
+ unless loaded?
656
+ result = exec_main_query(async: connection.current_transaction.closed?)
657
+
658
+ if result.is_a?(Array)
659
+ @records = result
660
+ else
661
+ @future_result = result
662
+ end
663
+ @loaded = true
664
+ end
665
+
666
+ self
667
+ end
668
+
669
+ # Returns <tt>true</tt> if the relation was scheduled on the background
670
+ # thread pool.
671
+ def scheduled?
672
+ !!@future_result
673
+ end
674
+
630
675
  # Causes the records to be loaded from the database if they have not
631
676
  # been loaded already. You can use this if for some reason you need
632
677
  # to explicitly load some records before actually using them. The
@@ -634,7 +679,7 @@ module ActiveRecord
634
679
  #
635
680
  # Post.where(published: true).load # => #<ActiveRecord::Relation>
636
681
  def load(&block)
637
- unless loaded?
682
+ if !loaded? || scheduled?
638
683
  @records = exec_queries(&block)
639
684
  @loaded = true
640
685
  end
@@ -649,10 +694,14 @@ module ActiveRecord
649
694
  end
650
695
 
651
696
  def reset
697
+ @future_result&.cancel
698
+ @future_result = nil
652
699
  @delegate_to_klass = false
653
700
  @to_sql = @arel = @loaded = @should_eager_load = nil
654
701
  @offsets = @take = nil
655
- @records = [].freeze
702
+ @cache_keys = nil
703
+ @records = nil
704
+ @limited_count = nil
656
705
  self
657
706
  end
658
707
 
@@ -661,16 +710,14 @@ module ActiveRecord
661
710
  # User.where(name: 'Oscar').to_sql
662
711
  # # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
663
712
  def to_sql
664
- @to_sql ||= begin
665
- if eager_loading?
666
- apply_join_dependency do |relation, join_dependency|
667
- relation = join_dependency.apply_column_aliases(relation)
668
- relation.to_sql
669
- end
670
- else
671
- conn = klass.connection
672
- conn.unprepared_statement { conn.to_sql(arel) }
713
+ @to_sql ||= if eager_loading?
714
+ apply_join_dependency do |relation, join_dependency|
715
+ relation = join_dependency.apply_column_aliases(relation)
716
+ relation.to_sql
673
717
  end
718
+ else
719
+ conn = klass.connection
720
+ conn.unprepared_statement { conn.to_sql(arel) }
674
721
  end
675
722
  end
676
723
 
@@ -728,6 +775,10 @@ module ActiveRecord
728
775
  @values.dup
729
776
  end
730
777
 
778
+ def values_for_queries # :nodoc:
779
+ @values.except(:extending, :skip_query_cache, :strict_loading)
780
+ end
781
+
731
782
  def inspect
732
783
  subject = loaded? ? records : annotate("loading for inspect")
733
784
  entries = subject.take([limit_value, 11].compact.min).map!(&:inspect)
@@ -762,11 +813,9 @@ module ActiveRecord
762
813
  def preload_associations(records) # :nodoc:
763
814
  preload = preload_values
764
815
  preload += includes_values unless eager_loading?
765
- preloader = nil
766
816
  scope = strict_loading_value ? StrictLoadingScope : nil
767
817
  preload.each do |associations|
768
- preloader ||= build_preloader
769
- preloader.preload records, associations, scope
818
+ ActiveRecord::Associations::Preloader.new(records: records, associations: associations, scope: scope).call
770
819
  end
771
820
  end
772
821
 
@@ -781,8 +830,12 @@ module ActiveRecord
781
830
  end
782
831
 
783
832
  private
784
- def already_in_scope?
785
- @delegate_to_klass && klass.current_scope(true)
833
+ def already_in_scope?(registry)
834
+ @delegate_to_klass && registry.current_scope(klass, true)
835
+ end
836
+
837
+ def global_scope?(registry)
838
+ registry.global_current_scope(klass, true)
786
839
  end
787
840
 
788
841
  def current_scope_restoring_block(&block)
@@ -805,11 +858,20 @@ module ActiveRecord
805
858
  klass.create!(attributes, &block)
806
859
  end
807
860
 
808
- def _scoping(scope)
809
- previous, klass.current_scope = klass.current_scope(true), scope
861
+ def _scoping(scope, registry, all_queries = false)
862
+ previous = registry.current_scope(klass, true)
863
+ registry.set_current_scope(klass, scope)
864
+
865
+ if all_queries
866
+ previous_global = registry.global_current_scope(klass, true)
867
+ registry.set_global_current_scope(klass, scope)
868
+ end
810
869
  yield
811
870
  ensure
812
- klass.current_scope = previous
871
+ registry.set_current_scope(klass, previous)
872
+ if all_queries
873
+ registry.set_global_current_scope(klass, previous_global)
874
+ end
813
875
  end
814
876
 
815
877
  def _substitute_values(values)
@@ -832,23 +894,15 @@ module ActiveRecord
832
894
 
833
895
  def exec_queries(&block)
834
896
  skip_query_cache_if_necessary do
835
- records =
836
- if where_clause.contradiction?
837
- []
838
- elsif eager_loading?
839
- apply_join_dependency do |relation, join_dependency|
840
- if relation.null_relation?
841
- []
842
- else
843
- relation = join_dependency.apply_column_aliases(relation)
844
- rows = connection.select_all(relation.arel, "SQL")
845
- join_dependency.instantiate(rows, strict_loading_value, &block)
846
- end.freeze
847
- end
848
- else
849
- klass.find_by_sql(arel, &block).freeze
850
- end
897
+ rows = if scheduled?
898
+ future = @future_result
899
+ @future_result = nil
900
+ future.result
901
+ else
902
+ exec_main_query
903
+ end
851
904
 
905
+ records = instantiate_records(rows, &block)
852
906
  preload_associations(records) unless skip_preloading_value
853
907
 
854
908
  records.each(&:readonly!) if readonly_value
@@ -858,18 +912,43 @@ module ActiveRecord
858
912
  end
859
913
  end
860
914
 
861
- def skip_query_cache_if_necessary
862
- if skip_query_cache_value
863
- uncached do
864
- yield
915
+ def exec_main_query(async: false)
916
+ skip_query_cache_if_necessary do
917
+ if where_clause.contradiction?
918
+ [].freeze
919
+ elsif eager_loading?
920
+ apply_join_dependency do |relation, join_dependency|
921
+ if relation.null_relation?
922
+ [].freeze
923
+ else
924
+ relation = join_dependency.apply_column_aliases(relation)
925
+ @_join_dependency = join_dependency
926
+ connection.select_all(relation.arel, "SQL", async: async)
927
+ end
928
+ end
929
+ else
930
+ klass._query_by_sql(arel, async: async)
865
931
  end
932
+ end
933
+ end
934
+
935
+ def instantiate_records(rows, &block)
936
+ return [].freeze if rows.empty?
937
+ if eager_loading?
938
+ records = @_join_dependency.instantiate(rows, strict_loading_value, &block).freeze
939
+ @_join_dependency = nil
940
+ records
866
941
  else
867
- yield
942
+ klass._load_from_sql(rows, &block).freeze
868
943
  end
869
944
  end
870
945
 
871
- def build_preloader
872
- ActiveRecord::Associations::Preloader.new
946
+ def skip_query_cache_if_necessary(&block)
947
+ if skip_query_cache_value
948
+ uncached(&block)
949
+ else
950
+ yield
951
+ end
873
952
  end
874
953
 
875
954
  def references_eager_loaded_tables?
@@ -895,5 +974,9 @@ module ActiveRecord
895
974
  # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
896
975
  string.scan(/[a-zA-Z_][.\w]+(?=.?\.)/).map!(&:downcase) - ["raw_sql_"]
897
976
  end
977
+
978
+ def limited_count
979
+ @limited_count ||= limit(2).count
980
+ end
898
981
  end
899
982
  end