activerecord 7.1.6 → 7.2.3

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 (193) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +839 -2248
  3. data/README.rdoc +16 -16
  4. data/examples/performance.rb +2 -2
  5. data/lib/active_record/association_relation.rb +1 -1
  6. data/lib/active_record/associations/alias_tracker.rb +31 -23
  7. data/lib/active_record/associations/association.rb +15 -8
  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/belongs_to.rb +1 -0
  11. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
  12. data/lib/active_record/associations/builder/has_many.rb +3 -4
  13. data/lib/active_record/associations/builder/has_one.rb +3 -4
  14. data/lib/active_record/associations/collection_association.rb +16 -8
  15. data/lib/active_record/associations/collection_proxy.rb +14 -1
  16. data/lib/active_record/associations/errors.rb +265 -0
  17. data/lib/active_record/associations/has_many_association.rb +1 -1
  18. data/lib/active_record/associations/has_many_through_association.rb +7 -1
  19. data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
  20. data/lib/active_record/associations/nested_error.rb +47 -0
  21. data/lib/active_record/associations/preloader/association.rb +2 -1
  22. data/lib/active_record/associations/preloader/branch.rb +7 -1
  23. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  24. data/lib/active_record/associations/singular_association.rb +6 -0
  25. data/lib/active_record/associations/through_association.rb +1 -1
  26. data/lib/active_record/associations.rb +59 -292
  27. data/lib/active_record/attribute_assignment.rb +0 -2
  28. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  29. data/lib/active_record/attribute_methods/primary_key.rb +23 -55
  30. data/lib/active_record/attribute_methods/read.rb +1 -13
  31. data/lib/active_record/attribute_methods/serialization.rb +5 -25
  32. data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -6
  33. data/lib/active_record/attribute_methods.rb +51 -60
  34. data/lib/active_record/attributes.rb +93 -68
  35. data/lib/active_record/autosave_association.rb +25 -32
  36. data/lib/active_record/base.rb +4 -5
  37. data/lib/active_record/callbacks.rb +1 -1
  38. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +24 -107
  39. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
  40. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +294 -72
  41. data/lib/active_record/connection_adapters/abstract/database_statements.rb +34 -17
  42. data/lib/active_record/connection_adapters/abstract/query_cache.rb +201 -75
  43. data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
  44. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +6 -2
  45. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +18 -6
  46. data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -62
  47. data/lib/active_record/connection_adapters/abstract_adapter.rb +46 -44
  48. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +53 -15
  49. data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
  50. data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
  51. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +6 -0
  52. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +19 -18
  53. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -23
  54. data/lib/active_record/connection_adapters/pool_config.rb +7 -6
  55. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
  56. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  57. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  58. data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
  59. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +30 -8
  60. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +1 -1
  61. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +16 -12
  62. data/lib/active_record/connection_adapters/postgresql_adapter.rb +36 -26
  63. data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
  64. data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
  65. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
  66. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -46
  67. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  68. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
  69. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  70. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +26 -2
  71. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +133 -78
  72. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +15 -15
  73. data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -48
  74. data/lib/active_record/connection_adapters.rb +121 -0
  75. data/lib/active_record/connection_handling.rb +68 -49
  76. data/lib/active_record/core.rb +112 -44
  77. data/lib/active_record/counter_cache.rb +19 -10
  78. data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
  79. data/lib/active_record/database_configurations/database_config.rb +19 -4
  80. data/lib/active_record/database_configurations/hash_config.rb +38 -34
  81. data/lib/active_record/database_configurations/url_config.rb +20 -1
  82. data/lib/active_record/database_configurations.rb +1 -1
  83. data/lib/active_record/delegated_type.rb +42 -18
  84. data/lib/active_record/dynamic_matchers.rb +2 -2
  85. data/lib/active_record/encryption/encryptable_record.rb +4 -4
  86. data/lib/active_record/encryption/encrypted_attribute_type.rb +25 -5
  87. data/lib/active_record/encryption/encryptor.rb +35 -19
  88. data/lib/active_record/encryption/key_provider.rb +1 -1
  89. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  90. data/lib/active_record/encryption/message_serializer.rb +4 -0
  91. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  92. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  93. data/lib/active_record/enum.rb +31 -13
  94. data/lib/active_record/errors.rb +49 -23
  95. data/lib/active_record/explain.rb +13 -24
  96. data/lib/active_record/fixture_set/table_row.rb +19 -2
  97. data/lib/active_record/fixtures.rb +37 -31
  98. data/lib/active_record/future_result.rb +8 -4
  99. data/lib/active_record/gem_version.rb +2 -2
  100. data/lib/active_record/inheritance.rb +4 -2
  101. data/lib/active_record/insert_all.rb +18 -15
  102. data/lib/active_record/integration.rb +4 -1
  103. data/lib/active_record/internal_metadata.rb +48 -34
  104. data/lib/active_record/locking/optimistic.rb +7 -6
  105. data/lib/active_record/log_subscriber.rb +0 -21
  106. data/lib/active_record/message_pack.rb +1 -1
  107. data/lib/active_record/migration/command_recorder.rb +2 -3
  108. data/lib/active_record/migration/compatibility.rb +5 -3
  109. data/lib/active_record/migration/default_strategy.rb +4 -5
  110. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  111. data/lib/active_record/migration.rb +87 -77
  112. data/lib/active_record/model_schema.rb +31 -68
  113. data/lib/active_record/nested_attributes.rb +11 -3
  114. data/lib/active_record/normalization.rb +3 -7
  115. data/lib/active_record/persistence.rb +30 -352
  116. data/lib/active_record/query_cache.rb +19 -8
  117. data/lib/active_record/query_logs.rb +19 -0
  118. data/lib/active_record/querying.rb +25 -13
  119. data/lib/active_record/railtie.rb +39 -57
  120. data/lib/active_record/railties/controller_runtime.rb +13 -4
  121. data/lib/active_record/railties/databases.rake +42 -44
  122. data/lib/active_record/reflection.rb +98 -36
  123. data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
  124. data/lib/active_record/relation/batches.rb +14 -8
  125. data/lib/active_record/relation/calculations.rb +127 -89
  126. data/lib/active_record/relation/delegation.rb +8 -11
  127. data/lib/active_record/relation/finder_methods.rb +26 -12
  128. data/lib/active_record/relation/merger.rb +4 -6
  129. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  130. data/lib/active_record/relation/predicate_builder/association_query_value.rb +10 -2
  131. data/lib/active_record/relation/predicate_builder.rb +3 -3
  132. data/lib/active_record/relation/query_attribute.rb +1 -1
  133. data/lib/active_record/relation/query_methods.rb +238 -65
  134. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  135. data/lib/active_record/relation/spawn_methods.rb +2 -18
  136. data/lib/active_record/relation/where_clause.rb +15 -21
  137. data/lib/active_record/relation.rb +508 -74
  138. data/lib/active_record/result.rb +31 -44
  139. data/lib/active_record/runtime_registry.rb +39 -0
  140. data/lib/active_record/sanitization.rb +24 -19
  141. data/lib/active_record/schema.rb +8 -6
  142. data/lib/active_record/schema_dumper.rb +48 -20
  143. data/lib/active_record/schema_migration.rb +30 -14
  144. data/lib/active_record/scoping/named.rb +1 -0
  145. data/lib/active_record/secure_token.rb +3 -3
  146. data/lib/active_record/signed_id.rb +27 -7
  147. data/lib/active_record/statement_cache.rb +7 -7
  148. data/lib/active_record/table_metadata.rb +1 -10
  149. data/lib/active_record/tasks/database_tasks.rb +69 -41
  150. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  151. data/lib/active_record/tasks/postgresql_database_tasks.rb +8 -1
  152. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
  153. data/lib/active_record/test_fixtures.rb +86 -89
  154. data/lib/active_record/testing/query_assertions.rb +121 -0
  155. data/lib/active_record/timestamp.rb +2 -2
  156. data/lib/active_record/token_for.rb +22 -12
  157. data/lib/active_record/touch_later.rb +1 -1
  158. data/lib/active_record/transaction.rb +132 -0
  159. data/lib/active_record/transactions.rb +73 -15
  160. data/lib/active_record/translation.rb +0 -2
  161. data/lib/active_record/type/serialized.rb +1 -3
  162. data/lib/active_record/type_caster/connection.rb +4 -4
  163. data/lib/active_record/validations/associated.rb +9 -3
  164. data/lib/active_record/validations/uniqueness.rb +15 -10
  165. data/lib/active_record/validations.rb +4 -1
  166. data/lib/active_record.rb +148 -39
  167. data/lib/arel/alias_predication.rb +1 -1
  168. data/lib/arel/collectors/bind.rb +3 -1
  169. data/lib/arel/collectors/composite.rb +7 -0
  170. data/lib/arel/collectors/sql_string.rb +1 -1
  171. data/lib/arel/collectors/substitute_binds.rb +1 -1
  172. data/lib/arel/crud.rb +2 -0
  173. data/lib/arel/delete_manager.rb +5 -0
  174. data/lib/arel/nodes/binary.rb +0 -6
  175. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  176. data/lib/arel/nodes/delete_statement.rb +4 -2
  177. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  178. data/lib/arel/nodes/node.rb +4 -3
  179. data/lib/arel/nodes/sql_literal.rb +7 -0
  180. data/lib/arel/nodes/update_statement.rb +4 -2
  181. data/lib/arel/nodes.rb +2 -2
  182. data/lib/arel/predications.rb +1 -1
  183. data/lib/arel/select_manager.rb +7 -3
  184. data/lib/arel/tree_manager.rb +3 -2
  185. data/lib/arel/update_manager.rb +7 -1
  186. data/lib/arel/visitors/dot.rb +3 -0
  187. data/lib/arel/visitors/mysql.rb +9 -4
  188. data/lib/arel/visitors/postgresql.rb +1 -12
  189. data/lib/arel/visitors/sqlite.rb +25 -0
  190. data/lib/arel/visitors/to_sql.rb +31 -16
  191. data/lib/arel.rb +7 -3
  192. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  193. metadata +16 -10
@@ -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,12 +60,13 @@ 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_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
71
  attr_reader :table, :klass, :loaded, :predicate_builder
23
72
  attr_accessor :skip_preloading_value
@@ -207,18 +256,27 @@ module ActiveRecord
207
256
  # the problem of running out of integers, if the underlying table is still stuck on a primary
208
257
  # key of type int (note: All \Rails apps since 5.1+ have defaulted to bigint, which is not liable
209
258
  # to this problem).
259
+ # * Columns with unique database constraints should not have uniqueness validations defined,
260
+ # otherwise #create will fail due to validation errors and #find_by will never be called.
210
261
  #
211
262
  # This method will return a record if all given attributes are covered by unique constraints
212
263
  # (unless the INSERT -> DELETE -> SELECT race condition is triggered), but if creation was attempted
213
264
  # and failed due to validation errors it won't be persisted, you get what #create returns in
214
265
  # such situation.
215
266
  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)
267
+ with_connection do |connection|
268
+ record = nil
269
+ transaction(requires_new: true) do
270
+ record = create(attributes, &block)
271
+ record._last_transaction_return_status || raise(ActiveRecord::Rollback)
272
+ end
273
+ record
274
+ rescue ActiveRecord::RecordNotUnique
275
+ if connection.transaction_open?
276
+ where(attributes).lock.find_by!(attributes)
277
+ else
278
+ find_by!(attributes)
279
+ end
222
280
  end
223
281
  end
224
282
 
@@ -226,16 +284,23 @@ module ActiveRecord
226
284
  # {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
227
285
  # is raised if the created record is invalid.
228
286
  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)
287
+ with_connection do |connection|
288
+ record = nil
289
+ transaction(requires_new: true) do
290
+ record = create!(attributes, &block)
291
+ record._last_transaction_return_status || raise(ActiveRecord::Rollback)
292
+ end
293
+ record
294
+ rescue ActiveRecord::RecordNotUnique
295
+ if connection.transaction_open?
296
+ where(attributes).lock.find_by!(attributes)
297
+ else
298
+ find_by!(attributes)
299
+ end
235
300
  end
236
301
  end
237
302
 
238
- # Like #find_or_create_by, but calls {new}[rdoc-ref:Core#new]
303
+ # Like #find_or_create_by, but calls {new}[rdoc-ref:Core.new]
239
304
  # instead of {create}[rdoc-ref:Persistence::ClassMethods#create].
240
305
  def find_or_initialize_by(attributes, &block)
241
306
  find_by(attributes) || new(attributes, &block)
@@ -245,13 +310,30 @@ module ActiveRecord
245
310
  # returns the result as a string. The string is formatted imitating the
246
311
  # ones printed by the database shell.
247
312
  #
313
+ # User.all.explain
314
+ # # EXPLAIN SELECT `users`.* FROM `users`
315
+ # # ...
316
+ #
248
317
  # Note that this method actually runs the queries, since the results of some
249
318
  # are needed by the next ones when eager loading is going on.
250
319
  #
320
+ # To run EXPLAIN on queries created by +first+, +pluck+ and +count+, call
321
+ # these methods on +explain+:
322
+ #
323
+ # User.all.explain.count
324
+ # # EXPLAIN SELECT COUNT(*) FROM `users`
325
+ # # ...
326
+ #
327
+ # The column name can be passed if required:
328
+ #
329
+ # User.all.explain.maximum(:id)
330
+ # # EXPLAIN SELECT MAX(`users`.`id`) FROM `users`
331
+ # # ...
332
+ #
251
333
  # Please see further details in the
252
334
  # {Active Record Query Interface guide}[https://guides.rubyonrails.org/active_record_querying.html#running-explain].
253
335
  def explain(*options)
254
- exec_explain(collecting_queries_for_explain { exec_queries }, options)
336
+ ExplainProxy.new(self, options)
255
337
  end
256
338
 
257
339
  # Converts relation objects to Array.
@@ -401,28 +483,30 @@ module ActiveRecord
401
483
  else
402
484
  collection = eager_loading? ? apply_join_dependency : self
403
485
 
404
- column = connection.visitor.compile(table[timestamp_column])
405
- select_values = "COUNT(*) AS #{connection.quote_column_name("size")}, MAX(%s) AS timestamp"
486
+ with_connection do |c|
487
+ column = c.visitor.compile(table[timestamp_column])
488
+ select_values = "COUNT(*) AS #{adapter_class.quote_column_name("size")}, MAX(%s) AS timestamp"
406
489
 
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
490
+ if collection.has_limit_or_offset?
491
+ query = collection.select("#{column} AS collection_cache_key_timestamp")
492
+ query._select!(table[Arel.star]) if distinct_value && collection.select_values.empty?
493
+ subquery_alias = "subquery_for_cache_key"
494
+ subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
495
+ arel = query.build_subquery(subquery_alias, select_values % subquery_column)
496
+ else
497
+ query = collection.unscope(:order)
498
+ query.select_values = [select_values % column]
499
+ arel = query.arel
500
+ end
418
501
 
419
- size, timestamp = connection.select_rows(arel, nil).first
502
+ size, timestamp = c.select_rows(arel, nil).first
420
503
 
421
- if size
422
- column_type = klass.type_for_attribute(timestamp_column)
423
- timestamp = column_type.deserialize(timestamp)
424
- else
425
- size = 0
504
+ if size
505
+ column_type = klass.type_for_attribute(timestamp_column)
506
+ timestamp = column_type.deserialize(timestamp)
507
+ else
508
+ size = 0
509
+ end
426
510
  end
427
511
  end
428
512
 
@@ -521,18 +605,20 @@ module ActiveRecord
521
605
  values = Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
522
606
  end
523
607
 
524
- arel = eager_loading? ? apply_join_dependency.arel : build_arel
525
- arel.source.left = table
608
+ klass.with_connection do |c|
609
+ arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
610
+ arel.source.left = table
526
611
 
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]
612
+ group_values_arel_columns = arel_columns(group_values.uniq)
613
+ having_clause_ast = having_clause.ast unless having_clause.empty?
614
+ key = if klass.composite_primary_key?
615
+ primary_key.map { |pk| table[pk] }
616
+ else
617
+ table[primary_key]
618
+ end
619
+ stmt = arel.compile_update(values, key, having_clause_ast, group_values_arel_columns)
620
+ c.update(stmt, "#{klass} Update All").tap { reset }
533
621
  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
622
  end
537
623
 
538
624
  def update(id = :all, attributes) # :nodoc:
@@ -551,6 +637,283 @@ module ActiveRecord
551
637
  end
552
638
  end
553
639
 
640
+
641
+ # Inserts a single record into the database in a single SQL INSERT
642
+ # statement. It does not instantiate any models nor does it trigger
643
+ # Active Record callbacks or validations. Though passed values
644
+ # go through Active Record's type casting and serialization.
645
+ #
646
+ # See #insert_all for documentation.
647
+ def insert(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
648
+ insert_all([ attributes ], returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
649
+ end
650
+
651
+ # Inserts multiple records into the database in a single SQL INSERT
652
+ # statement. It does not instantiate any models nor does it trigger
653
+ # Active Record callbacks or validations. Though passed values
654
+ # go through Active Record's type casting and serialization.
655
+ #
656
+ # The +attributes+ parameter is an Array of Hashes. Every Hash determines
657
+ # the attributes for a single row and must have the same keys.
658
+ #
659
+ # Rows are considered to be unique by every unique index on the table. Any
660
+ # duplicate rows are skipped.
661
+ # Override with <tt>:unique_by</tt> (see below).
662
+ #
663
+ # Returns an ActiveRecord::Result with its contents based on
664
+ # <tt>:returning</tt> (see below).
665
+ #
666
+ # ==== Options
667
+ #
668
+ # [:returning]
669
+ # (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
670
+ # inserted records, which by default is the primary key.
671
+ # Pass <tt>returning: %w[ id name ]</tt> for both id and name
672
+ # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
673
+ # clause entirely.
674
+ #
675
+ # You can also pass an SQL string if you need more control on the return values
676
+ # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
677
+ #
678
+ # [:unique_by]
679
+ # (PostgreSQL and SQLite only) By default rows are considered to be unique
680
+ # by every unique index on the table. Any duplicate rows are skipped.
681
+ #
682
+ # To skip rows according to just one unique index pass <tt>:unique_by</tt>.
683
+ #
684
+ # Consider a Book model where no duplicate ISBNs make sense, but if any
685
+ # row has an existing id, or is not unique by another unique index,
686
+ # ActiveRecord::RecordNotUnique is raised.
687
+ #
688
+ # Unique indexes can be identified by columns or name:
689
+ #
690
+ # unique_by: :isbn
691
+ # unique_by: %i[ author_id name ]
692
+ # unique_by: :index_books_on_isbn
693
+ #
694
+ # [:record_timestamps]
695
+ # By default, automatic setting of timestamp columns is controlled by
696
+ # the model's <tt>record_timestamps</tt> config, matching typical
697
+ # behavior.
698
+ #
699
+ # To override this and force automatic setting of timestamp columns one
700
+ # way or the other, pass <tt>:record_timestamps</tt>:
701
+ #
702
+ # record_timestamps: true # Always set timestamps automatically
703
+ # record_timestamps: false # Never set timestamps automatically
704
+ #
705
+ # Because it relies on the index information from the database
706
+ # <tt>:unique_by</tt> is recommended to be paired with
707
+ # Active Record's schema_cache.
708
+ #
709
+ # ==== Example
710
+ #
711
+ # # Insert records and skip inserting any duplicates.
712
+ # # Here "Eloquent Ruby" is skipped because its id is not unique.
713
+ #
714
+ # Book.insert_all([
715
+ # { id: 1, title: "Rework", author: "David" },
716
+ # { id: 1, title: "Eloquent Ruby", author: "Russ" }
717
+ # ])
718
+ #
719
+ # # insert_all works on chained scopes, and you can use create_with
720
+ # # to set default attributes for all inserted records.
721
+ #
722
+ # author.books.create_with(created_at: Time.now).insert_all([
723
+ # { id: 1, title: "Rework" },
724
+ # { id: 2, title: "Eloquent Ruby" }
725
+ # ])
726
+ def insert_all(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
727
+ InsertAll.execute(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
728
+ end
729
+
730
+ # Inserts a single record into the database in a single SQL INSERT
731
+ # statement. It does not instantiate any models nor does it trigger
732
+ # Active Record callbacks or validations. Though passed values
733
+ # go through Active Record's type casting and serialization.
734
+ #
735
+ # See #insert_all! for more.
736
+ def insert!(attributes, returning: nil, record_timestamps: nil)
737
+ insert_all!([ attributes ], returning: returning, record_timestamps: record_timestamps)
738
+ end
739
+
740
+ # Inserts multiple records into the database in a single SQL INSERT
741
+ # statement. It does not instantiate any models nor does it trigger
742
+ # Active Record callbacks or validations. Though passed values
743
+ # go through Active Record's type casting and serialization.
744
+ #
745
+ # The +attributes+ parameter is an Array of Hashes. Every Hash determines
746
+ # the attributes for a single row and must have the same keys.
747
+ #
748
+ # Raises ActiveRecord::RecordNotUnique if any rows violate a
749
+ # unique index on the table. In that case, no rows are inserted.
750
+ #
751
+ # To skip duplicate rows, see #insert_all. To replace them, see #upsert_all.
752
+ #
753
+ # Returns an ActiveRecord::Result with its contents based on
754
+ # <tt>:returning</tt> (see below).
755
+ #
756
+ # ==== Options
757
+ #
758
+ # [:returning]
759
+ # (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
760
+ # inserted records, which by default is the primary key.
761
+ # Pass <tt>returning: %w[ id name ]</tt> for both id and name
762
+ # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
763
+ # clause entirely.
764
+ #
765
+ # You can also pass an SQL string if you need more control on the return values
766
+ # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
767
+ #
768
+ # [:record_timestamps]
769
+ # By default, automatic setting of timestamp columns is controlled by
770
+ # the model's <tt>record_timestamps</tt> config, matching typical
771
+ # behavior.
772
+ #
773
+ # To override this and force automatic setting of timestamp columns one
774
+ # way or the other, pass <tt>:record_timestamps</tt>:
775
+ #
776
+ # record_timestamps: true # Always set timestamps automatically
777
+ # record_timestamps: false # Never set timestamps automatically
778
+ #
779
+ # ==== Examples
780
+ #
781
+ # # Insert multiple records
782
+ # Book.insert_all!([
783
+ # { title: "Rework", author: "David" },
784
+ # { title: "Eloquent Ruby", author: "Russ" }
785
+ # ])
786
+ #
787
+ # # Raises ActiveRecord::RecordNotUnique because "Eloquent Ruby"
788
+ # # does not have a unique id.
789
+ # Book.insert_all!([
790
+ # { id: 1, title: "Rework", author: "David" },
791
+ # { id: 1, title: "Eloquent Ruby", author: "Russ" }
792
+ # ])
793
+ def insert_all!(attributes, returning: nil, record_timestamps: nil)
794
+ InsertAll.execute(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps)
795
+ end
796
+
797
+ # Updates or inserts (upserts) a single record into the database in a
798
+ # single SQL INSERT statement. It does not instantiate any models nor does
799
+ # it trigger Active Record callbacks or validations. Though passed values
800
+ # go through Active Record's type casting and serialization.
801
+ #
802
+ # See #upsert_all for documentation.
803
+ def upsert(attributes, **kwargs)
804
+ upsert_all([ attributes ], **kwargs)
805
+ end
806
+
807
+ # Updates or inserts (upserts) multiple records into the database in a
808
+ # single SQL INSERT statement. It does not instantiate any models nor does
809
+ # it trigger Active Record callbacks or validations. Though passed values
810
+ # go through Active Record's type casting and serialization.
811
+ #
812
+ # The +attributes+ parameter is an Array of Hashes. Every Hash determines
813
+ # the attributes for a single row and must have the same keys.
814
+ #
815
+ # Returns an ActiveRecord::Result with its contents based on
816
+ # <tt>:returning</tt> (see below).
817
+ #
818
+ # By default, +upsert_all+ will update all the columns that can be updated when
819
+ # there is a conflict. These are all the columns except primary keys, read-only
820
+ # columns, and columns covered by the optional +unique_by+.
821
+ #
822
+ # ==== Options
823
+ #
824
+ # [:returning]
825
+ # (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
826
+ # upserted records, which by default is the primary key.
827
+ # Pass <tt>returning: %w[ id name ]</tt> for both id and name
828
+ # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
829
+ # clause entirely.
830
+ #
831
+ # You can also pass an SQL string if you need more control on the return values
832
+ # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
833
+ #
834
+ # [:unique_by]
835
+ # (PostgreSQL and SQLite only) By default rows are considered to be unique
836
+ # by every unique index on the table. Any duplicate rows are skipped.
837
+ #
838
+ # To skip rows according to just one unique index pass <tt>:unique_by</tt>.
839
+ #
840
+ # Consider a Book model where no duplicate ISBNs make sense, but if any
841
+ # row has an existing id, or is not unique by another unique index,
842
+ # ActiveRecord::RecordNotUnique is raised.
843
+ #
844
+ # Unique indexes can be identified by columns or name:
845
+ #
846
+ # unique_by: :isbn
847
+ # unique_by: %i[ author_id name ]
848
+ # unique_by: :index_books_on_isbn
849
+ #
850
+ # Because it relies on the index information from the database
851
+ # <tt>:unique_by</tt> is recommended to be paired with
852
+ # Active Record's schema_cache.
853
+ #
854
+ # [:on_duplicate]
855
+ # Configure the SQL update sentence that will be used in case of conflict.
856
+ #
857
+ # NOTE: If you use this option you must provide all the columns you want to update
858
+ # by yourself.
859
+ #
860
+ # Example:
861
+ #
862
+ # Commodity.upsert_all(
863
+ # [
864
+ # { id: 2, name: "Copper", price: 4.84 },
865
+ # { id: 4, name: "Gold", price: 1380.87 },
866
+ # { id: 6, name: "Aluminium", price: 0.35 }
867
+ # ],
868
+ # on_duplicate: Arel.sql("price = GREATEST(commodities.price, EXCLUDED.price)")
869
+ # )
870
+ #
871
+ # See the related +:update_only+ option. Both options can't be used at the same time.
872
+ #
873
+ # [:update_only]
874
+ # Provide a list of column names that will be updated in case of conflict. If not provided,
875
+ # +upsert_all+ will update all the columns that can be updated. These are all the columns
876
+ # except primary keys, read-only columns, and columns covered by the optional +unique_by+
877
+ #
878
+ # Example:
879
+ #
880
+ # Commodity.upsert_all(
881
+ # [
882
+ # { id: 2, name: "Copper", price: 4.84 },
883
+ # { id: 4, name: "Gold", price: 1380.87 },
884
+ # { id: 6, name: "Aluminium", price: 0.35 }
885
+ # ],
886
+ # update_only: [:price] # Only prices will be updated
887
+ # )
888
+ #
889
+ # See the related +:on_duplicate+ option. Both options can't be used at the same time.
890
+ #
891
+ # [:record_timestamps]
892
+ # By default, automatic setting of timestamp columns is controlled by
893
+ # the model's <tt>record_timestamps</tt> config, matching typical
894
+ # behavior.
895
+ #
896
+ # To override this and force automatic setting of timestamp columns one
897
+ # way or the other, pass <tt>:record_timestamps</tt>:
898
+ #
899
+ # record_timestamps: true # Always set timestamps automatically
900
+ # record_timestamps: false # Never set timestamps automatically
901
+ #
902
+ # ==== Examples
903
+ #
904
+ # # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
905
+ # # Here "Eloquent Ruby" overwrites "Rework" because its ISBN is duplicate.
906
+ #
907
+ # Book.upsert_all([
908
+ # { title: "Rework", author: "David", isbn: "1" },
909
+ # { title: "Eloquent Ruby", author: "Russ", isbn: "1" }
910
+ # ], unique_by: :isbn)
911
+ #
912
+ # Book.find_by(isbn: "1").title # => "Eloquent Ruby"
913
+ def upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
914
+ InsertAll.execute(self, attributes, on_duplicate: on_duplicate, update_only: update_only, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
915
+ end
916
+
554
917
  # Updates the counters of the records in the current relation.
555
918
  #
556
919
  # ==== Parameters
@@ -589,7 +952,7 @@ module ActiveRecord
589
952
  # If attribute names are passed, they are updated along with +updated_at+/+updated_on+ attributes.
590
953
  # If no time argument is passed, the current time is used as default.
591
954
  #
592
- # === Examples
955
+ # ==== Examples
593
956
  #
594
957
  # # Touch all records
595
958
  # Person.all.touch_all
@@ -659,19 +1022,79 @@ module ActiveRecord
659
1022
  raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
660
1023
  end
661
1024
 
662
- arel = eager_loading? ? apply_join_dependency.arel : build_arel
663
- arel.source.left = table
1025
+ klass.with_connection do |c|
1026
+ arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
1027
+ arel.source.left = table
1028
+
1029
+ group_values_arel_columns = arel_columns(group_values.uniq)
1030
+ having_clause_ast = having_clause.ast unless having_clause.empty?
1031
+ key = if klass.composite_primary_key?
1032
+ primary_key.map { |pk| table[pk] }
1033
+ else
1034
+ table[primary_key]
1035
+ end
1036
+ stmt = arel.compile_delete(key, having_clause_ast, group_values_arel_columns)
1037
+
1038
+ c.delete(stmt, "#{klass} Delete All").tap { reset }
1039
+ end
1040
+ end
1041
+
1042
+ # Deletes the row with a primary key matching the +id+ argument, using an
1043
+ # SQL +DELETE+ statement, and returns the number of rows deleted. Active
1044
+ # Record objects are not instantiated, so the object's callbacks are not
1045
+ # executed, including any <tt>:dependent</tt> association options.
1046
+ #
1047
+ # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
1048
+ #
1049
+ # Note: Although it is often much faster than the alternative, #destroy,
1050
+ # skipping callbacks might bypass business logic in your application
1051
+ # that ensures referential integrity or performs other essential jobs.
1052
+ #
1053
+ # ==== Examples
1054
+ #
1055
+ # # Delete a single row
1056
+ # Todo.delete(1)
1057
+ #
1058
+ # # Delete multiple rows
1059
+ # Todo.delete([2,3,4])
1060
+ def delete(id_or_array)
1061
+ return 0 if id_or_array.nil? || (id_or_array.is_a?(Array) && id_or_array.empty?)
1062
+
1063
+ where(model.primary_key => id_or_array).delete_all
1064
+ end
664
1065
 
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] }
1066
+
1067
+ # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
1068
+ # therefore all callbacks and filters are fired off before the object is deleted. This method is
1069
+ # less efficient than #delete but allows cleanup methods and other actions to be run.
1070
+ #
1071
+ # This essentially finds the object (or multiple objects) with the given id, creates a new object
1072
+ # from the attributes, and then calls destroy on it.
1073
+ #
1074
+ # ==== Parameters
1075
+ #
1076
+ # * +id+ - This should be the id or an array of ids to be destroyed.
1077
+ #
1078
+ # ==== Examples
1079
+ #
1080
+ # # Destroy a single object
1081
+ # Todo.destroy(1)
1082
+ #
1083
+ # # Destroy multiple objects
1084
+ # todos = [1,2,3]
1085
+ # Todo.destroy(todos)
1086
+ def destroy(id)
1087
+ multiple_ids = if model.composite_primary_key?
1088
+ id.first.is_a?(Array)
669
1089
  else
670
- table[primary_key]
1090
+ id.is_a?(Array)
671
1091
  end
672
- stmt = arel.compile_delete(key, having_clause_ast, group_values_arel_columns)
673
1092
 
674
- klass.connection.delete(stmt, "#{klass} Delete All").tap { reset }
1093
+ if multiple_ids
1094
+ find(id).each(&:destroy)
1095
+ else
1096
+ find(id).destroy
1097
+ end
675
1098
  end
676
1099
 
677
1100
  # Finds and destroys all records matching the specified conditions.
@@ -719,17 +1142,19 @@ module ActiveRecord
719
1142
  #
720
1143
  # ASYNC Post Load (0.0ms) (db time 2ms) SELECT "posts".* FROM "posts" LIMIT 100
721
1144
  def load_async
722
- return load if !connection.async_enabled?
1145
+ with_connection do |c|
1146
+ return load if !c.async_enabled?
723
1147
 
724
- unless loaded?
725
- result = exec_main_query(async: connection.current_transaction.closed?)
1148
+ unless loaded?
1149
+ result = exec_main_query(async: c.current_transaction.closed?)
726
1150
 
727
- if result.is_a?(Array)
728
- @records = result
729
- else
730
- @future_result = result
1151
+ if result.is_a?(Array)
1152
+ @records = result
1153
+ else
1154
+ @future_result = result
1155
+ end
1156
+ @loaded = true
731
1157
  end
732
- @loaded = true
733
1158
  end
734
1159
 
735
1160
  self
@@ -785,8 +1210,9 @@ module ActiveRecord
785
1210
  relation.to_sql
786
1211
  end
787
1212
  else
788
- conn = klass.connection
789
- conn.unprepared_statement { conn.to_sql(arel) }
1213
+ klass.with_connection do |conn|
1214
+ conn.unprepared_statement { conn.to_sql(arel) }
1215
+ end
790
1216
  end
791
1217
  end
792
1218
 
@@ -871,7 +1297,7 @@ module ActiveRecord
871
1297
  end
872
1298
 
873
1299
  def alias_tracker(joins = [], aliases = nil) # :nodoc:
874
- ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins, aliases)
1300
+ ActiveRecord::Associations::AliasTracker.create(connection_pool, table.name, joins, aliases)
875
1301
  end
876
1302
 
877
1303
  class StrictLoadingScope # :nodoc:
@@ -947,7 +1373,11 @@ module ActiveRecord
947
1373
  def _substitute_values(values)
948
1374
  values.map do |name, value|
949
1375
  attr = table[name]
950
- unless Arel.arel_node?(value)
1376
+ if Arel.arel_node?(value)
1377
+ if value.is_a?(Arel::Nodes::SqlLiteral)
1378
+ value = Arel::Nodes::Grouping.new(value)
1379
+ end
1380
+ else
951
1381
  type = klass.type_for_attribute(attr.name)
952
1382
  value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
953
1383
  end
@@ -995,17 +1425,21 @@ module ActiveRecord
995
1425
  if where_clause.contradiction?
996
1426
  [].freeze
997
1427
  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)
1428
+ klass.with_connection do |c|
1429
+ apply_join_dependency do |relation, join_dependency|
1430
+ if relation.null_relation?
1431
+ [].freeze
1432
+ else
1433
+ relation = join_dependency.apply_column_aliases(relation)
1434
+ @_join_dependency = join_dependency
1435
+ c.select_all(relation.arel, "SQL", async: async)
1436
+ end
1005
1437
  end
1006
1438
  end
1007
1439
  else
1008
- klass._query_by_sql(arel, async: async)
1440
+ klass.with_connection do |c|
1441
+ klass._query_by_sql(c, arel, async: async)
1442
+ end
1009
1443
  end
1010
1444
  end
1011
1445
  end