activerecord 6.1.4 → 7.0.0.rc1

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 (234) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1049 -977
  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/preloader/association.rb +187 -55
  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 +90 -82
  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 +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 +13 -14
  43. data/lib/active_record/attributes.rb +24 -35
  44. data/lib/active_record/autosave_association.rb +6 -21
  45. data/lib/active_record/base.rb +19 -1
  46. data/lib/active_record/callbacks.rb +2 -2
  47. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
  48. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  50. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -561
  51. data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
  52. data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
  53. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  54. data/lib/active_record/connection_adapters/abstract/quoting.rb +42 -72
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +34 -9
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +69 -18
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +149 -74
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +97 -81
  61. data/lib/active_record/connection_adapters/column.rb +4 -0
  62. data/lib/active_record/connection_adapters/mysql/database_statements.rb +35 -23
  63. data/lib/active_record/connection_adapters/mysql/quoting.rb +35 -21
  64. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +4 -1
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  66. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  67. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -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 +50 -50
  78. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
  79. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  80. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
  81. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  82. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +27 -16
  83. data/lib/active_record/connection_adapters/postgresql_adapter.rb +205 -105
  84. data/lib/active_record/connection_adapters/schema_cache.rb +29 -4
  85. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
  86. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +15 -16
  87. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -2
  88. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -30
  89. data/lib/active_record/connection_adapters.rb +6 -5
  90. data/lib/active_record/connection_handling.rb +47 -53
  91. data/lib/active_record/core.rb +122 -132
  92. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  93. data/lib/active_record/database_configurations/database_config.rb +12 -9
  94. data/lib/active_record/database_configurations/hash_config.rb +63 -5
  95. data/lib/active_record/database_configurations/url_config.rb +2 -2
  96. data/lib/active_record/database_configurations.rb +16 -32
  97. data/lib/active_record/delegated_type.rb +52 -11
  98. data/lib/active_record/destroy_association_async_job.rb +1 -1
  99. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  100. data/lib/active_record/dynamic_matchers.rb +1 -1
  101. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  102. data/lib/active_record/encryption/cipher.rb +53 -0
  103. data/lib/active_record/encryption/config.rb +44 -0
  104. data/lib/active_record/encryption/configurable.rb +61 -0
  105. data/lib/active_record/encryption/context.rb +35 -0
  106. data/lib/active_record/encryption/contexts.rb +72 -0
  107. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  108. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  109. data/lib/active_record/encryption/encryptable_record.rb +208 -0
  110. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  111. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  112. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  113. data/lib/active_record/encryption/encryptor.rb +155 -0
  114. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  115. data/lib/active_record/encryption/errors.rb +15 -0
  116. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  117. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  118. data/lib/active_record/encryption/key.rb +28 -0
  119. data/lib/active_record/encryption/key_generator.rb +42 -0
  120. data/lib/active_record/encryption/key_provider.rb +46 -0
  121. data/lib/active_record/encryption/message.rb +33 -0
  122. data/lib/active_record/encryption/message_serializer.rb +90 -0
  123. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  124. data/lib/active_record/encryption/properties.rb +76 -0
  125. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  126. data/lib/active_record/encryption/scheme.rb +99 -0
  127. data/lib/active_record/encryption.rb +55 -0
  128. data/lib/active_record/enum.rb +49 -42
  129. data/lib/active_record/errors.rb +67 -4
  130. data/lib/active_record/explain_registry.rb +11 -6
  131. data/lib/active_record/fixture_set/file.rb +15 -1
  132. data/lib/active_record/fixture_set/table_row.rb +41 -6
  133. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  134. data/lib/active_record/fixtures.rb +17 -20
  135. data/lib/active_record/future_result.rb +139 -0
  136. data/lib/active_record/gem_version.rb +4 -4
  137. data/lib/active_record/inheritance.rb +55 -17
  138. data/lib/active_record/insert_all.rb +80 -14
  139. data/lib/active_record/integration.rb +4 -3
  140. data/lib/active_record/internal_metadata.rb +3 -5
  141. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  142. data/lib/active_record/locking/optimistic.rb +10 -9
  143. data/lib/active_record/locking/pessimistic.rb +9 -3
  144. data/lib/active_record/log_subscriber.rb +14 -3
  145. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  146. data/lib/active_record/middleware/database_selector.rb +8 -3
  147. data/lib/active_record/middleware/shard_selector.rb +60 -0
  148. data/lib/active_record/migration/command_recorder.rb +4 -4
  149. data/lib/active_record/migration/compatibility.rb +83 -1
  150. data/lib/active_record/migration/join_table.rb +1 -1
  151. data/lib/active_record/migration.rb +109 -79
  152. data/lib/active_record/model_schema.rb +45 -58
  153. data/lib/active_record/nested_attributes.rb +13 -12
  154. data/lib/active_record/no_touching.rb +3 -3
  155. data/lib/active_record/null_relation.rb +2 -6
  156. data/lib/active_record/persistence.rb +219 -52
  157. data/lib/active_record/query_cache.rb +2 -2
  158. data/lib/active_record/query_logs.rb +138 -0
  159. data/lib/active_record/querying.rb +15 -5
  160. data/lib/active_record/railtie.rb +127 -17
  161. data/lib/active_record/railties/controller_runtime.rb +1 -1
  162. data/lib/active_record/railties/databases.rake +66 -129
  163. data/lib/active_record/readonly_attributes.rb +11 -0
  164. data/lib/active_record/reflection.rb +67 -50
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  166. data/lib/active_record/relation/batches.rb +3 -3
  167. data/lib/active_record/relation/calculations.rb +40 -36
  168. data/lib/active_record/relation/delegation.rb +6 -6
  169. data/lib/active_record/relation/finder_methods.rb +31 -35
  170. data/lib/active_record/relation/merger.rb +20 -13
  171. data/lib/active_record/relation/predicate_builder.rb +1 -6
  172. data/lib/active_record/relation/query_attribute.rb +5 -11
  173. data/lib/active_record/relation/query_methods.rb +235 -61
  174. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  175. data/lib/active_record/relation/spawn_methods.rb +2 -2
  176. data/lib/active_record/relation/where_clause.rb +10 -19
  177. data/lib/active_record/relation.rb +171 -84
  178. data/lib/active_record/result.rb +17 -7
  179. data/lib/active_record/runtime_registry.rb +9 -13
  180. data/lib/active_record/sanitization.rb +11 -7
  181. data/lib/active_record/schema_dumper.rb +10 -3
  182. data/lib/active_record/schema_migration.rb +0 -4
  183. data/lib/active_record/scoping/default.rb +61 -12
  184. data/lib/active_record/scoping/named.rb +3 -11
  185. data/lib/active_record/scoping.rb +64 -34
  186. data/lib/active_record/serialization.rb +1 -1
  187. data/lib/active_record/signed_id.rb +1 -1
  188. data/lib/active_record/suppressor.rb +11 -15
  189. data/lib/active_record/tasks/database_tasks.rb +116 -58
  190. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  191. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -12
  192. data/lib/active_record/test_databases.rb +1 -1
  193. data/lib/active_record/test_fixtures.rb +4 -4
  194. data/lib/active_record/timestamp.rb +3 -4
  195. data/lib/active_record/transactions.rb +9 -14
  196. data/lib/active_record/translation.rb +2 -2
  197. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  198. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  199. data/lib/active_record/type/internal/timezone.rb +2 -2
  200. data/lib/active_record/type/serialized.rb +1 -1
  201. data/lib/active_record/type/type_map.rb +17 -20
  202. data/lib/active_record/type.rb +1 -2
  203. data/lib/active_record/validations/associated.rb +1 -1
  204. data/lib/active_record/validations/uniqueness.rb +1 -1
  205. data/lib/active_record.rb +204 -28
  206. data/lib/arel/attributes/attribute.rb +0 -8
  207. data/lib/arel/crud.rb +28 -22
  208. data/lib/arel/delete_manager.rb +18 -4
  209. data/lib/arel/filter_predications.rb +9 -0
  210. data/lib/arel/insert_manager.rb +2 -3
  211. data/lib/arel/nodes/casted.rb +1 -1
  212. data/lib/arel/nodes/delete_statement.rb +12 -13
  213. data/lib/arel/nodes/filter.rb +10 -0
  214. data/lib/arel/nodes/function.rb +1 -0
  215. data/lib/arel/nodes/insert_statement.rb +2 -2
  216. data/lib/arel/nodes/select_core.rb +2 -2
  217. data/lib/arel/nodes/select_statement.rb +2 -2
  218. data/lib/arel/nodes/update_statement.rb +8 -3
  219. data/lib/arel/nodes.rb +1 -0
  220. data/lib/arel/predications.rb +11 -3
  221. data/lib/arel/select_manager.rb +10 -4
  222. data/lib/arel/table.rb +0 -1
  223. data/lib/arel/tree_manager.rb +0 -12
  224. data/lib/arel/update_manager.rb +18 -4
  225. data/lib/arel/visitors/dot.rb +80 -90
  226. data/lib/arel/visitors/mysql.rb +8 -2
  227. data/lib/arel/visitors/postgresql.rb +0 -10
  228. data/lib/arel/visitors/to_sql.rb +58 -2
  229. data/lib/arel.rb +2 -1
  230. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  231. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  232. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  233. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  234. metadata +56 -13
@@ -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
@@ -31,17 +31,15 @@ module ActiveRecord
31
31
  end
32
32
  # :startdoc:
33
33
 
34
- class QueryRegistry # :nodoc:
35
- extend ActiveSupport::PerThreadRegistry
34
+ module QueryRegistry # :nodoc:
35
+ extend self
36
36
 
37
- attr_reader :queries
38
-
39
- def initialize
40
- @queries = []
37
+ def queries
38
+ ActiveSupport::IsolatedExecutionState[:active_record_query_registry] ||= []
41
39
  end
42
40
 
43
41
  def reset
44
- @queries.clear
42
+ queries.clear
45
43
  end
46
44
  end
47
45
  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
@@ -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 6.2.
172
- To migrate to Rails 6.2'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,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)
@@ -38,11 +41,6 @@ module ActiveRecord
38
41
  reset
39
42
  end
40
43
 
41
- def arel_attribute(name) # :nodoc:
42
- table[name]
43
- end
44
- deprecate :arel_attribute
45
-
46
44
  def bind_attribute(name, value) # :nodoc:
47
45
  if reflection = klass._reflect_on_association(name)
48
46
  name = reflection.foreign_key
@@ -67,8 +65,12 @@ module ActiveRecord
67
65
  # user = users.new { |user| user.name = 'Oscar' }
68
66
  # user.name # => Oscar
69
67
  def new(attributes = nil, &block)
70
- block = current_scope_restoring_block(&block)
71
- scoping { _new(attributes, &block) }
68
+ if attributes.is_a?(Array)
69
+ attributes.collect { |attr| new(attr, &block) }
70
+ else
71
+ block = current_scope_restoring_block(&block)
72
+ scoping { _new(attributes, &block) }
73
+ end
72
74
  end
73
75
  alias build new
74
76
 
@@ -148,7 +150,7 @@ module ActiveRecord
148
150
  # above can be alternatively written this way:
149
151
  #
150
152
  # # Find the first user named "Scarlett" or create a new one with a
151
- # # different last name.
153
+ # # particular last name.
152
154
  # User.find_or_create_by(first_name: 'Scarlett') do |user|
153
155
  # user.last_name = 'Johansson'
154
156
  # end
@@ -175,7 +177,7 @@ module ActiveRecord
175
177
  find_by(attributes) || create!(attributes, &block)
176
178
  end
177
179
 
178
- # Attempts to create a record with the given attributes in a table that has a unique constraint
180
+ # Attempts to create a record with the given attributes in a table that has a unique database constraint
179
181
  # on one or several of its columns. If a row already exists with one or several of these
180
182
  # unique constraints, the exception such an insertion would normally raise is caught,
181
183
  # and the existing record with those attributes is found using #find_by!.
@@ -186,7 +188,7 @@ module ActiveRecord
186
188
  #
187
189
  # There are several drawbacks to #create_or_find_by, though:
188
190
  #
189
- # * The underlying table must have the relevant columns defined with unique constraints.
191
+ # * The underlying table must have the relevant columns defined with unique database constraints.
190
192
  # * A unique constraint violation may be triggered by only one, or at least less than all,
191
193
  # of the given attributes. This means that the subsequent #find_by! may fail to find a
192
194
  # matching record, which will then raise an <tt>ActiveRecord::RecordNotFound</tt> exception,
@@ -257,13 +259,20 @@ module ActiveRecord
257
259
 
258
260
  # Returns size of the records.
259
261
  def size
260
- loaded? ? @records.length : count(:all)
262
+ if loaded?
263
+ records.length
264
+ else
265
+ count(:all)
266
+ end
261
267
  end
262
268
 
263
269
  # Returns true if there are no records.
264
270
  def empty?
265
- return @records.empty? if loaded?
266
- !exists?
271
+ if loaded?
272
+ records.empty?
273
+ else
274
+ !exists?
275
+ end
267
276
  end
268
277
 
269
278
  # Returns true if there are no records.
@@ -281,13 +290,15 @@ module ActiveRecord
281
290
  # Returns true if there is exactly one record.
282
291
  def one?
283
292
  return super if block_given?
284
- limit_value ? records.one? : size == 1
293
+ return records.one? if loaded?
294
+ limited_count == 1
285
295
  end
286
296
 
287
297
  # Returns true if there is more than one record.
288
298
  def many?
289
299
  return super if block_given?
290
- limit_value ? records.many? : size > 1
300
+ return records.many? if loaded?
301
+ limited_count > 1
291
302
  end
292
303
 
293
304
  # Returns a stable cache key that can be used to identify this query.
@@ -344,7 +355,7 @@ module ActiveRecord
344
355
  def compute_cache_version(timestamp_column) # :nodoc:
345
356
  timestamp_column = timestamp_column.to_s
346
357
 
347
- if loaded? || distinct_value
358
+ if loaded?
348
359
  size = records.size
349
360
  if size > 0
350
361
  timestamp = records.map { |record| record.read_attribute(timestamp_column) }.max
@@ -357,6 +368,7 @@ module ActiveRecord
357
368
 
358
369
  if collection.has_limit_or_offset?
359
370
  query = collection.select("#{column} AS collection_cache_key_timestamp")
371
+ query._select!(table[Arel.star]) if distinct_value && collection.select_values.empty?
360
372
  subquery_alias = "subquery_for_cache_key"
361
373
  subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
362
374
  arel = query.build_subquery(subquery_alias, select_values % subquery_column)
@@ -377,7 +389,7 @@ module ActiveRecord
377
389
  end
378
390
 
379
391
  if timestamp
380
- "#{size}-#{timestamp.utc.to_s(cache_timestamp_format)}"
392
+ "#{size}-#{timestamp.utc.to_formatted_s(cache_timestamp_format)}"
381
393
  else
382
394
  "#{size}"
383
395
  end
@@ -400,15 +412,28 @@ module ActiveRecord
400
412
  # end
401
413
  # # => SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1
402
414
  #
415
+ # If <tt>all_queries: true</tt> is passed, scoping will apply to all queries
416
+ # for the relation including +update+ and +delete+ on instances.
417
+ # Once +all_queries+ is set to true it cannot be set to false in a
418
+ # nested block.
419
+ #
403
420
  # Please check unscoped if you want to remove all previous scopes (including
404
421
  # the default_scope) during the execution of a block.
405
- def scoping
406
- already_in_scope? ? yield : _scoping(self) { yield }
422
+ def scoping(all_queries: nil, &block)
423
+ registry = klass.scope_registry
424
+ if global_scope?(registry) && all_queries == false
425
+ raise ArgumentError, "Scoping is set to apply to all queries and cannot be unset in a nested block."
426
+ elsif already_in_scope?(registry)
427
+ yield
428
+ else
429
+ _scoping(self, registry, all_queries, &block)
430
+ end
407
431
  end
408
432
 
409
433
  def _exec_scope(*args, &block) # :nodoc:
410
434
  @delegate_to_klass = true
411
- _scoping(nil) { instance_exec(*args, &block) || self }
435
+ registry = klass.scope_registry
436
+ _scoping(nil, registry) { instance_exec(*args, &block) || self }
412
437
  ensure
413
438
  @delegate_to_klass = false
414
439
  end
@@ -440,17 +465,6 @@ module ActiveRecord
440
465
  def update_all(updates)
441
466
  raise ArgumentError, "Empty list of attributes to change" if updates.blank?
442
467
 
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
468
  if updates.is_a?(Hash)
455
469
  if klass.locking_enabled? &&
456
470
  !updates.key?(klass.locking_column) &&
@@ -458,11 +472,17 @@ module ActiveRecord
458
472
  attr = table[klass.locking_column]
459
473
  updates[attr.name] = _increment_attribute(attr)
460
474
  end
461
- stmt.set _substitute_values(updates)
475
+ values = _substitute_values(updates)
462
476
  else
463
- stmt.set Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
477
+ values = Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
464
478
  end
465
479
 
480
+ arel = eager_loading? ? apply_join_dependency.arel : build_arel
481
+ arel.source.left = table
482
+
483
+ group_values_arel_columns = arel_columns(group_values.uniq)
484
+ having_clause_ast = having_clause.ast unless having_clause.empty?
485
+ stmt = arel.compile_update(values, table[primary_key], having_clause_ast, group_values_arel_columns)
466
486
  klass.connection.update(stmt, "#{klass} Update All").tap { reset }
467
487
  end
468
488
 
@@ -474,6 +494,14 @@ module ActiveRecord
474
494
  end
475
495
  end
476
496
 
497
+ def update!(id = :all, attributes) # :nodoc:
498
+ if id == :all
499
+ each { |record| record.update!(attributes) }
500
+ else
501
+ klass.update!(id, attributes)
502
+ end
503
+ end
504
+
477
505
  # Updates the counters of the records in the current relation.
478
506
  #
479
507
  # ==== Parameters
@@ -583,15 +611,11 @@ module ActiveRecord
583
611
  arel = eager_loading? ? apply_join_dependency.arel : build_arel
584
612
  arel.source.left = table
585
613
 
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
614
+ group_values_arel_columns = arel_columns(group_values.uniq)
615
+ having_clause_ast = having_clause.ast unless having_clause.empty?
616
+ stmt = arel.compile_delete(table[primary_key], having_clause_ast, group_values_arel_columns)
593
617
 
594
- klass.connection.delete(stmt, "#{klass} Destroy").tap { reset }
618
+ klass.connection.delete(stmt, "#{klass} Delete All").tap { reset }
595
619
  end
596
620
 
597
621
  # Finds and destroys all records matching the specified conditions.
@@ -620,6 +644,32 @@ module ActiveRecord
620
644
  where(*args).delete_all
621
645
  end
622
646
 
647
+ # Schedule the query to be performed from a background thread pool.
648
+ #
649
+ # Post.where(published: true).load_async # => #<ActiveRecord::Relation>
650
+ def load_async
651
+ return load if !connection.async_enabled?
652
+
653
+ unless loaded?
654
+ result = exec_main_query(async: connection.current_transaction.closed?)
655
+
656
+ if result.is_a?(Array)
657
+ @records = result
658
+ else
659
+ @future_result = result
660
+ end
661
+ @loaded = true
662
+ end
663
+
664
+ self
665
+ end
666
+
667
+ # Returns <tt>true</tt> if the relation was scheduled on the background
668
+ # thread pool.
669
+ def scheduled?
670
+ !!@future_result
671
+ end
672
+
623
673
  # Causes the records to be loaded from the database if they have not
624
674
  # been loaded already. You can use this if for some reason you need
625
675
  # to explicitly load some records before actually using them. The
@@ -627,7 +677,7 @@ module ActiveRecord
627
677
  #
628
678
  # Post.where(published: true).load # => #<ActiveRecord::Relation>
629
679
  def load(&block)
630
- unless loaded?
680
+ if !loaded? || scheduled?
631
681
  @records = exec_queries(&block)
632
682
  @loaded = true
633
683
  end
@@ -642,11 +692,14 @@ module ActiveRecord
642
692
  end
643
693
 
644
694
  def reset
695
+ @future_result&.cancel
696
+ @future_result = nil
645
697
  @delegate_to_klass = false
646
698
  @to_sql = @arel = @loaded = @should_eager_load = nil
647
699
  @offsets = @take = nil
648
700
  @cache_keys = nil
649
- @records = [].freeze
701
+ @records = nil
702
+ @limited_count = nil
650
703
  self
651
704
  end
652
705
 
@@ -655,16 +708,14 @@ module ActiveRecord
655
708
  # User.where(name: 'Oscar').to_sql
656
709
  # # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
657
710
  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) }
711
+ @to_sql ||= if eager_loading?
712
+ apply_join_dependency do |relation, join_dependency|
713
+ relation = join_dependency.apply_column_aliases(relation)
714
+ relation.to_sql
667
715
  end
716
+ else
717
+ conn = klass.connection
718
+ conn.unprepared_statement { conn.to_sql(arel) }
668
719
  end
669
720
  end
670
721
 
@@ -722,6 +773,10 @@ module ActiveRecord
722
773
  @values.dup
723
774
  end
724
775
 
776
+ def values_for_queries # :nodoc:
777
+ @values.except(:extending, :skip_query_cache, :strict_loading)
778
+ end
779
+
725
780
  def inspect
726
781
  subject = loaded? ? records : annotate("loading for inspect")
727
782
  entries = subject.take([limit_value, 11].compact.min).map!(&:inspect)
@@ -756,11 +811,9 @@ module ActiveRecord
756
811
  def preload_associations(records) # :nodoc:
757
812
  preload = preload_values
758
813
  preload += includes_values unless eager_loading?
759
- preloader = nil
760
814
  scope = strict_loading_value ? StrictLoadingScope : nil
761
815
  preload.each do |associations|
762
- preloader ||= build_preloader
763
- preloader.preload records, associations, scope
816
+ ActiveRecord::Associations::Preloader.new(records: records, associations: associations, scope: scope).call
764
817
  end
765
818
  end
766
819
 
@@ -775,8 +828,12 @@ module ActiveRecord
775
828
  end
776
829
 
777
830
  private
778
- def already_in_scope?
779
- @delegate_to_klass && klass.current_scope(true)
831
+ def already_in_scope?(registry)
832
+ @delegate_to_klass && registry.current_scope(klass, true)
833
+ end
834
+
835
+ def global_scope?(registry)
836
+ registry.global_current_scope(klass, true)
780
837
  end
781
838
 
782
839
  def current_scope_restoring_block(&block)
@@ -799,11 +856,20 @@ module ActiveRecord
799
856
  klass.create!(attributes, &block)
800
857
  end
801
858
 
802
- def _scoping(scope)
803
- previous, klass.current_scope = klass.current_scope(true), scope
859
+ def _scoping(scope, registry, all_queries = false)
860
+ previous = registry.current_scope(klass, true)
861
+ registry.set_current_scope(klass, scope)
862
+
863
+ if all_queries
864
+ previous_global = registry.global_current_scope(klass, true)
865
+ registry.set_global_current_scope(klass, scope)
866
+ end
804
867
  yield
805
868
  ensure
806
- klass.current_scope = previous
869
+ registry.set_current_scope(klass, previous)
870
+ if all_queries
871
+ registry.set_global_current_scope(klass, previous_global)
872
+ end
807
873
  end
808
874
 
809
875
  def _substitute_values(values)
@@ -826,23 +892,15 @@ module ActiveRecord
826
892
 
827
893
  def exec_queries(&block)
828
894
  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
895
+ rows = if scheduled?
896
+ future = @future_result
897
+ @future_result = nil
898
+ future.result
899
+ else
900
+ exec_main_query
901
+ end
845
902
 
903
+ records = instantiate_records(rows, &block)
846
904
  preload_associations(records) unless skip_preloading_value
847
905
 
848
906
  records.each(&:readonly!) if readonly_value
@@ -852,18 +910,43 @@ module ActiveRecord
852
910
  end
853
911
  end
854
912
 
855
- def skip_query_cache_if_necessary
856
- if skip_query_cache_value
857
- uncached do
858
- yield
913
+ def exec_main_query(async: false)
914
+ skip_query_cache_if_necessary do
915
+ if where_clause.contradiction?
916
+ [].freeze
917
+ elsif eager_loading?
918
+ apply_join_dependency do |relation, join_dependency|
919
+ if relation.null_relation?
920
+ [].freeze
921
+ else
922
+ relation = join_dependency.apply_column_aliases(relation)
923
+ @_join_dependency = join_dependency
924
+ connection.select_all(relation.arel, "SQL", async: async)
925
+ end
926
+ end
927
+ else
928
+ klass._query_by_sql(arel, async: async)
859
929
  end
930
+ end
931
+ end
932
+
933
+ def instantiate_records(rows, &block)
934
+ return [].freeze if rows.empty?
935
+ if eager_loading?
936
+ records = @_join_dependency.instantiate(rows, strict_loading_value, &block).freeze
937
+ @_join_dependency = nil
938
+ records
860
939
  else
861
- yield
940
+ klass._load_from_sql(rows, &block).freeze
862
941
  end
863
942
  end
864
943
 
865
- def build_preloader
866
- ActiveRecord::Associations::Preloader.new
944
+ def skip_query_cache_if_necessary(&block)
945
+ if skip_query_cache_value
946
+ uncached(&block)
947
+ else
948
+ yield
949
+ end
867
950
  end
868
951
 
869
952
  def references_eager_loaded_tables?
@@ -889,5 +972,9 @@ module ActiveRecord
889
972
  # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
890
973
  string.scan(/[a-zA-Z_][.\w]+(?=.?\.)/).map!(&:downcase) - ["raw_sql_"]
891
974
  end
975
+
976
+ def limited_count
977
+ @limited_count ||= limit_value ? count : limit(2).count
978
+ end
892
979
  end
893
980
  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