activerecord 6.1.7.6 → 7.0.0

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 (238) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1055 -1180
  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 +18 -19
  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 +186 -52
  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 +90 -82
  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 +6 -21
  46. data/lib/active_record/base.rb +19 -1
  47. data/lib/active_record/callbacks.rb +2 -2
  48. data/lib/active_record/coders/yaml_column.rb +2 -14
  49. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
  50. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  52. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -561
  53. data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
  54. data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
  55. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  56. data/lib/active_record/connection_adapters/abstract/quoting.rb +43 -82
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +34 -13
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +69 -18
  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 +35 -23
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +35 -21
  66. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  67. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  68. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -1
  69. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
  70. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  71. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  73. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +28 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  77. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  78. data/lib/active_record/connection_adapters/postgresql/quoting.rb +50 -76
  79. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +32 -0
  80. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  81. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
  82. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  83. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +27 -16
  84. data/lib/active_record/connection_adapters/postgresql_adapter.rb +207 -107
  85. data/lib/active_record/connection_adapters/schema_cache.rb +39 -38
  86. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
  87. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +15 -16
  88. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +4 -2
  89. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -30
  90. data/lib/active_record/connection_adapters.rb +6 -5
  91. data/lib/active_record/connection_handling.rb +47 -53
  92. data/lib/active_record/core.rb +121 -146
  93. data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -1
  94. data/lib/active_record/database_configurations/database_config.rb +12 -9
  95. data/lib/active_record/database_configurations/hash_config.rb +63 -5
  96. data/lib/active_record/database_configurations/url_config.rb +2 -2
  97. data/lib/active_record/database_configurations.rb +15 -32
  98. data/lib/active_record/delegated_type.rb +52 -11
  99. data/lib/active_record/destroy_association_async_job.rb +1 -1
  100. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  101. data/lib/active_record/dynamic_matchers.rb +1 -1
  102. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  103. data/lib/active_record/encryption/cipher.rb +53 -0
  104. data/lib/active_record/encryption/config.rb +44 -0
  105. data/lib/active_record/encryption/configurable.rb +61 -0
  106. data/lib/active_record/encryption/context.rb +35 -0
  107. data/lib/active_record/encryption/contexts.rb +72 -0
  108. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  109. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  110. data/lib/active_record/encryption/encryptable_record.rb +208 -0
  111. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  112. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  113. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  114. data/lib/active_record/encryption/encryptor.rb +155 -0
  115. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  116. data/lib/active_record/encryption/errors.rb +15 -0
  117. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  118. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  119. data/lib/active_record/encryption/key.rb +28 -0
  120. data/lib/active_record/encryption/key_generator.rb +42 -0
  121. data/lib/active_record/encryption/key_provider.rb +46 -0
  122. data/lib/active_record/encryption/message.rb +33 -0
  123. data/lib/active_record/encryption/message_serializer.rb +90 -0
  124. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  125. data/lib/active_record/encryption/properties.rb +76 -0
  126. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  127. data/lib/active_record/encryption/scheme.rb +99 -0
  128. data/lib/active_record/encryption.rb +55 -0
  129. data/lib/active_record/enum.rb +49 -42
  130. data/lib/active_record/errors.rb +67 -4
  131. data/lib/active_record/explain_registry.rb +11 -6
  132. data/lib/active_record/fixture_set/file.rb +15 -1
  133. data/lib/active_record/fixture_set/table_row.rb +41 -6
  134. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  135. data/lib/active_record/fixtures.rb +17 -20
  136. data/lib/active_record/future_result.rb +139 -0
  137. data/lib/active_record/gem_version.rb +4 -4
  138. data/lib/active_record/inheritance.rb +55 -17
  139. data/lib/active_record/insert_all.rb +80 -14
  140. data/lib/active_record/integration.rb +4 -3
  141. data/lib/active_record/internal_metadata.rb +1 -5
  142. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  143. data/lib/active_record/locking/optimistic.rb +10 -9
  144. data/lib/active_record/locking/pessimistic.rb +9 -3
  145. data/lib/active_record/log_subscriber.rb +14 -3
  146. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  147. data/lib/active_record/middleware/database_selector.rb +8 -3
  148. data/lib/active_record/middleware/shard_selector.rb +60 -0
  149. data/lib/active_record/migration/command_recorder.rb +4 -4
  150. data/lib/active_record/migration/compatibility.rb +89 -10
  151. data/lib/active_record/migration/join_table.rb +1 -1
  152. data/lib/active_record/migration.rb +110 -80
  153. data/lib/active_record/model_schema.rb +45 -58
  154. data/lib/active_record/nested_attributes.rb +13 -12
  155. data/lib/active_record/no_touching.rb +3 -3
  156. data/lib/active_record/null_relation.rb +2 -6
  157. data/lib/active_record/persistence.rb +219 -52
  158. data/lib/active_record/query_cache.rb +2 -2
  159. data/lib/active_record/query_logs.rb +138 -0
  160. data/lib/active_record/querying.rb +15 -5
  161. data/lib/active_record/railtie.rb +127 -17
  162. data/lib/active_record/railties/controller_runtime.rb +1 -1
  163. data/lib/active_record/railties/databases.rake +66 -129
  164. data/lib/active_record/readonly_attributes.rb +11 -0
  165. data/lib/active_record/reflection.rb +67 -50
  166. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  167. data/lib/active_record/relation/batches.rb +3 -3
  168. data/lib/active_record/relation/calculations.rb +40 -36
  169. data/lib/active_record/relation/delegation.rb +6 -6
  170. data/lib/active_record/relation/finder_methods.rb +31 -35
  171. data/lib/active_record/relation/merger.rb +20 -13
  172. data/lib/active_record/relation/predicate_builder.rb +1 -6
  173. data/lib/active_record/relation/query_attribute.rb +5 -11
  174. data/lib/active_record/relation/query_methods.rb +235 -63
  175. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  176. data/lib/active_record/relation/spawn_methods.rb +2 -2
  177. data/lib/active_record/relation/where_clause.rb +10 -19
  178. data/lib/active_record/relation.rb +169 -84
  179. data/lib/active_record/result.rb +17 -7
  180. data/lib/active_record/runtime_registry.rb +9 -13
  181. data/lib/active_record/sanitization.rb +11 -7
  182. data/lib/active_record/schema_dumper.rb +10 -3
  183. data/lib/active_record/schema_migration.rb +4 -4
  184. data/lib/active_record/scoping/default.rb +61 -12
  185. data/lib/active_record/scoping/named.rb +3 -11
  186. data/lib/active_record/scoping.rb +64 -34
  187. data/lib/active_record/serialization.rb +1 -1
  188. data/lib/active_record/signed_id.rb +1 -1
  189. data/lib/active_record/store.rb +1 -6
  190. data/lib/active_record/suppressor.rb +11 -15
  191. data/lib/active_record/tasks/database_tasks.rb +116 -58
  192. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  193. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -12
  194. data/lib/active_record/test_databases.rb +1 -1
  195. data/lib/active_record/test_fixtures.rb +9 -13
  196. data/lib/active_record/timestamp.rb +3 -4
  197. data/lib/active_record/transactions.rb +9 -14
  198. data/lib/active_record/translation.rb +2 -2
  199. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  200. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  201. data/lib/active_record/type/internal/timezone.rb +2 -2
  202. data/lib/active_record/type/serialized.rb +1 -1
  203. data/lib/active_record/type/type_map.rb +17 -20
  204. data/lib/active_record/type.rb +1 -2
  205. data/lib/active_record/validations/associated.rb +1 -1
  206. data/lib/active_record/validations/uniqueness.rb +1 -1
  207. data/lib/active_record.rb +204 -28
  208. data/lib/arel/attributes/attribute.rb +0 -8
  209. data/lib/arel/crud.rb +28 -22
  210. data/lib/arel/delete_manager.rb +18 -4
  211. data/lib/arel/filter_predications.rb +9 -0
  212. data/lib/arel/insert_manager.rb +2 -3
  213. data/lib/arel/nodes/casted.rb +1 -1
  214. data/lib/arel/nodes/delete_statement.rb +12 -13
  215. data/lib/arel/nodes/filter.rb +10 -0
  216. data/lib/arel/nodes/function.rb +1 -0
  217. data/lib/arel/nodes/insert_statement.rb +2 -2
  218. data/lib/arel/nodes/select_core.rb +2 -2
  219. data/lib/arel/nodes/select_statement.rb +2 -2
  220. data/lib/arel/nodes/update_statement.rb +8 -3
  221. data/lib/arel/nodes.rb +1 -0
  222. data/lib/arel/predications.rb +11 -3
  223. data/lib/arel/select_manager.rb +10 -4
  224. data/lib/arel/table.rb +0 -1
  225. data/lib/arel/tree_manager.rb +0 -12
  226. data/lib/arel/update_manager.rb +18 -4
  227. data/lib/arel/visitors/dot.rb +80 -90
  228. data/lib/arel/visitors/mysql.rb +8 -2
  229. data/lib/arel/visitors/postgresql.rb +0 -10
  230. data/lib/arel/visitors/to_sql.rb +58 -2
  231. data/lib/arel.rb +2 -1
  232. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  233. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  234. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  235. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  236. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  237. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  238. metadata +58 -14
@@ -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 7.0.
172
- To migrate to Rails 7.0's behavior, use `relation.merge(other, rewhere: true)`.
173
- MSG
174
- false
175
- end
166
+
167
+ attr && referenced_columns[attr]
176
168
  end
177
169
  end
178
170
 
@@ -227,11 +219,10 @@ module ActiveRecord
227
219
  end
228
220
 
229
221
  def extract_node_value(node)
230
- case node
231
- when Array
232
- node.map { |v| extract_node_value(v) }
233
- when Arel::Nodes::BindParam, Arel::Nodes::Casted, Arel::Nodes::Quoted
222
+ if node.respond_to?(:value_before_type_cast)
234
223
  node.value_before_type_cast
224
+ elsif Array === node
225
+ node.map { |v| extract_node_value(v) }
235
226
  end
236
227
  end
237
228
  end
@@ -11,7 +11,7 @@ module ActiveRecord
11
11
  :reverse_order, :distinct, :create_with, :skip_query_cache]
12
12
 
13
13
  CLAUSE_METHODS = [:where, :having, :from]
14
- INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :group, :having]
14
+ INVALID_METHODS_FOR_DELETE_ALL = [:distinct]
15
15
 
16
16
  VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS + CLAUSE_METHODS
17
17
 
@@ -31,6 +31,8 @@ module ActiveRecord
31
31
  @loaded = false
32
32
  @predicate_builder = predicate_builder
33
33
  @delegate_to_klass = false
34
+ @future_result = nil
35
+ @records = nil
34
36
  end
35
37
 
36
38
  def initialize_copy(other)
@@ -38,11 +40,6 @@ module ActiveRecord
38
40
  reset
39
41
  end
40
42
 
41
- def arel_attribute(name) # :nodoc:
42
- table[name]
43
- end
44
- deprecate :arel_attribute
45
-
46
43
  def bind_attribute(name, value) # :nodoc:
47
44
  if reflection = klass._reflect_on_association(name)
48
45
  name = reflection.foreign_key
@@ -67,8 +64,12 @@ module ActiveRecord
67
64
  # user = users.new { |user| user.name = 'Oscar' }
68
65
  # user.name # => Oscar
69
66
  def new(attributes = nil, &block)
70
- block = current_scope_restoring_block(&block)
71
- scoping { _new(attributes, &block) }
67
+ if attributes.is_a?(Array)
68
+ attributes.collect { |attr| new(attr, &block) }
69
+ else
70
+ block = current_scope_restoring_block(&block)
71
+ scoping { _new(attributes, &block) }
72
+ end
72
73
  end
73
74
  alias build new
74
75
 
@@ -148,7 +149,7 @@ module ActiveRecord
148
149
  # above can be alternatively written this way:
149
150
  #
150
151
  # # Find the first user named "Scarlett" or create a new one with a
151
- # # different last name.
152
+ # # particular last name.
152
153
  # User.find_or_create_by(first_name: 'Scarlett') do |user|
153
154
  # user.last_name = 'Johansson'
154
155
  # end
@@ -175,7 +176,7 @@ module ActiveRecord
175
176
  find_by(attributes) || create!(attributes, &block)
176
177
  end
177
178
 
178
- # Attempts to create a record with the given attributes in a table that has a unique constraint
179
+ # Attempts to create a record with the given attributes in a table that has a unique database constraint
179
180
  # on one or several of its columns. If a row already exists with one or several of these
180
181
  # unique constraints, the exception such an insertion would normally raise is caught,
181
182
  # and the existing record with those attributes is found using #find_by!.
@@ -186,7 +187,7 @@ module ActiveRecord
186
187
  #
187
188
  # There are several drawbacks to #create_or_find_by, though:
188
189
  #
189
- # * The underlying table must have the relevant columns defined with unique constraints.
190
+ # * The underlying table must have the relevant columns defined with unique database constraints.
190
191
  # * A unique constraint violation may be triggered by only one, or at least less than all,
191
192
  # of the given attributes. This means that the subsequent #find_by! may fail to find a
192
193
  # matching record, which will then raise an <tt>ActiveRecord::RecordNotFound</tt> exception,
@@ -257,13 +258,20 @@ module ActiveRecord
257
258
 
258
259
  # Returns size of the records.
259
260
  def size
260
- loaded? ? @records.length : count(:all)
261
+ if loaded?
262
+ records.length
263
+ else
264
+ count(:all)
265
+ end
261
266
  end
262
267
 
263
268
  # Returns true if there are no records.
264
269
  def empty?
265
- return @records.empty? if loaded?
266
- !exists?
270
+ if loaded?
271
+ records.empty?
272
+ else
273
+ !exists?
274
+ end
267
275
  end
268
276
 
269
277
  # Returns true if there are no records.
@@ -281,13 +289,15 @@ module ActiveRecord
281
289
  # Returns true if there is exactly one record.
282
290
  def one?
283
291
  return super if block_given?
284
- limit_value ? records.one? : size == 1
292
+ return records.one? if loaded?
293
+ limited_count == 1
285
294
  end
286
295
 
287
296
  # Returns true if there is more than one record.
288
297
  def many?
289
298
  return super if block_given?
290
- limit_value ? records.many? : size > 1
299
+ return records.many? if loaded?
300
+ limited_count > 1
291
301
  end
292
302
 
293
303
  # Returns a stable cache key that can be used to identify this query.
@@ -344,7 +354,7 @@ module ActiveRecord
344
354
  def compute_cache_version(timestamp_column) # :nodoc:
345
355
  timestamp_column = timestamp_column.to_s
346
356
 
347
- if loaded? || distinct_value
357
+ if loaded?
348
358
  size = records.size
349
359
  if size > 0
350
360
  timestamp = records.map { |record| record.read_attribute(timestamp_column) }.max
@@ -357,6 +367,7 @@ module ActiveRecord
357
367
 
358
368
  if collection.has_limit_or_offset?
359
369
  query = collection.select("#{column} AS collection_cache_key_timestamp")
370
+ query._select!(table[Arel.star]) if distinct_value && collection.select_values.empty?
360
371
  subquery_alias = "subquery_for_cache_key"
361
372
  subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
362
373
  arel = query.build_subquery(subquery_alias, select_values % subquery_column)
@@ -377,7 +388,7 @@ module ActiveRecord
377
388
  end
378
389
 
379
390
  if timestamp
380
- "#{size}-#{timestamp.utc.to_s(cache_timestamp_format)}"
391
+ "#{size}-#{timestamp.utc.to_formatted_s(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,32 @@ 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
+ def load_async
650
+ return load if !connection.async_enabled?
651
+
652
+ unless loaded?
653
+ result = exec_main_query(async: connection.current_transaction.closed?)
654
+
655
+ if result.is_a?(Array)
656
+ @records = result
657
+ else
658
+ @future_result = result
659
+ end
660
+ @loaded = true
661
+ end
662
+
663
+ self
664
+ end
665
+
666
+ # Returns <tt>true</tt> if the relation was scheduled on the background
667
+ # thread pool.
668
+ def scheduled?
669
+ !!@future_result
670
+ end
671
+
623
672
  # Causes the records to be loaded from the database if they have not
624
673
  # been loaded already. You can use this if for some reason you need
625
674
  # to explicitly load some records before actually using them. The
@@ -627,7 +676,7 @@ module ActiveRecord
627
676
  #
628
677
  # Post.where(published: true).load # => #<ActiveRecord::Relation>
629
678
  def load(&block)
630
- unless loaded?
679
+ if !loaded? || scheduled?
631
680
  @records = exec_queries(&block)
632
681
  @loaded = true
633
682
  end
@@ -642,11 +691,13 @@ module ActiveRecord
642
691
  end
643
692
 
644
693
  def reset
694
+ @future_result&.cancel
695
+ @future_result = nil
645
696
  @delegate_to_klass = false
646
697
  @to_sql = @arel = @loaded = @should_eager_load = nil
647
698
  @offsets = @take = nil
648
699
  @cache_keys = nil
649
- @records = [].freeze
700
+ @records = nil
650
701
  self
651
702
  end
652
703
 
@@ -655,16 +706,14 @@ module ActiveRecord
655
706
  # User.where(name: 'Oscar').to_sql
656
707
  # # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
657
708
  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) }
709
+ @to_sql ||= if eager_loading?
710
+ apply_join_dependency do |relation, join_dependency|
711
+ relation = join_dependency.apply_column_aliases(relation)
712
+ relation.to_sql
667
713
  end
714
+ else
715
+ conn = klass.connection
716
+ conn.unprepared_statement { conn.to_sql(arel) }
668
717
  end
669
718
  end
670
719
 
@@ -722,6 +771,10 @@ module ActiveRecord
722
771
  @values.dup
723
772
  end
724
773
 
774
+ def values_for_queries # :nodoc:
775
+ @values.except(:extending, :skip_query_cache, :strict_loading)
776
+ end
777
+
725
778
  def inspect
726
779
  subject = loaded? ? records : annotate("loading for inspect")
727
780
  entries = subject.take([limit_value, 11].compact.min).map!(&:inspect)
@@ -756,11 +809,9 @@ module ActiveRecord
756
809
  def preload_associations(records) # :nodoc:
757
810
  preload = preload_values
758
811
  preload += includes_values unless eager_loading?
759
- preloader = nil
760
812
  scope = strict_loading_value ? StrictLoadingScope : nil
761
813
  preload.each do |associations|
762
- preloader ||= build_preloader
763
- preloader.preload records, associations, scope
814
+ ActiveRecord::Associations::Preloader.new(records: records, associations: associations, scope: scope).call
764
815
  end
765
816
  end
766
817
 
@@ -775,8 +826,12 @@ module ActiveRecord
775
826
  end
776
827
 
777
828
  private
778
- def already_in_scope?
779
- @delegate_to_klass && klass.current_scope(true)
829
+ def already_in_scope?(registry)
830
+ @delegate_to_klass && registry.current_scope(klass, true)
831
+ end
832
+
833
+ def global_scope?(registry)
834
+ registry.global_current_scope(klass, true)
780
835
  end
781
836
 
782
837
  def current_scope_restoring_block(&block)
@@ -799,11 +854,20 @@ module ActiveRecord
799
854
  klass.create!(attributes, &block)
800
855
  end
801
856
 
802
- def _scoping(scope)
803
- previous, klass.current_scope = klass.current_scope(true), scope
857
+ def _scoping(scope, registry, all_queries = false)
858
+ previous = registry.current_scope(klass, true)
859
+ registry.set_current_scope(klass, scope)
860
+
861
+ if all_queries
862
+ previous_global = registry.global_current_scope(klass, true)
863
+ registry.set_global_current_scope(klass, scope)
864
+ end
804
865
  yield
805
866
  ensure
806
- klass.current_scope = previous
867
+ registry.set_current_scope(klass, previous)
868
+ if all_queries
869
+ registry.set_global_current_scope(klass, previous_global)
870
+ end
807
871
  end
808
872
 
809
873
  def _substitute_values(values)
@@ -826,23 +890,15 @@ module ActiveRecord
826
890
 
827
891
  def exec_queries(&block)
828
892
  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
893
+ rows = if scheduled?
894
+ future = @future_result
895
+ @future_result = nil
896
+ future.result
897
+ else
898
+ exec_main_query
899
+ end
845
900
 
901
+ records = instantiate_records(rows, &block)
846
902
  preload_associations(records) unless skip_preloading_value
847
903
 
848
904
  records.each(&:readonly!) if readonly_value
@@ -852,18 +908,43 @@ module ActiveRecord
852
908
  end
853
909
  end
854
910
 
855
- def skip_query_cache_if_necessary
856
- if skip_query_cache_value
857
- uncached do
858
- yield
911
+ def exec_main_query(async: false)
912
+ skip_query_cache_if_necessary do
913
+ if where_clause.contradiction?
914
+ [].freeze
915
+ elsif eager_loading?
916
+ apply_join_dependency do |relation, join_dependency|
917
+ if relation.null_relation?
918
+ [].freeze
919
+ else
920
+ relation = join_dependency.apply_column_aliases(relation)
921
+ @_join_dependency = join_dependency
922
+ connection.select_all(relation.arel, "SQL", async: async)
923
+ end
924
+ end
925
+ else
926
+ klass._query_by_sql(arel, async: async)
859
927
  end
928
+ end
929
+ end
930
+
931
+ def instantiate_records(rows, &block)
932
+ return [].freeze if rows.empty?
933
+ if eager_loading?
934
+ records = @_join_dependency.instantiate(rows, strict_loading_value, &block).freeze
935
+ @_join_dependency = nil
936
+ records
860
937
  else
861
- yield
938
+ klass._load_from_sql(rows, &block).freeze
862
939
  end
863
940
  end
864
941
 
865
- def build_preloader
866
- ActiveRecord::Associations::Preloader.new
942
+ def skip_query_cache_if_necessary(&block)
943
+ if skip_query_cache_value
944
+ uncached(&block)
945
+ else
946
+ yield
947
+ end
867
948
  end
868
949
 
869
950
  def references_eager_loaded_tables?
@@ -889,5 +970,9 @@ module ActiveRecord
889
970
  # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
890
971
  string.scan(/[a-zA-Z_][.\w]+(?=.?\.)/).map!(&:downcase) - ["raw_sql_"]
891
972
  end
973
+
974
+ def limited_count
975
+ limit_value ? count : limit(2).count
976
+ end
892
977
  end
893
978
  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