activerecord 6.1.7.8 → 7.0.8.4

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