activerecord 7.1.5.2 → 8.1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +567 -2443
  3. data/README.rdoc +16 -16
  4. data/examples/performance.rb +2 -2
  5. data/lib/active_record/association_relation.rb +3 -2
  6. data/lib/active_record/associations/alias_tracker.rb +31 -23
  7. data/lib/active_record/associations/association.rb +43 -12
  8. data/lib/active_record/associations/belongs_to_association.rb +31 -8
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  10. data/lib/active_record/associations/builder/association.rb +23 -11
  11. data/lib/active_record/associations/builder/belongs_to.rb +18 -4
  12. data/lib/active_record/associations/builder/collection_association.rb +7 -3
  13. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
  14. data/lib/active_record/associations/builder/has_many.rb +3 -4
  15. data/lib/active_record/associations/builder/has_one.rb +3 -4
  16. data/lib/active_record/associations/builder/singular_association.rb +33 -5
  17. data/lib/active_record/associations/collection_association.rb +17 -9
  18. data/lib/active_record/associations/collection_proxy.rb +36 -5
  19. data/lib/active_record/associations/deprecation.rb +88 -0
  20. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  21. data/lib/active_record/associations/errors.rb +268 -0
  22. data/lib/active_record/associations/has_many_association.rb +1 -1
  23. data/lib/active_record/associations/has_many_through_association.rb +10 -3
  24. data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
  25. data/lib/active_record/associations/join_dependency.rb +4 -2
  26. data/lib/active_record/associations/nested_error.rb +47 -0
  27. data/lib/active_record/associations/preloader/association.rb +4 -3
  28. data/lib/active_record/associations/preloader/batch.rb +7 -1
  29. data/lib/active_record/associations/preloader/branch.rb +8 -1
  30. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  31. data/lib/active_record/associations/singular_association.rb +14 -3
  32. data/lib/active_record/associations/through_association.rb +1 -1
  33. data/lib/active_record/associations.rb +250 -315
  34. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  35. data/lib/active_record/attribute_assignment.rb +0 -2
  36. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  37. data/lib/active_record/attribute_methods/primary_key.rb +25 -61
  38. data/lib/active_record/attribute_methods/query.rb +34 -0
  39. data/lib/active_record/attribute_methods/read.rb +1 -13
  40. data/lib/active_record/attribute_methods/serialization.rb +20 -27
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +19 -20
  42. data/lib/active_record/attribute_methods.rb +71 -75
  43. data/lib/active_record/attributes.rb +96 -68
  44. data/lib/active_record/autosave_association.rb +93 -58
  45. data/lib/active_record/base.rb +5 -7
  46. data/lib/active_record/callbacks.rb +1 -1
  47. data/lib/active_record/coders/json.rb +14 -5
  48. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +50 -126
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
  50. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +52 -13
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +681 -135
  52. data/lib/active_record/connection_adapters/abstract/database_statements.rb +165 -86
  53. data/lib/active_record/connection_adapters/abstract/query_cache.rb +226 -78
  54. data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -114
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +37 -36
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +136 -34
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +163 -68
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +197 -129
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +112 -63
  62. data/lib/active_record/connection_adapters/column.rb +17 -4
  63. data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -3
  64. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
  65. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
  66. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
  67. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +79 -46
  68. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +97 -101
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +14 -33
  70. data/lib/active_record/connection_adapters/pool_config.rb +14 -13
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +98 -44
  73. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
  74. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  75. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
  77. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  78. data/lib/active_record/connection_adapters/postgresql/quoting.rb +79 -68
  79. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  80. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
  81. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +44 -41
  82. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +69 -32
  83. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +155 -75
  84. data/lib/active_record/connection_adapters/postgresql_adapter.rb +104 -121
  85. data/lib/active_record/connection_adapters/schema_cache.rb +126 -133
  86. data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
  87. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +91 -95
  88. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -54
  89. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
  90. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
  91. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +43 -2
  92. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -15
  93. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +231 -111
  94. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  95. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +40 -69
  96. data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
  97. data/lib/active_record/connection_adapters.rb +66 -0
  98. data/lib/active_record/connection_handling.rb +88 -46
  99. data/lib/active_record/core.rb +142 -58
  100. data/lib/active_record/counter_cache.rb +52 -18
  101. data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
  102. data/lib/active_record/database_configurations/database_config.rb +28 -5
  103. data/lib/active_record/database_configurations/hash_config.rb +103 -41
  104. data/lib/active_record/database_configurations/url_config.rb +30 -1
  105. data/lib/active_record/database_configurations.rb +8 -4
  106. data/lib/active_record/delegated_type.rb +43 -19
  107. data/lib/active_record/dynamic_matchers.rb +56 -71
  108. data/lib/active_record/encryption/config.rb +3 -1
  109. data/lib/active_record/encryption/encryptable_record.rb +12 -12
  110. data/lib/active_record/encryption/encrypted_attribute_type.rb +35 -6
  111. data/lib/active_record/encryption/encryptor.rb +58 -22
  112. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  113. data/lib/active_record/encryption/key_provider.rb +1 -1
  114. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  115. data/lib/active_record/encryption/message_serializer.rb +4 -0
  116. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  117. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  118. data/lib/active_record/encryption/scheme.rb +9 -2
  119. data/lib/active_record/enum.rb +57 -36
  120. data/lib/active_record/errors.rb +76 -26
  121. data/lib/active_record/explain.rb +14 -25
  122. data/lib/active_record/explain_registry.rb +51 -2
  123. data/lib/active_record/filter_attribute_handler.rb +73 -0
  124. data/lib/active_record/fixture_set/table_row.rb +19 -2
  125. data/lib/active_record/fixtures.rb +39 -35
  126. data/lib/active_record/future_result.rb +21 -13
  127. data/lib/active_record/gem_version.rb +3 -3
  128. data/lib/active_record/inheritance.rb +5 -3
  129. data/lib/active_record/insert_all.rb +28 -20
  130. data/lib/active_record/integration.rb +4 -1
  131. data/lib/active_record/internal_metadata.rb +48 -34
  132. data/lib/active_record/locking/optimistic.rb +15 -7
  133. data/lib/active_record/locking/pessimistic.rb +5 -0
  134. data/lib/active_record/log_subscriber.rb +3 -34
  135. data/lib/active_record/message_pack.rb +1 -1
  136. data/lib/active_record/middleware/shard_selector.rb +34 -17
  137. data/lib/active_record/migration/command_recorder.rb +46 -14
  138. data/lib/active_record/migration/compatibility.rb +40 -25
  139. data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
  140. data/lib/active_record/migration/default_strategy.rb +4 -5
  141. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  142. data/lib/active_record/migration.rb +119 -103
  143. data/lib/active_record/model_schema.rb +66 -78
  144. data/lib/active_record/nested_attributes.rb +17 -9
  145. data/lib/active_record/persistence.rb +161 -454
  146. data/lib/active_record/query_cache.rb +33 -15
  147. data/lib/active_record/query_logs.rb +106 -39
  148. data/lib/active_record/query_logs_formatter.rb +17 -28
  149. data/lib/active_record/querying.rb +28 -16
  150. data/lib/active_record/railtie.rb +61 -74
  151. data/lib/active_record/railties/controller_runtime.rb +19 -5
  152. data/lib/active_record/railties/databases.rake +64 -78
  153. data/lib/active_record/railties/job_checkpoints.rb +15 -0
  154. data/lib/active_record/railties/job_runtime.rb +10 -11
  155. data/lib/active_record/reflection.rb +147 -53
  156. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  157. data/lib/active_record/relation/batches.rb +153 -73
  158. data/lib/active_record/relation/calculations.rb +146 -96
  159. data/lib/active_record/relation/delegation.rb +30 -23
  160. data/lib/active_record/relation/finder_methods.rb +64 -34
  161. data/lib/active_record/relation/merger.rb +12 -14
  162. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  163. data/lib/active_record/relation/predicate_builder/association_query_value.rb +19 -11
  164. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
  165. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  166. data/lib/active_record/relation/predicate_builder.rb +25 -10
  167. data/lib/active_record/relation/query_attribute.rb +4 -2
  168. data/lib/active_record/relation/query_methods.rb +351 -124
  169. data/lib/active_record/relation/spawn_methods.rb +9 -25
  170. data/lib/active_record/relation/where_clause.rb +17 -30
  171. data/lib/active_record/relation.rb +590 -124
  172. data/lib/active_record/result.rb +131 -59
  173. data/lib/active_record/runtime_registry.rb +44 -21
  174. data/lib/active_record/sanitization.rb +33 -25
  175. data/lib/active_record/schema.rb +8 -6
  176. data/lib/active_record/schema_dumper.rb +66 -31
  177. data/lib/active_record/schema_migration.rb +31 -14
  178. data/lib/active_record/scoping/named.rb +6 -2
  179. data/lib/active_record/scoping.rb +0 -1
  180. data/lib/active_record/secure_token.rb +3 -3
  181. data/lib/active_record/signed_id.rb +68 -20
  182. data/lib/active_record/statement_cache.rb +29 -25
  183. data/lib/active_record/store.rb +51 -22
  184. data/lib/active_record/structured_event_subscriber.rb +85 -0
  185. data/lib/active_record/table_metadata.rb +7 -33
  186. data/lib/active_record/tasks/abstract_tasks.rb +76 -0
  187. data/lib/active_record/tasks/database_tasks.rb +118 -90
  188. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
  189. data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
  190. data/lib/active_record/tasks/sqlite_database_tasks.rb +17 -28
  191. data/lib/active_record/test_databases.rb +14 -4
  192. data/lib/active_record/test_fixtures.rb +123 -89
  193. data/lib/active_record/testing/query_assertions.rb +127 -0
  194. data/lib/active_record/timestamp.rb +6 -4
  195. data/lib/active_record/token_for.rb +22 -12
  196. data/lib/active_record/touch_later.rb +1 -1
  197. data/lib/active_record/transaction.rb +129 -0
  198. data/lib/active_record/transactions.rb +97 -18
  199. data/lib/active_record/translation.rb +0 -2
  200. data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
  201. data/lib/active_record/type/internal/timezone.rb +7 -0
  202. data/lib/active_record/type/json.rb +15 -2
  203. data/lib/active_record/type/serialized.rb +12 -7
  204. data/lib/active_record/type/type_map.rb +1 -1
  205. data/lib/active_record/type_caster/connection.rb +5 -4
  206. data/lib/active_record/validations/associated.rb +10 -4
  207. data/lib/active_record/validations/uniqueness.rb +23 -18
  208. data/lib/active_record/validations.rb +4 -1
  209. data/lib/active_record.rb +203 -59
  210. data/lib/arel/alias_predication.rb +3 -1
  211. data/lib/arel/collectors/bind.rb +4 -2
  212. data/lib/arel/collectors/composite.rb +7 -0
  213. data/lib/arel/collectors/sql_string.rb +2 -2
  214. data/lib/arel/collectors/substitute_binds.rb +3 -3
  215. data/lib/arel/crud.rb +8 -11
  216. data/lib/arel/delete_manager.rb +5 -0
  217. data/lib/arel/nodes/binary.rb +1 -7
  218. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  219. data/lib/arel/nodes/count.rb +2 -2
  220. data/lib/arel/nodes/delete_statement.rb +4 -2
  221. data/lib/arel/nodes/function.rb +4 -10
  222. data/lib/arel/nodes/named_function.rb +2 -2
  223. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  224. data/lib/arel/nodes/node.rb +6 -5
  225. data/lib/arel/nodes/sql_literal.rb +8 -1
  226. data/lib/arel/nodes/update_statement.rb +4 -2
  227. data/lib/arel/nodes.rb +2 -4
  228. data/lib/arel/predications.rb +1 -1
  229. data/lib/arel/select_manager.rb +14 -5
  230. data/lib/arel/table.rb +3 -7
  231. data/lib/arel/tree_manager.rb +3 -2
  232. data/lib/arel/update_manager.rb +7 -1
  233. data/lib/arel/visitors/dot.rb +3 -3
  234. data/lib/arel/visitors/mysql.rb +9 -4
  235. data/lib/arel/visitors/postgresql.rb +56 -12
  236. data/lib/arel/visitors/sqlite.rb +80 -8
  237. data/lib/arel/visitors/to_sql.rb +34 -37
  238. data/lib/arel.rb +10 -4
  239. data/lib/rails/generators/active_record/application_record/USAGE +1 -1
  240. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  241. metadata +23 -14
  242. data/lib/active_record/explain_subscriber.rb +0 -34
  243. data/lib/active_record/normalization.rb +0 -167
  244. data/lib/active_record/relation/record_fetch_warning.rb +0 -49
@@ -3,6 +3,54 @@
3
3
  module ActiveRecord
4
4
  # = Active Record \Relation
5
5
  class Relation
6
+ class ExplainProxy # :nodoc:
7
+ def initialize(relation, options)
8
+ @relation = relation
9
+ @options = options
10
+ end
11
+
12
+ def inspect
13
+ exec_explain { @relation.send(:exec_queries) }
14
+ end
15
+
16
+ def average(column_name)
17
+ exec_explain { @relation.average(column_name) }
18
+ end
19
+
20
+ def count(column_name = nil)
21
+ exec_explain { @relation.count(column_name) }
22
+ end
23
+
24
+ def first(limit = nil)
25
+ exec_explain { @relation.first(limit) }
26
+ end
27
+
28
+ def last(limit = nil)
29
+ exec_explain { @relation.last(limit) }
30
+ end
31
+
32
+ def maximum(column_name)
33
+ exec_explain { @relation.maximum(column_name) }
34
+ end
35
+
36
+ def minimum(column_name)
37
+ exec_explain { @relation.minimum(column_name) }
38
+ end
39
+
40
+ def pluck(*column_names)
41
+ exec_explain { @relation.pluck(*column_names) }
42
+ end
43
+
44
+ def sum(identity_or_column = nil)
45
+ exec_explain { @relation.sum(identity_or_column) }
46
+ end
47
+
48
+ private
49
+ def exec_explain(&block)
50
+ @relation.exec_explain(@relation.collecting_queries_for_explain { block.call }, @options)
51
+ end
52
+ end
53
+
6
54
  MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
7
55
  :order, :joins, :left_outer_joins, :references,
8
56
  :extending, :unscope, :optimizer_hints, :annotate,
@@ -12,26 +60,34 @@ module ActiveRecord
12
60
  :reverse_order, :distinct, :create_with, :skip_query_cache]
13
61
 
14
62
  CLAUSE_METHODS = [:where, :having, :from]
15
- INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :with]
63
+ INVALID_METHODS_FOR_UPDATE_AND_DELETE_ALL = [:distinct, :with, :with_recursive]
16
64
 
17
65
  VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS + CLAUSE_METHODS
18
66
 
19
67
  include Enumerable
20
68
  include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
69
+ include SignedId::RelationMethods, TokenFor::RelationMethods
21
70
 
22
- attr_reader :table, :klass, :loaded, :predicate_builder
71
+ attr_reader :table, :model, :loaded, :predicate_builder
23
72
  attr_accessor :skip_preloading_value
24
- alias :model :klass
73
+ alias :klass :model
25
74
  alias :loaded? :loaded
26
75
  alias :locked? :lock_value
27
76
 
28
- def initialize(klass, table: klass.arel_table, predicate_builder: klass.predicate_builder, values: {})
29
- @klass = klass
77
+ def initialize(model, table: nil, predicate_builder: nil, values: {})
78
+ if table
79
+ predicate_builder ||= model.predicate_builder.with(TableMetadata.new(model, table))
80
+ else
81
+ table = model.arel_table
82
+ predicate_builder ||= model.predicate_builder
83
+ end
84
+
85
+ @model = model
30
86
  @table = table
31
87
  @values = values
32
88
  @loaded = false
33
89
  @predicate_builder = predicate_builder
34
- @delegate_to_klass = false
90
+ @delegate_to_model = false
35
91
  @future_result = nil
36
92
  @records = nil
37
93
  @async = false
@@ -44,7 +100,7 @@ module ActiveRecord
44
100
  end
45
101
 
46
102
  def bind_attribute(name, value) # :nodoc:
47
- if reflection = klass._reflect_on_association(name)
103
+ if reflection = model._reflect_on_association(name)
48
104
  name = reflection.foreign_key
49
105
  value = value.read_attribute(reflection.association_primary_key) unless value.nil?
50
106
  end
@@ -207,18 +263,27 @@ module ActiveRecord
207
263
  # the problem of running out of integers, if the underlying table is still stuck on a primary
208
264
  # key of type int (note: All \Rails apps since 5.1+ have defaulted to bigint, which is not liable
209
265
  # to this problem).
266
+ # * Columns with unique database constraints should not have uniqueness validations defined,
267
+ # otherwise #create will fail due to validation errors and #find_by will never be called.
210
268
  #
211
269
  # This method will return a record if all given attributes are covered by unique constraints
212
270
  # (unless the INSERT -> DELETE -> SELECT race condition is triggered), but if creation was attempted
213
271
  # and failed due to validation errors it won't be persisted, you get what #create returns in
214
272
  # such situation.
215
273
  def create_or_find_by(attributes, &block)
216
- transaction(requires_new: true) { create(attributes, &block) }
217
- rescue ActiveRecord::RecordNotUnique
218
- if connection.transaction_open?
219
- where(attributes).lock.find_by!(attributes)
220
- else
221
- find_by!(attributes)
274
+ with_connection do |connection|
275
+ record = nil
276
+ transaction(requires_new: true) do
277
+ record = create(attributes, &block)
278
+ record._last_transaction_return_status || raise(ActiveRecord::Rollback)
279
+ end
280
+ record
281
+ rescue ActiveRecord::RecordNotUnique
282
+ if connection.transaction_open?
283
+ where(attributes).lock.find_by!(attributes)
284
+ else
285
+ find_by!(attributes)
286
+ end
222
287
  end
223
288
  end
224
289
 
@@ -226,16 +291,23 @@ module ActiveRecord
226
291
  # {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
227
292
  # is raised if the created record is invalid.
228
293
  def create_or_find_by!(attributes, &block)
229
- transaction(requires_new: true) { create!(attributes, &block) }
230
- rescue ActiveRecord::RecordNotUnique
231
- if connection.transaction_open?
232
- where(attributes).lock.find_by!(attributes)
233
- else
234
- find_by!(attributes)
294
+ with_connection do |connection|
295
+ record = nil
296
+ transaction(requires_new: true) do
297
+ record = create!(attributes, &block)
298
+ record._last_transaction_return_status || raise(ActiveRecord::Rollback)
299
+ end
300
+ record
301
+ rescue ActiveRecord::RecordNotUnique
302
+ if connection.transaction_open?
303
+ where(attributes).lock.find_by!(attributes)
304
+ else
305
+ find_by!(attributes)
306
+ end
235
307
  end
236
308
  end
237
309
 
238
- # Like #find_or_create_by, but calls {new}[rdoc-ref:Core#new]
310
+ # Like #find_or_create_by, but calls {new}[rdoc-ref:Core.new]
239
311
  # instead of {create}[rdoc-ref:Persistence::ClassMethods#create].
240
312
  def find_or_initialize_by(attributes, &block)
241
313
  find_by(attributes) || new(attributes, &block)
@@ -245,13 +317,30 @@ module ActiveRecord
245
317
  # returns the result as a string. The string is formatted imitating the
246
318
  # ones printed by the database shell.
247
319
  #
320
+ # User.all.explain
321
+ # # EXPLAIN SELECT `users`.* FROM `users`
322
+ # # ...
323
+ #
248
324
  # Note that this method actually runs the queries, since the results of some
249
325
  # are needed by the next ones when eager loading is going on.
250
326
  #
327
+ # To run EXPLAIN on queries created by +first+, +pluck+ and +count+, call
328
+ # these methods on +explain+:
329
+ #
330
+ # User.all.explain.count
331
+ # # EXPLAIN SELECT COUNT(*) FROM `users`
332
+ # # ...
333
+ #
334
+ # The column name can be passed if required:
335
+ #
336
+ # User.all.explain.maximum(:id)
337
+ # # EXPLAIN SELECT MAX(`users`.`id`) FROM `users`
338
+ # # ...
339
+ #
251
340
  # Please see further details in the
252
341
  # {Active Record Query Interface guide}[https://guides.rubyonrails.org/active_record_querying.html#running-explain].
253
342
  def explain(*options)
254
- exec_explain(collecting_queries_for_explain { exec_queries }, options)
343
+ ExplainProxy.new(self, options)
255
344
  end
256
345
 
257
346
  # Converts relation objects to Array.
@@ -358,14 +447,14 @@ module ActiveRecord
358
447
  # Product.where("name like ?", "%Game%").cache_key(:last_reviewed_at)
359
448
  def cache_key(timestamp_column = "updated_at")
360
449
  @cache_keys ||= {}
361
- @cache_keys[timestamp_column] ||= klass.collection_cache_key(self, timestamp_column)
450
+ @cache_keys[timestamp_column] ||= model.collection_cache_key(self, timestamp_column)
362
451
  end
363
452
 
364
453
  def compute_cache_key(timestamp_column = :updated_at) # :nodoc:
365
454
  query_signature = ActiveSupport::Digest.hexdigest(to_sql)
366
- key = "#{klass.model_name.cache_key}/query-#{query_signature}"
455
+ key = "#{model.model_name.cache_key}/query-#{query_signature}"
367
456
 
368
- if collection_cache_versioning
457
+ if model.collection_cache_versioning
369
458
  key
370
459
  else
371
460
  "#{key}-#{compute_cache_version(timestamp_column)}"
@@ -384,7 +473,7 @@ module ActiveRecord
384
473
  #
385
474
  # SELECT COUNT(*), MAX("products"."updated_at") FROM "products" WHERE (name like '%Cosmic Encounter%')
386
475
  def cache_version(timestamp_column = :updated_at)
387
- if collection_cache_versioning
476
+ if model.collection_cache_versioning
388
477
  @cache_versions ||= {}
389
478
  @cache_versions[timestamp_column] ||= compute_cache_version(timestamp_column)
390
479
  end
@@ -401,33 +490,35 @@ module ActiveRecord
401
490
  else
402
491
  collection = eager_loading? ? apply_join_dependency : self
403
492
 
404
- column = connection.visitor.compile(table[timestamp_column])
405
- select_values = "COUNT(*) AS #{connection.quote_column_name("size")}, MAX(%s) AS timestamp"
493
+ with_connection do |c|
494
+ column = c.visitor.compile(table[timestamp_column])
495
+ select_values = "COUNT(*) AS #{model.adapter_class.quote_column_name("size")}, MAX(%s) AS timestamp"
406
496
 
407
- if collection.has_limit_or_offset?
408
- query = collection.select("#{column} AS collection_cache_key_timestamp")
409
- query._select!(table[Arel.star]) if distinct_value && collection.select_values.empty?
410
- subquery_alias = "subquery_for_cache_key"
411
- subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
412
- arel = query.build_subquery(subquery_alias, select_values % subquery_column)
413
- else
414
- query = collection.unscope(:order)
415
- query.select_values = [select_values % column]
416
- arel = query.arel
417
- end
497
+ if collection.has_limit_or_offset?
498
+ query = collection.select("#{column} AS collection_cache_key_timestamp")
499
+ query._select!(table[Arel.star]) if distinct_value && collection.select_values.empty?
500
+ subquery_alias = "subquery_for_cache_key"
501
+ subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
502
+ arel = query.build_subquery(subquery_alias, select_values % subquery_column)
503
+ else
504
+ query = collection.unscope(:order)
505
+ query.select_values = [select_values % column]
506
+ arel = query.arel
507
+ end
418
508
 
419
- size, timestamp = connection.select_rows(arel, nil).first
509
+ size, timestamp = c.select_rows(arel, nil).first
420
510
 
421
- if size
422
- column_type = klass.type_for_attribute(timestamp_column)
423
- timestamp = column_type.deserialize(timestamp)
424
- else
425
- size = 0
511
+ if size
512
+ column_type = model.type_for_attribute(timestamp_column)
513
+ timestamp = column_type.deserialize(timestamp)
514
+ else
515
+ size = 0
516
+ end
426
517
  end
427
518
  end
428
519
 
429
520
  if timestamp
430
- "#{size}-#{timestamp.utc.to_fs(cache_timestamp_format)}"
521
+ "#{size}-#{timestamp.utc.to_fs(model.cache_timestamp_format)}"
431
522
  else
432
523
  "#{size}"
433
524
  end
@@ -458,7 +549,7 @@ module ActiveRecord
458
549
  # Please check unscoped if you want to remove all previous scopes (including
459
550
  # the default_scope) during the execution of a block.
460
551
  def scoping(all_queries: nil, &block)
461
- registry = klass.scope_registry
552
+ registry = model.scope_registry
462
553
  if global_scope?(registry) && all_queries == false
463
554
  raise ArgumentError, "Scoping is set to apply to all queries and cannot be unset in a nested block."
464
555
  elsif already_in_scope?(registry)
@@ -469,11 +560,11 @@ module ActiveRecord
469
560
  end
470
561
 
471
562
  def _exec_scope(...) # :nodoc:
472
- @delegate_to_klass = true
473
- registry = klass.scope_registry
563
+ @delegate_to_model = true
564
+ registry = model.scope_registry
474
565
  _scoping(nil, registry) { instance_exec(...) || self }
475
566
  ensure
476
- @delegate_to_klass = false
567
+ @delegate_to_model = false
477
568
  end
478
569
 
479
570
  # Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
@@ -509,37 +600,49 @@ module ActiveRecord
509
600
 
510
601
  return 0 if @none
511
602
 
603
+ invalid_methods = INVALID_METHODS_FOR_UPDATE_AND_DELETE_ALL.select do |method|
604
+ value = @values[method]
605
+ method == :distinct ? value : value&.any?
606
+ end
607
+ if invalid_methods.any?
608
+ ActiveRecord.deprecator.warn <<~MESSAGE
609
+ `#{invalid_methods.join(', ')}` is not supported by `update_all` and was never included in the generated query.
610
+
611
+ Calling `#{invalid_methods.join(', ')}` with `update_all` will raise an error in Rails 8.2.
612
+ MESSAGE
613
+ end
614
+
512
615
  if updates.is_a?(Hash)
513
- if klass.locking_enabled? &&
514
- !updates.key?(klass.locking_column) &&
515
- !updates.key?(klass.locking_column.to_sym)
516
- attr = table[klass.locking_column]
616
+ if model.locking_enabled? &&
617
+ !updates.key?(model.locking_column) &&
618
+ !updates.key?(model.locking_column.to_sym)
619
+ attr = table[model.locking_column]
517
620
  updates[attr.name] = _increment_attribute(attr)
518
621
  end
519
622
  values = _substitute_values(updates)
520
623
  else
521
- values = Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
624
+ values = Arel.sql(model.sanitize_sql_for_assignment(updates, table.name))
522
625
  end
523
626
 
524
- arel = eager_loading? ? apply_join_dependency.arel : build_arel
525
- arel.source.left = table
627
+ model.with_connection do |c|
628
+ arel = eager_loading? ? apply_join_dependency.arel : arel()
629
+ arel.source.left = table
526
630
 
527
- group_values_arel_columns = arel_columns(group_values.uniq)
528
- having_clause_ast = having_clause.ast unless having_clause.empty?
529
- key = if klass.composite_primary_key?
530
- primary_key.map { |pk| table[pk] }
531
- else
532
- table[primary_key]
631
+ key = if model.composite_primary_key?
632
+ primary_key.map { |pk| table[pk] }
633
+ else
634
+ table[primary_key]
635
+ end
636
+ stmt = arel.compile_update(values, key)
637
+ c.update(stmt, "#{model} Update All").tap { reset }
533
638
  end
534
- stmt = arel.compile_update(values, key, having_clause_ast, group_values_arel_columns)
535
- klass.connection.update(stmt, "#{klass} Update All").tap { reset }
536
639
  end
537
640
 
538
641
  def update(id = :all, attributes) # :nodoc:
539
642
  if id == :all
540
643
  each { |record| record.update(attributes) }
541
644
  else
542
- klass.update(id, attributes)
645
+ model.update(id, attributes)
543
646
  end
544
647
  end
545
648
 
@@ -547,10 +650,289 @@ module ActiveRecord
547
650
  if id == :all
548
651
  each { |record| record.update!(attributes) }
549
652
  else
550
- klass.update!(id, attributes)
653
+ model.update!(id, attributes)
551
654
  end
552
655
  end
553
656
 
657
+
658
+ # Inserts a single record into the database in a single SQL INSERT
659
+ # statement. It does not instantiate any models nor does it trigger
660
+ # Active Record callbacks or validations. Though passed values
661
+ # go through Active Record's type casting and serialization.
662
+ #
663
+ # See #insert_all for documentation.
664
+ def insert(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
665
+ insert_all([ attributes ], returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
666
+ end
667
+
668
+ # Inserts multiple records into the database in a single SQL INSERT
669
+ # statement. It does not instantiate any models nor does it trigger
670
+ # Active Record callbacks or validations. Though passed values
671
+ # go through Active Record's type casting and serialization.
672
+ #
673
+ # The +attributes+ parameter is an Array of Hashes. Every Hash determines
674
+ # the attributes for a single row and must have the same keys.
675
+ #
676
+ # Rows are considered to be unique by every unique index on the table. Any
677
+ # duplicate rows are skipped.
678
+ # Override with <tt>:unique_by</tt> (see below).
679
+ #
680
+ # Returns an ActiveRecord::Result with its contents based on
681
+ # <tt>:returning</tt> (see below).
682
+ #
683
+ # ==== Options
684
+ #
685
+ # [:returning]
686
+ # (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
687
+ # inserted records, which by default is the primary key.
688
+ # Pass <tt>returning: %w[ id name ]</tt> for both id and name
689
+ # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
690
+ # clause entirely.
691
+ #
692
+ # You can also pass an SQL string if you need more control on the return values
693
+ # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
694
+ #
695
+ # [:unique_by]
696
+ # (PostgreSQL and SQLite only) By default rows are considered to be unique
697
+ # by every unique index on the table. Any duplicate rows are skipped.
698
+ #
699
+ # To skip rows according to just one unique index pass <tt>:unique_by</tt>.
700
+ #
701
+ # Consider a Book model where no duplicate ISBNs make sense, but if any
702
+ # row has an existing id, or is not unique by another unique index,
703
+ # ActiveRecord::RecordNotUnique is raised.
704
+ #
705
+ # Unique indexes can be identified by columns or name:
706
+ #
707
+ # unique_by: :isbn
708
+ # unique_by: %i[ author_id name ]
709
+ # unique_by: :index_books_on_isbn
710
+ #
711
+ # [:record_timestamps]
712
+ # By default, automatic setting of timestamp columns is controlled by
713
+ # the model's <tt>record_timestamps</tt> config, matching typical
714
+ # behavior.
715
+ #
716
+ # To override this and force automatic setting of timestamp columns one
717
+ # way or the other, pass <tt>:record_timestamps</tt>:
718
+ #
719
+ # record_timestamps: true # Always set timestamps automatically
720
+ # record_timestamps: false # Never set timestamps automatically
721
+ #
722
+ # Because it relies on the index information from the database
723
+ # <tt>:unique_by</tt> is recommended to be paired with
724
+ # Active Record's schema_cache.
725
+ #
726
+ # ==== Example
727
+ #
728
+ # # Insert records and skip inserting any duplicates.
729
+ # # Here "Eloquent Ruby" is skipped because its id is not unique.
730
+ #
731
+ # Book.insert_all([
732
+ # { id: 1, title: "Rework", author: "David" },
733
+ # { id: 1, title: "Eloquent Ruby", author: "Russ" }
734
+ # ])
735
+ #
736
+ # # insert_all works on chained scopes, and you can use create_with
737
+ # # to set default attributes for all inserted records.
738
+ #
739
+ # author.books.create_with(created_at: Time.now).insert_all([
740
+ # { id: 1, title: "Rework" },
741
+ # { id: 2, title: "Eloquent Ruby" }
742
+ # ])
743
+ def insert_all(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
744
+ InsertAll.execute(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
745
+ end
746
+
747
+ # Inserts a single record into the database in a single SQL INSERT
748
+ # statement. It does not instantiate any models nor does it trigger
749
+ # Active Record callbacks or validations. Though passed values
750
+ # go through Active Record's type casting and serialization.
751
+ #
752
+ # See #insert_all! for more.
753
+ def insert!(attributes, returning: nil, record_timestamps: nil)
754
+ insert_all!([ attributes ], returning: returning, record_timestamps: record_timestamps)
755
+ end
756
+
757
+ # Inserts multiple records into the database in a single SQL INSERT
758
+ # statement. It does not instantiate any models nor does it trigger
759
+ # Active Record callbacks or validations. Though passed values
760
+ # go through Active Record's type casting and serialization.
761
+ #
762
+ # The +attributes+ parameter is an Array of Hashes. Every Hash determines
763
+ # the attributes for a single row and must have the same keys.
764
+ #
765
+ # Raises ActiveRecord::RecordNotUnique if any rows violate a
766
+ # unique index on the table. In that case, no rows are inserted.
767
+ #
768
+ # To skip duplicate rows, see #insert_all. To replace them, see #upsert_all.
769
+ #
770
+ # Returns an ActiveRecord::Result with its contents based on
771
+ # <tt>:returning</tt> (see below).
772
+ #
773
+ # ==== Options
774
+ #
775
+ # [:returning]
776
+ # (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
777
+ # inserted records, which by default is the primary key.
778
+ # Pass <tt>returning: %w[ id name ]</tt> for both id and name
779
+ # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
780
+ # clause entirely.
781
+ #
782
+ # You can also pass an SQL string if you need more control on the return values
783
+ # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
784
+ #
785
+ # [:record_timestamps]
786
+ # By default, automatic setting of timestamp columns is controlled by
787
+ # the model's <tt>record_timestamps</tt> config, matching typical
788
+ # behavior.
789
+ #
790
+ # To override this and force automatic setting of timestamp columns one
791
+ # way or the other, pass <tt>:record_timestamps</tt>:
792
+ #
793
+ # record_timestamps: true # Always set timestamps automatically
794
+ # record_timestamps: false # Never set timestamps automatically
795
+ #
796
+ # ==== Examples
797
+ #
798
+ # # Insert multiple records
799
+ # Book.insert_all!([
800
+ # { title: "Rework", author: "David" },
801
+ # { title: "Eloquent Ruby", author: "Russ" }
802
+ # ])
803
+ #
804
+ # # Raises ActiveRecord::RecordNotUnique because "Eloquent Ruby"
805
+ # # does not have a unique id.
806
+ # Book.insert_all!([
807
+ # { id: 1, title: "Rework", author: "David" },
808
+ # { id: 1, title: "Eloquent Ruby", author: "Russ" }
809
+ # ])
810
+ def insert_all!(attributes, returning: nil, record_timestamps: nil)
811
+ InsertAll.execute(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps)
812
+ end
813
+
814
+ # Updates or inserts (upserts) a single record into the database in a
815
+ # single SQL INSERT statement. It does not instantiate any models nor does
816
+ # it trigger Active Record callbacks or validations. Though passed values
817
+ # go through Active Record's type casting and serialization.
818
+ #
819
+ # See #upsert_all for documentation.
820
+ def upsert(attributes, **kwargs)
821
+ upsert_all([ attributes ], **kwargs)
822
+ end
823
+
824
+ # Updates or inserts (upserts) multiple records into the database in a
825
+ # single SQL INSERT statement. It does not instantiate any models nor does
826
+ # it trigger Active Record callbacks or validations. Though passed values
827
+ # go through Active Record's type casting and serialization.
828
+ #
829
+ # The +attributes+ parameter is an Array of Hashes. Every Hash determines
830
+ # the attributes for a single row and must have the same keys.
831
+ #
832
+ # Returns an ActiveRecord::Result with its contents based on
833
+ # <tt>:returning</tt> (see below).
834
+ #
835
+ # By default, +upsert_all+ will update all the columns that can be updated when
836
+ # there is a conflict. These are all the columns except primary keys, read-only
837
+ # columns, and columns covered by the optional +unique_by+.
838
+ #
839
+ # ==== Options
840
+ #
841
+ # [:returning]
842
+ # (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
843
+ # upserted records, which by default is the primary key.
844
+ # Pass <tt>returning: %w[ id name ]</tt> for both id and name
845
+ # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
846
+ # clause entirely.
847
+ #
848
+ # You can also pass an SQL string if you need more control on the return values
849
+ # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
850
+ #
851
+ # [:unique_by]
852
+ # (PostgreSQL and SQLite only) By default rows are considered to be unique
853
+ # by every unique index on the table. Any duplicate rows are skipped.
854
+ #
855
+ # To skip rows according to just one unique index pass <tt>:unique_by</tt>.
856
+ #
857
+ # Consider a Book model where no duplicate ISBNs make sense, but if any
858
+ # row has an existing id, or is not unique by another unique index,
859
+ # ActiveRecord::RecordNotUnique is raised.
860
+ #
861
+ # Unique indexes can be identified by columns or name:
862
+ #
863
+ # unique_by: :isbn
864
+ # unique_by: %i[ author_id name ]
865
+ # unique_by: :index_books_on_isbn
866
+ #
867
+ # Because it relies on the index information from the database
868
+ # <tt>:unique_by</tt> is recommended to be paired with
869
+ # Active Record's schema_cache.
870
+ #
871
+ # [:on_duplicate]
872
+ # Configure the behavior that will be used in case of conflict. Use `:skip`
873
+ # to ignore any conflicts or provide a safe SQL fragment wrapped with
874
+ # `Arel.sql`.
875
+ #
876
+ # NOTE: If you use this option you must provide all the columns you want to update
877
+ # by yourself.
878
+ #
879
+ # Example:
880
+ #
881
+ # Commodity.upsert_all(
882
+ # [
883
+ # { id: 2, name: "Copper", price: 4.84 },
884
+ # { id: 4, name: "Gold", price: 1380.87 },
885
+ # { id: 6, name: "Aluminium", price: 0.35 }
886
+ # ],
887
+ # on_duplicate: Arel.sql("price = GREATEST(commodities.price, EXCLUDED.price)")
888
+ # )
889
+ #
890
+ # See the related +:update_only+ option. Both options can't be used at the same time.
891
+ #
892
+ # [:update_only]
893
+ # Provide a list of column names that will be updated in case of conflict. If not provided,
894
+ # +upsert_all+ will update all the columns that can be updated. These are all the columns
895
+ # except primary keys, read-only columns, and columns covered by the optional +unique_by+
896
+ #
897
+ # Example:
898
+ #
899
+ # Commodity.upsert_all(
900
+ # [
901
+ # { id: 2, name: "Copper", price: 4.84 },
902
+ # { id: 4, name: "Gold", price: 1380.87 },
903
+ # { id: 6, name: "Aluminium", price: 0.35 }
904
+ # ],
905
+ # update_only: [:price] # Only prices will be updated
906
+ # )
907
+ #
908
+ # See the related +:on_duplicate+ option. Both options can't be used at the same time.
909
+ #
910
+ # [:record_timestamps]
911
+ # By default, automatic setting of timestamp columns is controlled by
912
+ # the model's <tt>record_timestamps</tt> config, matching typical
913
+ # behavior.
914
+ #
915
+ # To override this and force automatic setting of timestamp columns one
916
+ # way or the other, pass <tt>:record_timestamps</tt>:
917
+ #
918
+ # record_timestamps: true # Always set timestamps automatically
919
+ # record_timestamps: false # Never set timestamps automatically
920
+ #
921
+ # ==== Examples
922
+ #
923
+ # # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
924
+ # # Here "Eloquent Ruby" overwrites "Rework" because its ISBN is duplicate.
925
+ #
926
+ # Book.upsert_all([
927
+ # { title: "Rework", author: "David", isbn: "1" },
928
+ # { title: "Eloquent Ruby", author: "Russ", isbn: "1" }
929
+ # ], unique_by: :isbn)
930
+ #
931
+ # Book.find_by(isbn: "1").title # => "Eloquent Ruby"
932
+ def upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
933
+ InsertAll.execute(self, attributes, on_duplicate: on_duplicate, update_only: update_only, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
934
+ end
935
+
554
936
  # Updates the counters of the records in the current relation.
555
937
  #
556
938
  # ==== Parameters
@@ -576,7 +958,7 @@ module ActiveRecord
576
958
  names = touch if touch != true
577
959
  names = Array.wrap(names)
578
960
  options = names.extract_options!
579
- touch_updates = klass.touch_attributes_with_time(*names, **options)
961
+ touch_updates = model.touch_attributes_with_time(*names, **options)
580
962
  updates.merge!(touch_updates) unless touch_updates.empty?
581
963
  end
582
964
 
@@ -589,7 +971,7 @@ module ActiveRecord
589
971
  # If attribute names are passed, they are updated along with +updated_at+/+updated_on+ attributes.
590
972
  # If no time argument is passed, the current time is used as default.
591
973
  #
592
- # === Examples
974
+ # ==== Examples
593
975
  #
594
976
  # # Touch all records
595
977
  # Person.all.touch_all
@@ -607,7 +989,7 @@ module ActiveRecord
607
989
  # Person.where(name: 'David').touch_all
608
990
  # # => "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670' WHERE \"people\".\"name\" = 'David'"
609
991
  def touch_all(*names, time: nil)
610
- update_all klass.touch_attributes_with_time(*names, time: time)
992
+ update_all model.touch_attributes_with_time(*names, time: time)
611
993
  end
612
994
 
613
995
  # Destroys the records by instantiating each
@@ -640,7 +1022,7 @@ module ActiveRecord
640
1022
  #
641
1023
  # Post.where(person_id: 5).where(category: ['Something', 'Else']).delete_all
642
1024
  #
643
- # Both calls delete the affected posts all at once with a single DELETE statement.
1025
+ # This call deletes the affected posts all at once with a single DELETE statement.
644
1026
  # If you need to destroy dependent associations or call your <tt>before_*</tt> or
645
1027
  # +after_destroy+ callbacks, use the #destroy_all method instead.
646
1028
  #
@@ -651,7 +1033,7 @@ module ActiveRecord
651
1033
  def delete_all
652
1034
  return 0 if @none
653
1035
 
654
- invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select do |method|
1036
+ invalid_methods = INVALID_METHODS_FOR_UPDATE_AND_DELETE_ALL.select do |method|
655
1037
  value = @values[method]
656
1038
  method == :distinct ? value : value&.any?
657
1039
  end
@@ -659,19 +1041,77 @@ module ActiveRecord
659
1041
  raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
660
1042
  end
661
1043
 
662
- arel = eager_loading? ? apply_join_dependency.arel : build_arel
663
- arel.source.left = table
1044
+ model.with_connection do |c|
1045
+ arel = eager_loading? ? apply_join_dependency.arel : arel()
1046
+ arel.source.left = table
664
1047
 
665
- group_values_arel_columns = arel_columns(group_values.uniq)
666
- having_clause_ast = having_clause.ast unless having_clause.empty?
667
- key = if klass.composite_primary_key?
668
- primary_key.map { |pk| table[pk] }
1048
+ key = if model.composite_primary_key?
1049
+ primary_key.map { |pk| table[pk] }
1050
+ else
1051
+ table[primary_key]
1052
+ end
1053
+ stmt = arel.compile_delete(key)
1054
+
1055
+ c.delete(stmt, "#{model} Delete All").tap { reset }
1056
+ end
1057
+ end
1058
+
1059
+ # Deletes the row with a primary key matching the +id+ argument, using an
1060
+ # SQL +DELETE+ statement, and returns the number of rows deleted. Active
1061
+ # Record objects are not instantiated, so the object's callbacks are not
1062
+ # executed, including any <tt>:dependent</tt> association options.
1063
+ #
1064
+ # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
1065
+ #
1066
+ # Note: Although it is often much faster than the alternative, #destroy,
1067
+ # skipping callbacks might bypass business logic in your application
1068
+ # that ensures referential integrity or performs other essential jobs.
1069
+ #
1070
+ # ==== Examples
1071
+ #
1072
+ # # Delete a single row
1073
+ # Todo.delete(1)
1074
+ #
1075
+ # # Delete multiple rows
1076
+ # Todo.delete([2,3,4])
1077
+ def delete(id_or_array)
1078
+ return 0 if id_or_array.nil? || (id_or_array.is_a?(Array) && id_or_array.empty?)
1079
+
1080
+ where(model.primary_key => id_or_array).delete_all
1081
+ end
1082
+
1083
+
1084
+ # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
1085
+ # therefore all callbacks and filters are fired off before the object is deleted. This method is
1086
+ # less efficient than #delete but allows cleanup methods and other actions to be run.
1087
+ #
1088
+ # This essentially finds the object (or multiple objects) with the given id, creates a new object
1089
+ # from the attributes, and then calls destroy on it.
1090
+ #
1091
+ # ==== Parameters
1092
+ #
1093
+ # * +id+ - This should be the id or an array of ids to be destroyed.
1094
+ #
1095
+ # ==== Examples
1096
+ #
1097
+ # # Destroy a single object
1098
+ # Todo.destroy(1)
1099
+ #
1100
+ # # Destroy multiple objects
1101
+ # todos = [1,2,3]
1102
+ # Todo.destroy(todos)
1103
+ def destroy(id)
1104
+ multiple_ids = if model.composite_primary_key?
1105
+ id.first.is_a?(Array)
669
1106
  else
670
- table[primary_key]
1107
+ id.is_a?(Array)
671
1108
  end
672
- stmt = arel.compile_delete(key, having_clause_ast, group_values_arel_columns)
673
1109
 
674
- klass.connection.delete(stmt, "#{klass} Delete All").tap { reset }
1110
+ if multiple_ids
1111
+ find(id).each(&:destroy)
1112
+ else
1113
+ find(id).destroy
1114
+ end
675
1115
  end
676
1116
 
677
1117
  # Finds and destroys all records matching the specified conditions.
@@ -711,30 +1151,39 @@ module ActiveRecord
711
1151
  # for queries to actually be executed concurrently. Otherwise it defaults to
712
1152
  # executing them in the foreground.
713
1153
  #
714
- # +load_async+ will also fall back to executing in the foreground in the test environment when transactional
715
- # fixtures are enabled.
716
- #
717
1154
  # If the query was actually executed in the background, the Active Record logs will show
718
1155
  # it by prefixing the log line with <tt>ASYNC</tt>:
719
1156
  #
720
1157
  # ASYNC Post Load (0.0ms) (db time 2ms) SELECT "posts".* FROM "posts" LIMIT 100
721
1158
  def load_async
722
- return load if !connection.async_enabled?
1159
+ with_connection do |c|
1160
+ return load if !c.async_enabled?
723
1161
 
724
- unless loaded?
725
- result = exec_main_query(async: connection.current_transaction.closed?)
1162
+ unless loaded?
1163
+ result = exec_main_query(async: !c.current_transaction.joinable?)
726
1164
 
727
- if result.is_a?(Array)
728
- @records = result
729
- else
730
- @future_result = result
1165
+ if result.is_a?(Array)
1166
+ @records = result
1167
+ else
1168
+ @future_result = result
1169
+ end
1170
+ @loaded = true
731
1171
  end
732
- @loaded = true
733
1172
  end
734
1173
 
735
1174
  self
736
1175
  end
737
1176
 
1177
+ def then(&block) # :nodoc:
1178
+ if @future_result
1179
+ @future_result.then do
1180
+ yield self
1181
+ end
1182
+ else
1183
+ super
1184
+ end
1185
+ end
1186
+
738
1187
  # Returns <tt>true</tt> if the relation was scheduled on the background
739
1188
  # thread pool.
740
1189
  def scheduled?
@@ -765,7 +1214,7 @@ module ActiveRecord
765
1214
  def reset
766
1215
  @future_result&.cancel
767
1216
  @future_result = nil
768
- @delegate_to_klass = false
1217
+ @delegate_to_model = false
769
1218
  @to_sql = @arel = @loaded = @should_eager_load = nil
770
1219
  @offsets = @take = nil
771
1220
  @cache_keys = nil
@@ -785,8 +1234,9 @@ module ActiveRecord
785
1234
  relation.to_sql
786
1235
  end
787
1236
  else
788
- conn = klass.connection
789
- conn.unprepared_statement { conn.to_sql(arel) }
1237
+ model.with_connection do |conn|
1238
+ conn.unprepared_statement { conn.to_sql(arel) }
1239
+ end
790
1240
  end
791
1241
  end
792
1242
 
@@ -794,12 +1244,12 @@ module ActiveRecord
794
1244
  #
795
1245
  # User.where(name: 'Oscar').where_values_hash
796
1246
  # # => {name: "Oscar"}
797
- def where_values_hash(relation_table_name = klass.table_name) # :nodoc:
1247
+ def where_values_hash(relation_table_name = model.table_name) # :nodoc:
798
1248
  where_clause.to_h(relation_table_name)
799
1249
  end
800
1250
 
801
1251
  def scope_for_create
802
- hash = where_clause.to_h(klass.table_name, equality_only: true)
1252
+ hash = where_clause.to_h(model.table_name, equality_only: true)
803
1253
  create_with_value.each { |k, v| hash[k.to_s] = v } unless create_with_value.empty?
804
1254
  hash
805
1255
  end
@@ -845,6 +1295,10 @@ module ActiveRecord
845
1295
  records.blank?
846
1296
  end
847
1297
 
1298
+ def readonly?
1299
+ readonly_value
1300
+ end
1301
+
848
1302
  def values
849
1303
  @values.dup
850
1304
  end
@@ -863,7 +1317,7 @@ module ActiveRecord
863
1317
  end
864
1318
 
865
1319
  def empty_scope? # :nodoc:
866
- @values == klass.unscoped.values
1320
+ @values == model.unscoped.values
867
1321
  end
868
1322
 
869
1323
  def has_limit_or_offset? # :nodoc:
@@ -871,7 +1325,7 @@ module ActiveRecord
871
1325
  end
872
1326
 
873
1327
  def alias_tracker(joins = [], aliases = nil) # :nodoc:
874
- ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins, aliases)
1328
+ ActiveRecord::Associations::AliasTracker.create(model.connection_pool, table.name, joins, aliases)
875
1329
  end
876
1330
 
877
1331
  class StrictLoadingScope # :nodoc:
@@ -901,54 +1355,58 @@ module ActiveRecord
901
1355
 
902
1356
  private
903
1357
  def already_in_scope?(registry)
904
- @delegate_to_klass && registry.current_scope(klass, true)
1358
+ @delegate_to_model && registry.current_scope(model, true)
905
1359
  end
906
1360
 
907
1361
  def global_scope?(registry)
908
- registry.global_current_scope(klass, true)
1362
+ registry.global_current_scope(model, true)
909
1363
  end
910
1364
 
911
1365
  def current_scope_restoring_block(&block)
912
- current_scope = klass.current_scope(true)
1366
+ current_scope = model.current_scope(true)
913
1367
  -> record do
914
- klass.current_scope = current_scope
1368
+ model.current_scope = current_scope
915
1369
  yield record if block_given?
916
1370
  end
917
1371
  end
918
1372
 
919
1373
  def _new(attributes, &block)
920
- klass.new(attributes, &block)
1374
+ model.new(attributes, &block)
921
1375
  end
922
1376
 
923
1377
  def _create(attributes, &block)
924
- klass.create(attributes, &block)
1378
+ model.create(attributes, &block)
925
1379
  end
926
1380
 
927
1381
  def _create!(attributes, &block)
928
- klass.create!(attributes, &block)
1382
+ model.create!(attributes, &block)
929
1383
  end
930
1384
 
931
1385
  def _scoping(scope, registry, all_queries = false)
932
- previous = registry.current_scope(klass, true)
933
- registry.set_current_scope(klass, scope)
1386
+ previous = registry.current_scope(model, true)
1387
+ registry.set_current_scope(model, scope)
934
1388
 
935
1389
  if all_queries
936
- previous_global = registry.global_current_scope(klass, true)
937
- registry.set_global_current_scope(klass, scope)
1390
+ previous_global = registry.global_current_scope(model, true)
1391
+ registry.set_global_current_scope(model, scope)
938
1392
  end
939
1393
  yield
940
1394
  ensure
941
- registry.set_current_scope(klass, previous)
1395
+ registry.set_current_scope(model, previous)
942
1396
  if all_queries
943
- registry.set_global_current_scope(klass, previous_global)
1397
+ registry.set_global_current_scope(model, previous_global)
944
1398
  end
945
1399
  end
946
1400
 
947
1401
  def _substitute_values(values)
948
1402
  values.map do |name, value|
949
1403
  attr = table[name]
950
- unless Arel.arel_node?(value)
951
- type = klass.type_for_attribute(attr.name)
1404
+ if Arel.arel_node?(value)
1405
+ if value.is_a?(Arel::Nodes::SqlLiteral)
1406
+ value = Arel::Nodes::Grouping.new(value)
1407
+ end
1408
+ else
1409
+ type = model.type_for_attribute(attr.name)
952
1410
  value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
953
1411
  end
954
1412
  [attr, value]
@@ -957,12 +1415,16 @@ module ActiveRecord
957
1415
 
958
1416
  def _increment_attribute(attribute, value = 1)
959
1417
  bind = predicate_builder.build_bind_attribute(attribute.name, value.abs)
960
- expr = table.coalesce(Arel::Nodes::UnqualifiedColumn.new(attribute), 0)
1418
+ expr = table.coalesce(attribute, 0)
961
1419
  expr = value < 0 ? expr - bind : expr + bind
962
1420
  expr.expr
963
1421
  end
964
1422
 
965
1423
  def exec_queries(&block)
1424
+ if lock_value && model.current_preventing_writes
1425
+ raise ActiveRecord::ReadOnlyError, "Lock query attempted while in readonly mode"
1426
+ end
1427
+
966
1428
  skip_query_cache_if_necessary do
967
1429
  rows = if scheduled?
968
1430
  future = @future_result
@@ -995,17 +1457,21 @@ module ActiveRecord
995
1457
  if where_clause.contradiction?
996
1458
  [].freeze
997
1459
  elsif eager_loading?
998
- apply_join_dependency do |relation, join_dependency|
999
- if relation.null_relation?
1000
- [].freeze
1001
- else
1002
- relation = join_dependency.apply_column_aliases(relation)
1003
- @_join_dependency = join_dependency
1004
- connection.select_all(relation.arel, "SQL", async: async)
1460
+ model.with_connection do |c|
1461
+ apply_join_dependency do |relation, join_dependency|
1462
+ if relation.null_relation?
1463
+ [].freeze
1464
+ else
1465
+ relation = join_dependency.apply_column_aliases(relation)
1466
+ @_join_dependency = join_dependency
1467
+ c.select_all(relation.arel, "#{model.name} Eager Load", async: async)
1468
+ end
1005
1469
  end
1006
1470
  end
1007
1471
  else
1008
- klass._query_by_sql(arel, async: async)
1472
+ model.with_connection do |c|
1473
+ model._query_by_sql(c, arel, async: async)
1474
+ end
1009
1475
  end
1010
1476
  end
1011
1477
  end
@@ -1017,13 +1483,13 @@ module ActiveRecord
1017
1483
  @_join_dependency = nil
1018
1484
  records
1019
1485
  else
1020
- klass._load_from_sql(rows, &block).freeze
1486
+ model._load_from_sql(rows, &block).freeze
1021
1487
  end
1022
1488
  end
1023
1489
 
1024
1490
  def skip_query_cache_if_necessary(&block)
1025
1491
  if skip_query_cache_value
1026
- uncached(&block)
1492
+ model.uncached(&block)
1027
1493
  else
1028
1494
  yield
1029
1495
  end