activerecord 7.1.5 → 7.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (183) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +515 -2440
  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 +9 -8
  8. data/lib/active_record/associations/belongs_to_association.rb +14 -7
  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 +6 -4
  15. data/lib/active_record/associations/collection_proxy.rb +14 -1
  16. data/lib/active_record/associations/has_many_association.rb +1 -1
  17. data/lib/active_record/associations/join_dependency/join_association.rb +29 -28
  18. data/lib/active_record/associations/join_dependency.rb +5 -5
  19. data/lib/active_record/associations/nested_error.rb +47 -0
  20. data/lib/active_record/associations/preloader/association.rb +2 -1
  21. data/lib/active_record/associations/preloader/branch.rb +7 -1
  22. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  23. data/lib/active_record/associations/singular_association.rb +6 -0
  24. data/lib/active_record/associations/through_association.rb +1 -1
  25. data/lib/active_record/associations.rb +33 -16
  26. data/lib/active_record/attribute_assignment.rb +1 -11
  27. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  28. data/lib/active_record/attribute_methods/dirty.rb +1 -1
  29. data/lib/active_record/attribute_methods/primary_key.rb +23 -55
  30. data/lib/active_record/attribute_methods/read.rb +4 -16
  31. data/lib/active_record/attribute_methods/serialization.rb +4 -24
  32. data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -10
  33. data/lib/active_record/attribute_methods/write.rb +3 -3
  34. data/lib/active_record/attribute_methods.rb +60 -71
  35. data/lib/active_record/attributes.rb +55 -42
  36. data/lib/active_record/autosave_association.rb +13 -32
  37. data/lib/active_record/base.rb +2 -3
  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 +248 -65
  41. data/lib/active_record/connection_adapters/abstract/database_statements.rb +34 -17
  42. data/lib/active_record/connection_adapters/abstract/query_cache.rb +159 -74
  43. data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
  44. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
  45. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +14 -5
  46. data/lib/active_record/connection_adapters/abstract/transaction.rb +60 -57
  47. data/lib/active_record/connection_adapters/abstract_adapter.rb +18 -46
  48. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +32 -6
  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 +7 -1
  52. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +11 -5
  53. data/lib/active_record/connection_adapters/mysql2_adapter.rb +5 -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/cidr.rb +1 -1
  57. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  58. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  59. data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
  60. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -0
  61. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +15 -13
  62. data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -21
  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 +44 -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 +25 -2
  71. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +107 -75
  72. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +12 -6
  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 +56 -41
  76. data/lib/active_record/core.rb +53 -37
  77. data/lib/active_record/counter_cache.rb +18 -9
  78. data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
  79. data/lib/active_record/database_configurations/database_config.rb +15 -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 +24 -0
  84. data/lib/active_record/dynamic_matchers.rb +2 -2
  85. data/lib/active_record/encryption/encryptable_record.rb +2 -2
  86. data/lib/active_record/encryption/encrypted_attribute_type.rb +22 -2
  87. data/lib/active_record/encryption/encryptor.rb +17 -2
  88. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  89. data/lib/active_record/encryption/message_serializer.rb +4 -0
  90. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  91. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  92. data/lib/active_record/encryption.rb +0 -2
  93. data/lib/active_record/enum.rb +10 -1
  94. data/lib/active_record/errors.rb +16 -11
  95. data/lib/active_record/explain.rb +13 -24
  96. data/lib/active_record/fixtures.rb +37 -31
  97. data/lib/active_record/future_result.rb +8 -4
  98. data/lib/active_record/gem_version.rb +3 -3
  99. data/lib/active_record/inheritance.rb +4 -2
  100. data/lib/active_record/insert_all.rb +18 -15
  101. data/lib/active_record/integration.rb +4 -1
  102. data/lib/active_record/internal_metadata.rb +48 -34
  103. data/lib/active_record/locking/optimistic.rb +7 -6
  104. data/lib/active_record/log_subscriber.rb +0 -21
  105. data/lib/active_record/marshalling.rb +1 -4
  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 +85 -76
  112. data/lib/active_record/model_schema.rb +28 -68
  113. data/lib/active_record/nested_attributes.rb +13 -16
  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 +18 -6
  117. data/lib/active_record/query_logs.rb +15 -0
  118. data/lib/active_record/querying.rb +21 -9
  119. data/lib/active_record/railtie.rb +50 -62
  120. data/lib/active_record/railties/controller_runtime.rb +13 -4
  121. data/lib/active_record/railties/databases.rake +41 -44
  122. data/lib/active_record/reflection.rb +90 -35
  123. data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
  124. data/lib/active_record/relation/batches.rb +3 -3
  125. data/lib/active_record/relation/calculations.rb +94 -61
  126. data/lib/active_record/relation/delegation.rb +8 -11
  127. data/lib/active_record/relation/finder_methods.rb +16 -2
  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.rb +3 -3
  131. data/lib/active_record/relation/query_methods.rb +196 -57
  132. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  133. data/lib/active_record/relation/spawn_methods.rb +2 -18
  134. data/lib/active_record/relation/where_clause.rb +7 -19
  135. data/lib/active_record/relation.rb +496 -72
  136. data/lib/active_record/result.rb +31 -44
  137. data/lib/active_record/runtime_registry.rb +39 -0
  138. data/lib/active_record/sanitization.rb +24 -19
  139. data/lib/active_record/schema.rb +8 -6
  140. data/lib/active_record/schema_dumper.rb +19 -9
  141. data/lib/active_record/schema_migration.rb +30 -14
  142. data/lib/active_record/signed_id.rb +11 -1
  143. data/lib/active_record/statement_cache.rb +7 -7
  144. data/lib/active_record/table_metadata.rb +1 -10
  145. data/lib/active_record/tasks/database_tasks.rb +76 -70
  146. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  147. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  148. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
  149. data/lib/active_record/test_fixtures.rb +81 -91
  150. data/lib/active_record/testing/query_assertions.rb +121 -0
  151. data/lib/active_record/timestamp.rb +1 -1
  152. data/lib/active_record/token_for.rb +22 -12
  153. data/lib/active_record/touch_later.rb +1 -1
  154. data/lib/active_record/transaction.rb +68 -0
  155. data/lib/active_record/transactions.rb +43 -14
  156. data/lib/active_record/translation.rb +0 -2
  157. data/lib/active_record/type/serialized.rb +1 -3
  158. data/lib/active_record/type_caster/connection.rb +4 -4
  159. data/lib/active_record/validations/associated.rb +9 -3
  160. data/lib/active_record/validations/uniqueness.rb +14 -10
  161. data/lib/active_record/validations.rb +4 -1
  162. data/lib/active_record.rb +149 -40
  163. data/lib/arel/alias_predication.rb +1 -1
  164. data/lib/arel/collectors/bind.rb +2 -0
  165. data/lib/arel/collectors/composite.rb +7 -0
  166. data/lib/arel/collectors/sql_string.rb +1 -1
  167. data/lib/arel/collectors/substitute_binds.rb +1 -1
  168. data/lib/arel/nodes/binary.rb +0 -6
  169. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  170. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  171. data/lib/arel/nodes/node.rb +4 -3
  172. data/lib/arel/nodes/sql_literal.rb +7 -0
  173. data/lib/arel/nodes.rb +2 -2
  174. data/lib/arel/predications.rb +1 -1
  175. data/lib/arel/select_manager.rb +1 -1
  176. data/lib/arel/tree_manager.rb +3 -2
  177. data/lib/arel/update_manager.rb +2 -1
  178. data/lib/arel/visitors/dot.rb +1 -0
  179. data/lib/arel/visitors/mysql.rb +9 -4
  180. data/lib/arel/visitors/postgresql.rb +1 -12
  181. data/lib/arel/visitors/to_sql.rb +29 -16
  182. data/lib/arel.rb +7 -3
  183. metadata +20 -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,18 +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
- key = if klass.composite_primary_key?
530
- primary_key.map { |pk| table[pk] }
531
- else
532
- table[primary_key]
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 }
533
611
  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
612
  end
537
613
 
538
614
  def update(id = :all, attributes) # :nodoc:
@@ -551,6 +627,283 @@ module ActiveRecord
551
627
  end
552
628
  end
553
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
+
554
907
  # Updates the counters of the records in the current relation.
555
908
  #
556
909
  # ==== Parameters
@@ -659,19 +1012,79 @@ module ActiveRecord
659
1012
  raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
660
1013
  end
661
1014
 
662
- arel = eager_loading? ? apply_join_dependency.arel : build_arel
663
- 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
1031
+
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?)
1052
+
1053
+ where(model.primary_key => id_or_array).delete_all
1054
+ end
664
1055
 
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] }
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)
669
1079
  else
670
- table[primary_key]
1080
+ id.is_a?(Array)
671
1081
  end
672
- stmt = arel.compile_delete(key, having_clause_ast, group_values_arel_columns)
673
1082
 
674
- klass.connection.delete(stmt, "#{klass} Delete All").tap { reset }
1083
+ if multiple_ids
1084
+ find(id).each(&:destroy)
1085
+ else
1086
+ find(id).destroy
1087
+ end
675
1088
  end
676
1089
 
677
1090
  # Finds and destroys all records matching the specified conditions.
@@ -719,17 +1132,19 @@ module ActiveRecord
719
1132
  #
720
1133
  # ASYNC Post Load (0.0ms) (db time 2ms) SELECT "posts".* FROM "posts" LIMIT 100
721
1134
  def load_async
722
- return load if !connection.async_enabled?
1135
+ with_connection do |c|
1136
+ return load if !c.async_enabled?
723
1137
 
724
- unless loaded?
725
- result = exec_main_query(async: connection.current_transaction.closed?)
1138
+ unless loaded?
1139
+ result = exec_main_query(async: c.current_transaction.closed?)
726
1140
 
727
- if result.is_a?(Array)
728
- @records = result
729
- else
730
- @future_result = result
1141
+ if result.is_a?(Array)
1142
+ @records = result
1143
+ else
1144
+ @future_result = result
1145
+ end
1146
+ @loaded = true
731
1147
  end
732
- @loaded = true
733
1148
  end
734
1149
 
735
1150
  self
@@ -785,8 +1200,9 @@ module ActiveRecord
785
1200
  relation.to_sql
786
1201
  end
787
1202
  else
788
- conn = klass.connection
789
- conn.unprepared_statement { conn.to_sql(arel) }
1203
+ klass.with_connection do |conn|
1204
+ conn.unprepared_statement { conn.to_sql(arel) }
1205
+ end
790
1206
  end
791
1207
  end
792
1208
 
@@ -871,7 +1287,7 @@ module ActiveRecord
871
1287
  end
872
1288
 
873
1289
  def alias_tracker(joins = [], aliases = nil) # :nodoc:
874
- ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins, aliases)
1290
+ ActiveRecord::Associations::AliasTracker.create(connection_pool, table.name, joins, aliases)
875
1291
  end
876
1292
 
877
1293
  class StrictLoadingScope # :nodoc:
@@ -947,7 +1363,11 @@ module ActiveRecord
947
1363
  def _substitute_values(values)
948
1364
  values.map do |name, value|
949
1365
  attr = table[name]
950
- 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
951
1371
  type = klass.type_for_attribute(attr.name)
952
1372
  value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
953
1373
  end
@@ -995,17 +1415,21 @@ module ActiveRecord
995
1415
  if where_clause.contradiction?
996
1416
  [].freeze
997
1417
  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)
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
1005
1427
  end
1006
1428
  end
1007
1429
  else
1008
- klass._query_by_sql(arel, async: async)
1430
+ klass.with_connection do |c|
1431
+ klass._query_by_sql(c, arel, async: async)
1432
+ end
1009
1433
  end
1010
1434
  end
1011
1435
  end