activerecord 7.1.3.4 → 7.2.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 (196) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +652 -2032
  3. data/README.rdoc +15 -15
  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 +25 -19
  7. data/lib/active_record/associations/association.rb +15 -8
  8. data/lib/active_record/associations/belongs_to_association.rb +18 -11
  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 +11 -5
  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 +3 -3
  18. data/lib/active_record/associations/has_many_through_association.rb +7 -1
  19. data/lib/active_record/associations/has_one_association.rb +2 -2
  20. data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
  21. data/lib/active_record/associations/join_dependency.rb +10 -12
  22. data/lib/active_record/associations/nested_error.rb +47 -0
  23. data/lib/active_record/associations/preloader/association.rb +2 -1
  24. data/lib/active_record/associations/preloader/branch.rb +7 -1
  25. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  26. data/lib/active_record/associations/singular_association.rb +6 -0
  27. data/lib/active_record/associations/through_association.rb +1 -1
  28. data/lib/active_record/associations.rb +62 -289
  29. data/lib/active_record/attribute_assignment.rb +0 -2
  30. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  31. data/lib/active_record/attribute_methods/dirty.rb +2 -2
  32. data/lib/active_record/attribute_methods/primary_key.rb +23 -55
  33. data/lib/active_record/attribute_methods/read.rb +4 -16
  34. data/lib/active_record/attribute_methods/serialization.rb +4 -24
  35. data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
  36. data/lib/active_record/attribute_methods/write.rb +3 -3
  37. data/lib/active_record/attribute_methods.rb +89 -58
  38. data/lib/active_record/attributes.rb +61 -47
  39. data/lib/active_record/autosave_association.rb +17 -31
  40. data/lib/active_record/base.rb +2 -3
  41. data/lib/active_record/callbacks.rb +1 -1
  42. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +24 -107
  43. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
  44. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +270 -58
  45. data/lib/active_record/connection_adapters/abstract/database_statements.rb +35 -18
  46. data/lib/active_record/connection_adapters/abstract/query_cache.rb +190 -75
  47. data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
  48. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
  49. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +23 -10
  50. data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -62
  51. data/lib/active_record/connection_adapters/abstract_adapter.rb +38 -59
  52. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +73 -19
  53. data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
  54. data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
  55. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +8 -1
  56. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +16 -15
  57. data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -32
  58. data/lib/active_record/connection_adapters/pool_config.rb +7 -6
  59. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
  60. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  61. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  62. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  63. data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
  64. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -0
  65. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +18 -12
  66. data/lib/active_record/connection_adapters/postgresql_adapter.rb +29 -24
  67. data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
  68. data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
  69. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
  70. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
  71. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  72. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
  73. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  74. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
  75. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +127 -77
  76. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +15 -15
  77. data/lib/active_record/connection_adapters/trilogy_adapter.rb +32 -65
  78. data/lib/active_record/connection_adapters.rb +121 -0
  79. data/lib/active_record/connection_handling.rb +56 -41
  80. data/lib/active_record/core.rb +93 -40
  81. data/lib/active_record/counter_cache.rb +23 -10
  82. data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
  83. data/lib/active_record/database_configurations/database_config.rb +19 -4
  84. data/lib/active_record/database_configurations/hash_config.rb +44 -36
  85. data/lib/active_record/database_configurations/url_config.rb +20 -1
  86. data/lib/active_record/database_configurations.rb +1 -1
  87. data/lib/active_record/delegated_type.rb +30 -6
  88. data/lib/active_record/destroy_association_async_job.rb +1 -1
  89. data/lib/active_record/dynamic_matchers.rb +2 -2
  90. data/lib/active_record/encryption/encryptable_record.rb +3 -3
  91. data/lib/active_record/encryption/encrypted_attribute_type.rb +26 -6
  92. data/lib/active_record/encryption/encryptor.rb +18 -3
  93. data/lib/active_record/encryption/key_provider.rb +1 -1
  94. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  95. data/lib/active_record/encryption/message_serializer.rb +4 -0
  96. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  97. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  98. data/lib/active_record/encryption/scheme.rb +8 -4
  99. data/lib/active_record/encryption.rb +2 -0
  100. data/lib/active_record/enum.rb +19 -2
  101. data/lib/active_record/errors.rb +46 -20
  102. data/lib/active_record/explain.rb +13 -24
  103. data/lib/active_record/fixtures.rb +37 -31
  104. data/lib/active_record/future_result.rb +17 -4
  105. data/lib/active_record/gem_version.rb +3 -3
  106. data/lib/active_record/inheritance.rb +4 -2
  107. data/lib/active_record/insert_all.rb +18 -15
  108. data/lib/active_record/integration.rb +4 -1
  109. data/lib/active_record/internal_metadata.rb +48 -34
  110. data/lib/active_record/locking/optimistic.rb +8 -7
  111. data/lib/active_record/log_subscriber.rb +0 -21
  112. data/lib/active_record/marshalling.rb +4 -1
  113. data/lib/active_record/message_pack.rb +2 -2
  114. data/lib/active_record/migration/command_recorder.rb +2 -3
  115. data/lib/active_record/migration/compatibility.rb +11 -3
  116. data/lib/active_record/migration/default_strategy.rb +4 -5
  117. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  118. data/lib/active_record/migration.rb +85 -76
  119. data/lib/active_record/model_schema.rb +38 -70
  120. data/lib/active_record/nested_attributes.rb +24 -5
  121. data/lib/active_record/normalization.rb +3 -7
  122. data/lib/active_record/persistence.rb +32 -354
  123. data/lib/active_record/query_cache.rb +19 -8
  124. data/lib/active_record/query_logs.rb +15 -0
  125. data/lib/active_record/query_logs_formatter.rb +1 -1
  126. data/lib/active_record/querying.rb +21 -9
  127. data/lib/active_record/railtie.rb +50 -68
  128. data/lib/active_record/railties/controller_runtime.rb +13 -4
  129. data/lib/active_record/railties/databases.rake +42 -45
  130. data/lib/active_record/reflection.rb +106 -38
  131. data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
  132. data/lib/active_record/relation/batches.rb +14 -8
  133. data/lib/active_record/relation/calculations.rb +96 -63
  134. data/lib/active_record/relation/delegation.rb +8 -11
  135. data/lib/active_record/relation/finder_methods.rb +16 -2
  136. data/lib/active_record/relation/merger.rb +4 -6
  137. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  138. data/lib/active_record/relation/predicate_builder/association_query_value.rb +9 -3
  139. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +6 -1
  140. data/lib/active_record/relation/predicate_builder.rb +3 -3
  141. data/lib/active_record/relation/query_methods.rb +245 -65
  142. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  143. data/lib/active_record/relation/spawn_methods.rb +2 -18
  144. data/lib/active_record/relation/where_clause.rb +7 -19
  145. data/lib/active_record/relation.rb +500 -66
  146. data/lib/active_record/result.rb +32 -45
  147. data/lib/active_record/runtime_registry.rb +39 -0
  148. data/lib/active_record/sanitization.rb +24 -19
  149. data/lib/active_record/schema.rb +8 -6
  150. data/lib/active_record/schema_dumper.rb +19 -9
  151. data/lib/active_record/schema_migration.rb +30 -14
  152. data/lib/active_record/scoping/named.rb +1 -0
  153. data/lib/active_record/signed_id.rb +20 -1
  154. data/lib/active_record/statement_cache.rb +7 -7
  155. data/lib/active_record/table_metadata.rb +1 -10
  156. data/lib/active_record/tasks/database_tasks.rb +98 -48
  157. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  158. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  159. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
  160. data/lib/active_record/test_fixtures.rb +87 -89
  161. data/lib/active_record/testing/query_assertions.rb +121 -0
  162. data/lib/active_record/timestamp.rb +5 -3
  163. data/lib/active_record/token_for.rb +22 -12
  164. data/lib/active_record/touch_later.rb +1 -1
  165. data/lib/active_record/transaction.rb +132 -0
  166. data/lib/active_record/transactions.rb +70 -14
  167. data/lib/active_record/translation.rb +0 -2
  168. data/lib/active_record/type/serialized.rb +1 -3
  169. data/lib/active_record/type_caster/connection.rb +4 -4
  170. data/lib/active_record/validations/associated.rb +9 -3
  171. data/lib/active_record/validations/uniqueness.rb +15 -10
  172. data/lib/active_record/validations.rb +4 -1
  173. data/lib/active_record.rb +150 -41
  174. data/lib/arel/alias_predication.rb +1 -1
  175. data/lib/arel/collectors/bind.rb +2 -0
  176. data/lib/arel/collectors/composite.rb +7 -0
  177. data/lib/arel/collectors/sql_string.rb +1 -1
  178. data/lib/arel/collectors/substitute_binds.rb +1 -1
  179. data/lib/arel/nodes/binary.rb +0 -6
  180. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  181. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  182. data/lib/arel/nodes/node.rb +4 -3
  183. data/lib/arel/nodes/sql_literal.rb +7 -0
  184. data/lib/arel/nodes.rb +2 -2
  185. data/lib/arel/predications.rb +1 -1
  186. data/lib/arel/select_manager.rb +1 -1
  187. data/lib/arel/tree_manager.rb +8 -3
  188. data/lib/arel/update_manager.rb +2 -1
  189. data/lib/arel/visitors/dot.rb +1 -0
  190. data/lib/arel/visitors/mysql.rb +9 -4
  191. data/lib/arel/visitors/postgresql.rb +1 -12
  192. data/lib/arel/visitors/sqlite.rb +25 -0
  193. data/lib/arel/visitors/to_sql.rb +31 -17
  194. data/lib/arel.rb +7 -3
  195. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  196. metadata +21 -15
@@ -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,22 @@ 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
+ transaction(requires_new: true) { create(attributes, &block) }
269
+ rescue ActiveRecord::RecordNotUnique
270
+ if connection.transaction_open?
271
+ where(attributes).lock.find_by!(attributes)
272
+ else
273
+ find_by!(attributes)
274
+ end
222
275
  end
223
276
  end
224
277
 
@@ -226,12 +279,14 @@ module ActiveRecord
226
279
  # {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
227
280
  # is raised if the created record is invalid.
228
281
  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)
282
+ with_connection do |connection|
283
+ transaction(requires_new: true) { create!(attributes, &block) }
284
+ rescue ActiveRecord::RecordNotUnique
285
+ if connection.transaction_open?
286
+ where(attributes).lock.find_by!(attributes)
287
+ else
288
+ find_by!(attributes)
289
+ end
235
290
  end
236
291
  end
237
292
 
@@ -245,13 +300,30 @@ module ActiveRecord
245
300
  # returns the result as a string. The string is formatted imitating the
246
301
  # ones printed by the database shell.
247
302
  #
303
+ # User.all.explain
304
+ # # EXPLAIN SELECT `users`.* FROM `users`
305
+ # # ...
306
+ #
248
307
  # Note that this method actually runs the queries, since the results of some
249
308
  # are needed by the next ones when eager loading is going on.
250
309
  #
310
+ # To run EXPLAIN on queries created by +first+, +pluck+ and +count+, call
311
+ # these methods on +explain+:
312
+ #
313
+ # User.all.explain.count
314
+ # # EXPLAIN SELECT COUNT(*) FROM `users`
315
+ # # ...
316
+ #
317
+ # The column name can be passed if required:
318
+ #
319
+ # User.all.explain.maximum(:id)
320
+ # # EXPLAIN SELECT MAX(`users`.`id`) FROM `users`
321
+ # # ...
322
+ #
251
323
  # Please see further details in the
252
324
  # {Active Record Query Interface guide}[https://guides.rubyonrails.org/active_record_querying.html#running-explain].
253
325
  def explain(*options)
254
- exec_explain(collecting_queries_for_explain { exec_queries }, options)
326
+ ExplainProxy.new(self, options)
255
327
  end
256
328
 
257
329
  # Converts relation objects to Array.
@@ -401,28 +473,30 @@ module ActiveRecord
401
473
  else
402
474
  collection = eager_loading? ? apply_join_dependency : self
403
475
 
404
- column = connection.visitor.compile(table[timestamp_column])
405
- select_values = "COUNT(*) AS #{connection.quote_column_name("size")}, MAX(%s) AS timestamp"
476
+ with_connection do |c|
477
+ column = c.visitor.compile(table[timestamp_column])
478
+ select_values = "COUNT(*) AS #{adapter_class.quote_column_name("size")}, MAX(%s) AS timestamp"
406
479
 
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
480
+ if collection.has_limit_or_offset?
481
+ query = collection.select("#{column} AS collection_cache_key_timestamp")
482
+ query._select!(table[Arel.star]) if distinct_value && collection.select_values.empty?
483
+ subquery_alias = "subquery_for_cache_key"
484
+ subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
485
+ arel = query.build_subquery(subquery_alias, select_values % subquery_column)
486
+ else
487
+ query = collection.unscope(:order)
488
+ query.select_values = [select_values % column]
489
+ arel = query.arel
490
+ end
418
491
 
419
- size, timestamp = connection.select_rows(arel, nil).first
492
+ size, timestamp = c.select_rows(arel, nil).first
420
493
 
421
- if size
422
- column_type = klass.type_for_attribute(timestamp_column)
423
- timestamp = column_type.deserialize(timestamp)
424
- else
425
- size = 0
494
+ if size
495
+ column_type = klass.type_for_attribute(timestamp_column)
496
+ timestamp = column_type.deserialize(timestamp)
497
+ else
498
+ size = 0
499
+ end
426
500
  end
427
501
  end
428
502
 
@@ -521,13 +595,20 @@ module ActiveRecord
521
595
  values = Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
522
596
  end
523
597
 
524
- arel = eager_loading? ? apply_join_dependency.arel : build_arel
525
- arel.source.left = table
598
+ klass.with_connection do |c|
599
+ arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
600
+ arel.source.left = table
526
601
 
527
- group_values_arel_columns = arel_columns(group_values.uniq)
528
- having_clause_ast = having_clause.ast unless having_clause.empty?
529
- stmt = arel.compile_update(values, table[primary_key], having_clause_ast, group_values_arel_columns)
530
- klass.connection.update(stmt, "#{klass} Update All").tap { reset }
602
+ group_values_arel_columns = arel_columns(group_values.uniq)
603
+ having_clause_ast = having_clause.ast unless having_clause.empty?
604
+ key = if klass.composite_primary_key?
605
+ primary_key.map { |pk| table[pk] }
606
+ else
607
+ table[primary_key]
608
+ end
609
+ stmt = arel.compile_update(values, key, having_clause_ast, group_values_arel_columns)
610
+ c.update(stmt, "#{klass} Update All").tap { reset }
611
+ end
531
612
  end
532
613
 
533
614
  def update(id = :all, attributes) # :nodoc:
@@ -546,6 +627,283 @@ module ActiveRecord
546
627
  end
547
628
  end
548
629
 
630
+
631
+ # Inserts a single record into the database in a single SQL INSERT
632
+ # statement. It does not instantiate any models nor does it trigger
633
+ # Active Record callbacks or validations. Though passed values
634
+ # go through Active Record's type casting and serialization.
635
+ #
636
+ # See #insert_all for documentation.
637
+ def insert(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
638
+ insert_all([ attributes ], returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
639
+ end
640
+
641
+ # Inserts multiple records 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
+ # The +attributes+ parameter is an Array of Hashes. Every Hash determines
647
+ # the attributes for a single row and must have the same keys.
648
+ #
649
+ # Rows are considered to be unique by every unique index on the table. Any
650
+ # duplicate rows are skipped.
651
+ # Override with <tt>:unique_by</tt> (see below).
652
+ #
653
+ # Returns an ActiveRecord::Result with its contents based on
654
+ # <tt>:returning</tt> (see below).
655
+ #
656
+ # ==== Options
657
+ #
658
+ # [:returning]
659
+ # (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
660
+ # inserted records, which by default is the primary key.
661
+ # Pass <tt>returning: %w[ id name ]</tt> for both id and name
662
+ # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
663
+ # clause entirely.
664
+ #
665
+ # You can also pass an SQL string if you need more control on the return values
666
+ # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
667
+ #
668
+ # [:unique_by]
669
+ # (PostgreSQL and SQLite only) By default rows are considered to be unique
670
+ # by every unique index on the table. Any duplicate rows are skipped.
671
+ #
672
+ # To skip rows according to just one unique index pass <tt>:unique_by</tt>.
673
+ #
674
+ # Consider a Book model where no duplicate ISBNs make sense, but if any
675
+ # row has an existing id, or is not unique by another unique index,
676
+ # ActiveRecord::RecordNotUnique is raised.
677
+ #
678
+ # Unique indexes can be identified by columns or name:
679
+ #
680
+ # unique_by: :isbn
681
+ # unique_by: %i[ author_id name ]
682
+ # unique_by: :index_books_on_isbn
683
+ #
684
+ # [:record_timestamps]
685
+ # By default, automatic setting of timestamp columns is controlled by
686
+ # the model's <tt>record_timestamps</tt> config, matching typical
687
+ # behavior.
688
+ #
689
+ # To override this and force automatic setting of timestamp columns one
690
+ # way or the other, pass <tt>:record_timestamps</tt>:
691
+ #
692
+ # record_timestamps: true # Always set timestamps automatically
693
+ # record_timestamps: false # Never set timestamps automatically
694
+ #
695
+ # Because it relies on the index information from the database
696
+ # <tt>:unique_by</tt> is recommended to be paired with
697
+ # Active Record's schema_cache.
698
+ #
699
+ # ==== Example
700
+ #
701
+ # # Insert records and skip inserting any duplicates.
702
+ # # Here "Eloquent Ruby" is skipped because its id is not unique.
703
+ #
704
+ # Book.insert_all([
705
+ # { id: 1, title: "Rework", author: "David" },
706
+ # { id: 1, title: "Eloquent Ruby", author: "Russ" }
707
+ # ])
708
+ #
709
+ # # insert_all works on chained scopes, and you can use create_with
710
+ # # to set default attributes for all inserted records.
711
+ #
712
+ # author.books.create_with(created_at: Time.now).insert_all([
713
+ # { id: 1, title: "Rework" },
714
+ # { id: 2, title: "Eloquent Ruby" }
715
+ # ])
716
+ def insert_all(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
717
+ InsertAll.execute(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
718
+ end
719
+
720
+ # Inserts a single record into the database in a single SQL INSERT
721
+ # statement. It does not instantiate any models nor does it trigger
722
+ # Active Record callbacks or validations. Though passed values
723
+ # go through Active Record's type casting and serialization.
724
+ #
725
+ # See #insert_all! for more.
726
+ def insert!(attributes, returning: nil, record_timestamps: nil)
727
+ insert_all!([ attributes ], returning: returning, record_timestamps: record_timestamps)
728
+ end
729
+
730
+ # Inserts multiple records 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
+ # The +attributes+ parameter is an Array of Hashes. Every Hash determines
736
+ # the attributes for a single row and must have the same keys.
737
+ #
738
+ # Raises ActiveRecord::RecordNotUnique if any rows violate a
739
+ # unique index on the table. In that case, no rows are inserted.
740
+ #
741
+ # To skip duplicate rows, see #insert_all. To replace them, see #upsert_all.
742
+ #
743
+ # Returns an ActiveRecord::Result with its contents based on
744
+ # <tt>:returning</tt> (see below).
745
+ #
746
+ # ==== Options
747
+ #
748
+ # [:returning]
749
+ # (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
750
+ # inserted records, which by default is the primary key.
751
+ # Pass <tt>returning: %w[ id name ]</tt> for both id and name
752
+ # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
753
+ # clause entirely.
754
+ #
755
+ # You can also pass an SQL string if you need more control on the return values
756
+ # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
757
+ #
758
+ # [:record_timestamps]
759
+ # By default, automatic setting of timestamp columns is controlled by
760
+ # the model's <tt>record_timestamps</tt> config, matching typical
761
+ # behavior.
762
+ #
763
+ # To override this and force automatic setting of timestamp columns one
764
+ # way or the other, pass <tt>:record_timestamps</tt>:
765
+ #
766
+ # record_timestamps: true # Always set timestamps automatically
767
+ # record_timestamps: false # Never set timestamps automatically
768
+ #
769
+ # ==== Examples
770
+ #
771
+ # # Insert multiple records
772
+ # Book.insert_all!([
773
+ # { title: "Rework", author: "David" },
774
+ # { title: "Eloquent Ruby", author: "Russ" }
775
+ # ])
776
+ #
777
+ # # Raises ActiveRecord::RecordNotUnique because "Eloquent Ruby"
778
+ # # does not have a unique id.
779
+ # Book.insert_all!([
780
+ # { id: 1, title: "Rework", author: "David" },
781
+ # { id: 1, title: "Eloquent Ruby", author: "Russ" }
782
+ # ])
783
+ def insert_all!(attributes, returning: nil, record_timestamps: nil)
784
+ InsertAll.execute(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps)
785
+ end
786
+
787
+ # Updates or inserts (upserts) a single record into the database in a
788
+ # single SQL INSERT statement. It does not instantiate any models nor does
789
+ # it trigger Active Record callbacks or validations. Though passed values
790
+ # go through Active Record's type casting and serialization.
791
+ #
792
+ # See #upsert_all for documentation.
793
+ def upsert(attributes, **kwargs)
794
+ upsert_all([ attributes ], **kwargs)
795
+ end
796
+
797
+ # Updates or inserts (upserts) multiple records 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
+ # The +attributes+ parameter is an Array of Hashes. Every Hash determines
803
+ # the attributes for a single row and must have the same keys.
804
+ #
805
+ # Returns an ActiveRecord::Result with its contents based on
806
+ # <tt>:returning</tt> (see below).
807
+ #
808
+ # By default, +upsert_all+ will update all the columns that can be updated when
809
+ # there is a conflict. These are all the columns except primary keys, read-only
810
+ # columns, and columns covered by the optional +unique_by+.
811
+ #
812
+ # ==== Options
813
+ #
814
+ # [:returning]
815
+ # (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
816
+ # inserted records, which by default is the primary key.
817
+ # Pass <tt>returning: %w[ id name ]</tt> for both id and name
818
+ # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
819
+ # clause entirely.
820
+ #
821
+ # You can also pass an SQL string if you need more control on the return values
822
+ # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
823
+ #
824
+ # [:unique_by]
825
+ # (PostgreSQL and SQLite only) By default rows are considered to be unique
826
+ # by every unique index on the table. Any duplicate rows are skipped.
827
+ #
828
+ # To skip rows according to just one unique index pass <tt>:unique_by</tt>.
829
+ #
830
+ # Consider a Book model where no duplicate ISBNs make sense, but if any
831
+ # row has an existing id, or is not unique by another unique index,
832
+ # ActiveRecord::RecordNotUnique is raised.
833
+ #
834
+ # Unique indexes can be identified by columns or name:
835
+ #
836
+ # unique_by: :isbn
837
+ # unique_by: %i[ author_id name ]
838
+ # unique_by: :index_books_on_isbn
839
+ #
840
+ # Because it relies on the index information from the database
841
+ # <tt>:unique_by</tt> is recommended to be paired with
842
+ # Active Record's schema_cache.
843
+ #
844
+ # [:on_duplicate]
845
+ # Configure the SQL update sentence that will be used in case of conflict.
846
+ #
847
+ # NOTE: If you use this option you must provide all the columns you want to update
848
+ # by yourself.
849
+ #
850
+ # Example:
851
+ #
852
+ # Commodity.upsert_all(
853
+ # [
854
+ # { id: 2, name: "Copper", price: 4.84 },
855
+ # { id: 4, name: "Gold", price: 1380.87 },
856
+ # { id: 6, name: "Aluminium", price: 0.35 }
857
+ # ],
858
+ # on_duplicate: Arel.sql("price = GREATEST(commodities.price, EXCLUDED.price)")
859
+ # )
860
+ #
861
+ # See the related +:update_only+ option. Both options can't be used at the same time.
862
+ #
863
+ # [:update_only]
864
+ # Provide a list of column names that will be updated in case of conflict. If not provided,
865
+ # +upsert_all+ will update all the columns that can be updated. These are all the columns
866
+ # except primary keys, read-only columns, and columns covered by the optional +unique_by+
867
+ #
868
+ # Example:
869
+ #
870
+ # Commodity.upsert_all(
871
+ # [
872
+ # { id: 2, name: "Copper", price: 4.84 },
873
+ # { id: 4, name: "Gold", price: 1380.87 },
874
+ # { id: 6, name: "Aluminium", price: 0.35 }
875
+ # ],
876
+ # update_only: [:price] # Only prices will be updated
877
+ # )
878
+ #
879
+ # See the related +:on_duplicate+ option. Both options can't be used at the same time.
880
+ #
881
+ # [:record_timestamps]
882
+ # By default, automatic setting of timestamp columns is controlled by
883
+ # the model's <tt>record_timestamps</tt> config, matching typical
884
+ # behavior.
885
+ #
886
+ # To override this and force automatic setting of timestamp columns one
887
+ # way or the other, pass <tt>:record_timestamps</tt>:
888
+ #
889
+ # record_timestamps: true # Always set timestamps automatically
890
+ # record_timestamps: false # Never set timestamps automatically
891
+ #
892
+ # ==== Examples
893
+ #
894
+ # # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
895
+ # # Here "Eloquent Ruby" overwrites "Rework" because its ISBN is duplicate.
896
+ #
897
+ # Book.upsert_all([
898
+ # { title: "Rework", author: "David", isbn: "1" },
899
+ # { title: "Eloquent Ruby", author: "Russ", isbn: "1" }
900
+ # ], unique_by: :isbn)
901
+ #
902
+ # Book.find_by(isbn: "1").title # => "Eloquent Ruby"
903
+ def upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
904
+ InsertAll.execute(self, attributes, on_duplicate: on_duplicate, update_only: update_only, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
905
+ end
906
+
549
907
  # Updates the counters of the records in the current relation.
550
908
  #
551
909
  # ==== Parameters
@@ -654,14 +1012,79 @@ module ActiveRecord
654
1012
  raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
655
1013
  end
656
1014
 
657
- arel = eager_loading? ? apply_join_dependency.arel : build_arel
658
- arel.source.left = table
1015
+ klass.with_connection do |c|
1016
+ arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
1017
+ arel.source.left = table
1018
+
1019
+ group_values_arel_columns = arel_columns(group_values.uniq)
1020
+ having_clause_ast = having_clause.ast unless having_clause.empty?
1021
+ key = if klass.composite_primary_key?
1022
+ primary_key.map { |pk| table[pk] }
1023
+ else
1024
+ table[primary_key]
1025
+ end
1026
+ stmt = arel.compile_delete(key, having_clause_ast, group_values_arel_columns)
1027
+
1028
+ c.delete(stmt, "#{klass} Delete All").tap { reset }
1029
+ end
1030
+ end
659
1031
 
660
- group_values_arel_columns = arel_columns(group_values.uniq)
661
- having_clause_ast = having_clause.ast unless having_clause.empty?
662
- stmt = arel.compile_delete(table[primary_key], having_clause_ast, group_values_arel_columns)
1032
+ # Deletes the row with a primary key matching the +id+ argument, using an
1033
+ # SQL +DELETE+ statement, and returns the number of rows deleted. Active
1034
+ # Record objects are not instantiated, so the object's callbacks are not
1035
+ # executed, including any <tt>:dependent</tt> association options.
1036
+ #
1037
+ # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
1038
+ #
1039
+ # Note: Although it is often much faster than the alternative, #destroy,
1040
+ # skipping callbacks might bypass business logic in your application
1041
+ # that ensures referential integrity or performs other essential jobs.
1042
+ #
1043
+ # ==== Examples
1044
+ #
1045
+ # # Delete a single row
1046
+ # Todo.delete(1)
1047
+ #
1048
+ # # Delete multiple rows
1049
+ # Todo.delete([2,3,4])
1050
+ def delete(id_or_array)
1051
+ return 0 if id_or_array.nil? || (id_or_array.is_a?(Array) && id_or_array.empty?)
663
1052
 
664
- klass.connection.delete(stmt, "#{klass} Delete All").tap { reset }
1053
+ where(model.primary_key => id_or_array).delete_all
1054
+ end
1055
+
1056
+
1057
+ # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
1058
+ # therefore all callbacks and filters are fired off before the object is deleted. This method is
1059
+ # less efficient than #delete but allows cleanup methods and other actions to be run.
1060
+ #
1061
+ # This essentially finds the object (or multiple objects) with the given id, creates a new object
1062
+ # from the attributes, and then calls destroy on it.
1063
+ #
1064
+ # ==== Parameters
1065
+ #
1066
+ # * +id+ - This should be the id or an array of ids to be destroyed.
1067
+ #
1068
+ # ==== Examples
1069
+ #
1070
+ # # Destroy a single object
1071
+ # Todo.destroy(1)
1072
+ #
1073
+ # # Destroy multiple objects
1074
+ # todos = [1,2,3]
1075
+ # Todo.destroy(todos)
1076
+ def destroy(id)
1077
+ multiple_ids = if model.composite_primary_key?
1078
+ id.first.is_a?(Array)
1079
+ else
1080
+ id.is_a?(Array)
1081
+ end
1082
+
1083
+ if multiple_ids
1084
+ find(id).each(&:destroy)
1085
+ else
1086
+ find(id).destroy
1087
+ end
665
1088
  end
666
1089
 
667
1090
  # Finds and destroys all records matching the specified conditions.
@@ -709,17 +1132,19 @@ module ActiveRecord
709
1132
  #
710
1133
  # ASYNC Post Load (0.0ms) (db time 2ms) SELECT "posts".* FROM "posts" LIMIT 100
711
1134
  def load_async
712
- return load if !connection.async_enabled?
1135
+ with_connection do |c|
1136
+ return load if !c.async_enabled?
713
1137
 
714
- unless loaded?
715
- result = exec_main_query(async: connection.current_transaction.closed?)
1138
+ unless loaded?
1139
+ result = exec_main_query(async: c.current_transaction.closed?)
716
1140
 
717
- if result.is_a?(Array)
718
- @records = result
719
- else
720
- @future_result = result
1141
+ if result.is_a?(Array)
1142
+ @records = result
1143
+ else
1144
+ @future_result = result
1145
+ end
1146
+ @loaded = true
721
1147
  end
722
- @loaded = true
723
1148
  end
724
1149
 
725
1150
  self
@@ -775,8 +1200,9 @@ module ActiveRecord
775
1200
  relation.to_sql
776
1201
  end
777
1202
  else
778
- conn = klass.connection
779
- conn.unprepared_statement { conn.to_sql(arel) }
1203
+ klass.with_connection do |conn|
1204
+ conn.unprepared_statement { conn.to_sql(arel) }
1205
+ end
780
1206
  end
781
1207
  end
782
1208
 
@@ -861,7 +1287,7 @@ module ActiveRecord
861
1287
  end
862
1288
 
863
1289
  def alias_tracker(joins = [], aliases = nil) # :nodoc:
864
- ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins, aliases)
1290
+ ActiveRecord::Associations::AliasTracker.create(connection_pool, table.name, joins, aliases)
865
1291
  end
866
1292
 
867
1293
  class StrictLoadingScope # :nodoc:
@@ -937,7 +1363,11 @@ module ActiveRecord
937
1363
  def _substitute_values(values)
938
1364
  values.map do |name, value|
939
1365
  attr = table[name]
940
- unless Arel.arel_node?(value)
1366
+ if Arel.arel_node?(value)
1367
+ if value.is_a?(Arel::Nodes::SqlLiteral)
1368
+ value = Arel::Nodes::Grouping.new(value)
1369
+ end
1370
+ else
941
1371
  type = klass.type_for_attribute(attr.name)
942
1372
  value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
943
1373
  end
@@ -975,7 +1405,7 @@ module ActiveRecord
975
1405
  def exec_main_query(async: false)
976
1406
  if @none
977
1407
  if async
978
- return FutureResult::Complete.new([])
1408
+ return FutureResult.wrap([])
979
1409
  else
980
1410
  return []
981
1411
  end
@@ -985,17 +1415,21 @@ module ActiveRecord
985
1415
  if where_clause.contradiction?
986
1416
  [].freeze
987
1417
  elsif eager_loading?
988
- apply_join_dependency do |relation, join_dependency|
989
- if relation.null_relation?
990
- [].freeze
991
- else
992
- relation = join_dependency.apply_column_aliases(relation)
993
- @_join_dependency = join_dependency
994
- connection.select_all(relation.arel, "SQL", async: async)
1418
+ klass.with_connection do |c|
1419
+ apply_join_dependency do |relation, join_dependency|
1420
+ if relation.null_relation?
1421
+ [].freeze
1422
+ else
1423
+ relation = join_dependency.apply_column_aliases(relation)
1424
+ @_join_dependency = join_dependency
1425
+ c.select_all(relation.arel, "SQL", async: async)
1426
+ end
995
1427
  end
996
1428
  end
997
1429
  else
998
- klass._query_by_sql(arel, async: async)
1430
+ klass.with_connection do |c|
1431
+ klass._query_by_sql(c, arel, async: async)
1432
+ end
999
1433
  end
1000
1434
  end
1001
1435
  end