activerecord 7.0.8.6 → 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 (279) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +631 -1939
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +29 -29
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +16 -13
  7. data/lib/active_record/association_relation.rb +2 -2
  8. data/lib/active_record/associations/alias_tracker.rb +25 -19
  9. data/lib/active_record/associations/association.rb +35 -12
  10. data/lib/active_record/associations/association_scope.rb +16 -9
  11. data/lib/active_record/associations/belongs_to_association.rb +23 -8
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  13. data/lib/active_record/associations/builder/association.rb +3 -3
  14. data/lib/active_record/associations/builder/belongs_to.rb +22 -8
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  16. data/lib/active_record/associations/builder/has_many.rb +3 -4
  17. data/lib/active_record/associations/builder/has_one.rb +3 -4
  18. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  19. data/lib/active_record/associations/collection_association.rb +26 -14
  20. data/lib/active_record/associations/collection_proxy.rb +29 -11
  21. data/lib/active_record/associations/errors.rb +265 -0
  22. data/lib/active_record/associations/foreign_association.rb +10 -3
  23. data/lib/active_record/associations/has_many_association.rb +21 -14
  24. data/lib/active_record/associations/has_many_through_association.rb +17 -7
  25. data/lib/active_record/associations/has_one_association.rb +10 -3
  26. data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
  27. data/lib/active_record/associations/join_dependency.rb +10 -10
  28. data/lib/active_record/associations/nested_error.rb +47 -0
  29. data/lib/active_record/associations/preloader/association.rb +33 -8
  30. data/lib/active_record/associations/preloader/branch.rb +7 -1
  31. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  32. data/lib/active_record/associations/preloader.rb +13 -10
  33. data/lib/active_record/associations/singular_association.rb +7 -1
  34. data/lib/active_record/associations/through_association.rb +22 -11
  35. data/lib/active_record/associations.rb +354 -485
  36. data/lib/active_record/attribute_assignment.rb +0 -4
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  38. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  39. data/lib/active_record/attribute_methods/dirty.rb +53 -35
  40. data/lib/active_record/attribute_methods/primary_key.rb +45 -25
  41. data/lib/active_record/attribute_methods/query.rb +28 -16
  42. data/lib/active_record/attribute_methods/read.rb +8 -7
  43. data/lib/active_record/attribute_methods/serialization.rb +131 -32
  44. data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
  45. data/lib/active_record/attribute_methods/write.rb +6 -6
  46. data/lib/active_record/attribute_methods.rb +148 -33
  47. data/lib/active_record/attributes.rb +64 -50
  48. data/lib/active_record/autosave_association.rb +69 -37
  49. data/lib/active_record/base.rb +9 -5
  50. data/lib/active_record/callbacks.rb +11 -25
  51. data/lib/active_record/coders/column_serializer.rb +61 -0
  52. data/lib/active_record/coders/json.rb +1 -1
  53. data/lib/active_record/coders/yaml_column.rb +70 -42
  54. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +123 -131
  55. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
  57. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +323 -88
  58. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  59. data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
  60. data/lib/active_record/connection_adapters/abstract/query_cache.rb +217 -63
  61. data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -63
  62. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  63. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +137 -11
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +307 -129
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +510 -111
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +278 -125
  69. data/lib/active_record/connection_adapters/column.rb +9 -0
  70. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  71. data/lib/active_record/connection_adapters/mysql/database_statements.rb +26 -139
  72. data/lib/active_record/connection_adapters/mysql/quoting.rb +53 -54
  73. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  74. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
  75. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  76. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +25 -13
  77. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  78. data/lib/active_record/connection_adapters/mysql2_adapter.rb +101 -68
  79. data/lib/active_record/connection_adapters/pool_config.rb +20 -10
  80. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  81. data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
  82. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +100 -43
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  87. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +1 -1
  88. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  89. data/lib/active_record/connection_adapters/postgresql/quoting.rb +65 -61
  90. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
  91. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  92. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +151 -2
  93. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  94. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +370 -63
  95. data/lib/active_record/connection_adapters/postgresql_adapter.rb +367 -201
  96. data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
  97. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  98. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
  99. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +45 -46
  100. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  101. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +14 -0
  102. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  103. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +50 -8
  104. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +290 -110
  105. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  106. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  107. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  108. data/lib/active_record/connection_adapters.rb +124 -1
  109. data/lib/active_record/connection_handling.rb +96 -104
  110. data/lib/active_record/core.rb +251 -176
  111. data/lib/active_record/counter_cache.rb +68 -34
  112. data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
  113. data/lib/active_record/database_configurations/database_config.rb +26 -5
  114. data/lib/active_record/database_configurations/hash_config.rb +52 -34
  115. data/lib/active_record/database_configurations/url_config.rb +37 -12
  116. data/lib/active_record/database_configurations.rb +87 -34
  117. data/lib/active_record/delegated_type.rb +39 -10
  118. data/lib/active_record/deprecator.rb +7 -0
  119. data/lib/active_record/destroy_association_async_job.rb +3 -1
  120. data/lib/active_record/dynamic_matchers.rb +2 -2
  121. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  122. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  123. data/lib/active_record/encryption/config.rb +25 -1
  124. data/lib/active_record/encryption/configurable.rb +12 -19
  125. data/lib/active_record/encryption/context.rb +10 -3
  126. data/lib/active_record/encryption/contexts.rb +5 -1
  127. data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
  128. data/lib/active_record/encryption/encryptable_record.rb +45 -21
  129. data/lib/active_record/encryption/encrypted_attribute_type.rb +47 -12
  130. data/lib/active_record/encryption/encryptor.rb +18 -3
  131. data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
  132. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  133. data/lib/active_record/encryption/key_generator.rb +12 -1
  134. data/lib/active_record/encryption/key_provider.rb +1 -1
  135. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  136. data/lib/active_record/encryption/message_serializer.rb +6 -0
  137. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  138. data/lib/active_record/encryption/properties.rb +3 -3
  139. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  140. data/lib/active_record/encryption/scheme.rb +22 -21
  141. data/lib/active_record/encryption.rb +3 -0
  142. data/lib/active_record/enum.rb +129 -28
  143. data/lib/active_record/errors.rb +151 -31
  144. data/lib/active_record/explain.rb +21 -12
  145. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  146. data/lib/active_record/fixture_set/render_context.rb +2 -0
  147. data/lib/active_record/fixture_set/table_row.rb +29 -8
  148. data/lib/active_record/fixtures.rb +167 -97
  149. data/lib/active_record/future_result.rb +47 -8
  150. data/lib/active_record/gem_version.rb +4 -4
  151. data/lib/active_record/inheritance.rb +34 -18
  152. data/lib/active_record/insert_all.rb +72 -22
  153. data/lib/active_record/integration.rb +11 -8
  154. data/lib/active_record/internal_metadata.rb +124 -20
  155. data/lib/active_record/locking/optimistic.rb +8 -7
  156. data/lib/active_record/locking/pessimistic.rb +5 -2
  157. data/lib/active_record/log_subscriber.rb +18 -22
  158. data/lib/active_record/marshalling.rb +59 -0
  159. data/lib/active_record/message_pack.rb +124 -0
  160. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  161. data/lib/active_record/middleware/database_selector.rb +6 -8
  162. data/lib/active_record/middleware/shard_selector.rb +3 -1
  163. data/lib/active_record/migration/command_recorder.rb +106 -8
  164. data/lib/active_record/migration/compatibility.rb +147 -5
  165. data/lib/active_record/migration/default_strategy.rb +22 -0
  166. data/lib/active_record/migration/execution_strategy.rb +19 -0
  167. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  168. data/lib/active_record/migration.rb +234 -117
  169. data/lib/active_record/model_schema.rb +90 -102
  170. data/lib/active_record/nested_attributes.rb +48 -11
  171. data/lib/active_record/normalization.rb +163 -0
  172. data/lib/active_record/persistence.rb +168 -339
  173. data/lib/active_record/promise.rb +84 -0
  174. data/lib/active_record/query_cache.rb +18 -25
  175. data/lib/active_record/query_logs.rb +92 -52
  176. data/lib/active_record/query_logs_formatter.rb +41 -0
  177. data/lib/active_record/querying.rb +33 -8
  178. data/lib/active_record/railtie.rb +129 -85
  179. data/lib/active_record/railties/controller_runtime.rb +22 -7
  180. data/lib/active_record/railties/databases.rake +145 -154
  181. data/lib/active_record/railties/job_runtime.rb +23 -0
  182. data/lib/active_record/readonly_attributes.rb +32 -5
  183. data/lib/active_record/reflection.rb +267 -69
  184. data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
  185. data/lib/active_record/relation/batches.rb +198 -63
  186. data/lib/active_record/relation/calculations.rb +250 -93
  187. data/lib/active_record/relation/delegation.rb +30 -19
  188. data/lib/active_record/relation/finder_methods.rb +93 -18
  189. data/lib/active_record/relation/merger.rb +6 -6
  190. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  191. data/lib/active_record/relation/predicate_builder/association_query_value.rb +18 -3
  192. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  193. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  194. data/lib/active_record/relation/predicate_builder.rb +28 -16
  195. data/lib/active_record/relation/query_attribute.rb +2 -1
  196. data/lib/active_record/relation/query_methods.rb +576 -107
  197. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  198. data/lib/active_record/relation/spawn_methods.rb +5 -4
  199. data/lib/active_record/relation/where_clause.rb +7 -19
  200. data/lib/active_record/relation.rb +580 -90
  201. data/lib/active_record/result.rb +49 -48
  202. data/lib/active_record/runtime_registry.rb +63 -1
  203. data/lib/active_record/sanitization.rb +70 -25
  204. data/lib/active_record/schema.rb +8 -7
  205. data/lib/active_record/schema_dumper.rb +63 -14
  206. data/lib/active_record/schema_migration.rb +75 -24
  207. data/lib/active_record/scoping/default.rb +15 -5
  208. data/lib/active_record/scoping/named.rb +3 -2
  209. data/lib/active_record/scoping.rb +2 -1
  210. data/lib/active_record/secure_password.rb +60 -0
  211. data/lib/active_record/secure_token.rb +21 -3
  212. data/lib/active_record/signed_id.rb +27 -6
  213. data/lib/active_record/statement_cache.rb +7 -7
  214. data/lib/active_record/store.rb +8 -8
  215. data/lib/active_record/suppressor.rb +3 -1
  216. data/lib/active_record/table_metadata.rb +1 -1
  217. data/lib/active_record/tasks/database_tasks.rb +190 -118
  218. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  219. data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
  220. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  221. data/lib/active_record/test_fixtures.rb +170 -155
  222. data/lib/active_record/testing/query_assertions.rb +121 -0
  223. data/lib/active_record/timestamp.rb +31 -17
  224. data/lib/active_record/token_for.rb +123 -0
  225. data/lib/active_record/touch_later.rb +12 -7
  226. data/lib/active_record/transaction.rb +132 -0
  227. data/lib/active_record/transactions.rb +106 -24
  228. data/lib/active_record/translation.rb +0 -2
  229. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  230. data/lib/active_record/type/internal/timezone.rb +7 -2
  231. data/lib/active_record/type/serialized.rb +1 -3
  232. data/lib/active_record/type/time.rb +4 -0
  233. data/lib/active_record/type_caster/connection.rb +4 -4
  234. data/lib/active_record/validations/absence.rb +1 -1
  235. data/lib/active_record/validations/associated.rb +9 -3
  236. data/lib/active_record/validations/numericality.rb +5 -4
  237. data/lib/active_record/validations/presence.rb +5 -28
  238. data/lib/active_record/validations/uniqueness.rb +61 -11
  239. data/lib/active_record/validations.rb +12 -5
  240. data/lib/active_record/version.rb +1 -1
  241. data/lib/active_record.rb +247 -33
  242. data/lib/arel/alias_predication.rb +1 -1
  243. data/lib/arel/collectors/bind.rb +2 -0
  244. data/lib/arel/collectors/composite.rb +7 -0
  245. data/lib/arel/collectors/sql_string.rb +1 -1
  246. data/lib/arel/collectors/substitute_binds.rb +1 -1
  247. data/lib/arel/errors.rb +10 -0
  248. data/lib/arel/factory_methods.rb +4 -0
  249. data/lib/arel/nodes/binary.rb +6 -7
  250. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  251. data/lib/arel/nodes/cte.rb +36 -0
  252. data/lib/arel/nodes/fragments.rb +35 -0
  253. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  254. data/lib/arel/nodes/leading_join.rb +8 -0
  255. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  256. data/lib/arel/nodes/node.rb +115 -5
  257. data/lib/arel/nodes/sql_literal.rb +13 -0
  258. data/lib/arel/nodes/table_alias.rb +4 -0
  259. data/lib/arel/nodes.rb +6 -2
  260. data/lib/arel/predications.rb +3 -1
  261. data/lib/arel/select_manager.rb +1 -1
  262. data/lib/arel/table.rb +9 -5
  263. data/lib/arel/tree_manager.rb +8 -3
  264. data/lib/arel/update_manager.rb +2 -1
  265. data/lib/arel/visitors/dot.rb +1 -0
  266. data/lib/arel/visitors/mysql.rb +17 -5
  267. data/lib/arel/visitors/postgresql.rb +1 -12
  268. data/lib/arel/visitors/sqlite.rb +25 -0
  269. data/lib/arel/visitors/to_sql.rb +112 -34
  270. data/lib/arel/visitors/visitor.rb +2 -2
  271. data/lib/arel.rb +21 -3
  272. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  273. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  274. data/lib/rails/generators/active_record/migration.rb +3 -1
  275. data/lib/rails/generators/active_record/model/USAGE +113 -0
  276. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  277. metadata +56 -14
  278. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  279. data/lib/active_record/null_relation.rb +0 -63
@@ -3,20 +3,70 @@
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
- :extending, :unscope, :optimizer_hints, :annotate]
56
+ :extending, :unscope, :optimizer_hints, :annotate,
57
+ :with]
9
58
 
10
59
  SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering, :strict_loading,
11
60
  :reverse_order, :distinct, :create_with, :skip_query_cache]
12
61
 
13
62
  CLAUSE_METHODS = [:where, :having, :from]
14
- INVALID_METHODS_FOR_DELETE_ALL = [:distinct]
63
+ INVALID_METHODS_FOR_DELETE_ALL = [:distinct, :with, :with_recursive]
15
64
 
16
65
  VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS + CLAUSE_METHODS
17
66
 
18
67
  include Enumerable
19
68
  include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
69
+ include SignedId::RelationMethods, TokenFor::RelationMethods
20
70
 
21
71
  attr_reader :table, :klass, :loaded, :predicate_builder
22
72
  attr_accessor :skip_preloading_value
@@ -33,6 +83,8 @@ module ActiveRecord
33
83
  @delegate_to_klass = false
34
84
  @future_result = nil
35
85
  @records = nil
86
+ @async = false
87
+ @none = false
36
88
  end
37
89
 
38
90
  def initialize_copy(other)
@@ -43,7 +95,7 @@ module ActiveRecord
43
95
  def bind_attribute(name, value) # :nodoc:
44
96
  if reflection = klass._reflect_on_association(name)
45
97
  name = reflection.foreign_key
46
- value = value.read_attribute(reflection.klass.primary_key) unless value.nil?
98
+ value = value.read_attribute(reflection.association_primary_key) unless value.nil?
47
99
  end
48
100
 
49
101
  attr = table[name]
@@ -159,21 +211,25 @@ module ActiveRecord
159
211
  # failed due to validation errors it won't be persisted, you get what
160
212
  # #create returns in such situation.
161
213
  #
162
- # Please note <b>this method is not atomic</b>, it runs first a SELECT, and if
163
- # there are no results an INSERT is attempted. If there are other threads
164
- # or processes there is a race condition between both calls and it could
165
- # be the case that you end up with two similar records.
214
+ # If creation failed because of a unique constraint, this method will
215
+ # assume it encountered a race condition and will try finding the record
216
+ # once more. If somehow the second find still does not find a record
217
+ # because a concurrent DELETE happened, it will then raise an
218
+ # ActiveRecord::RecordNotFound exception.
166
219
  #
167
- # If this might be a problem for your application, please see #create_or_find_by.
220
+ # Please note <b>this method is not atomic</b>, it runs first a SELECT,
221
+ # and if there are no results an INSERT is attempted. So if the table
222
+ # doesn't have a relevant unique constraint it could be the case that
223
+ # you end up with two or more similar records.
168
224
  def find_or_create_by(attributes, &block)
169
- find_by(attributes) || create(attributes, &block)
225
+ find_by(attributes) || create_or_find_by(attributes, &block)
170
226
  end
171
227
 
172
228
  # Like #find_or_create_by, but calls
173
229
  # {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
174
230
  # is raised if the created record is invalid.
175
231
  def find_or_create_by!(attributes, &block)
176
- find_by(attributes) || create!(attributes, &block)
232
+ find_by(attributes) || create_or_find_by!(attributes, &block)
177
233
  end
178
234
 
179
235
  # Attempts to create a record with the given attributes in a table that has a unique database constraint
@@ -181,16 +237,15 @@ module ActiveRecord
181
237
  # unique constraints, the exception such an insertion would normally raise is caught,
182
238
  # and the existing record with those attributes is found using #find_by!.
183
239
  #
184
- # This is similar to #find_or_create_by, but avoids the problem of stale reads between the SELECT
185
- # and the INSERT, as that method needs to first query the table, then attempt to insert a row
186
- # if none is found.
240
+ # This is similar to #find_or_create_by, but tries to create the record first. As such it is
241
+ # better suited for cases where the record is most likely not to exist yet.
187
242
  #
188
243
  # There are several drawbacks to #create_or_find_by, though:
189
244
  #
190
245
  # * The underlying table must have the relevant columns defined with unique database constraints.
191
246
  # * A unique constraint violation may be triggered by only one, or at least less than all,
192
247
  # of the given attributes. This means that the subsequent #find_by! may fail to find a
193
- # matching record, which will then raise an <tt>ActiveRecord::RecordNotFound</tt> exception,
248
+ # matching record, which will then raise an ActiveRecord::RecordNotFound exception,
194
249
  # rather than a record with the given attributes.
195
250
  # * While we avoid the race condition between SELECT -> INSERT from #find_or_create_by,
196
251
  # we actually have another race condition between INSERT -> SELECT, which can be triggered
@@ -199,26 +254,40 @@ module ActiveRecord
199
254
  # * It relies on exception handling to handle control flow, which may be marginally slower.
200
255
  # * The primary key may auto-increment on each create, even if it fails. This can accelerate
201
256
  # the problem of running out of integers, if the underlying table is still stuck on a primary
202
- # key of type int (note: All Rails apps since 5.1+ have defaulted to bigint, which is not liable
257
+ # key of type int (note: All \Rails apps since 5.1+ have defaulted to bigint, which is not liable
203
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.
204
261
  #
205
262
  # This method will return a record if all given attributes are covered by unique constraints
206
263
  # (unless the INSERT -> DELETE -> SELECT race condition is triggered), but if creation was attempted
207
264
  # and failed due to validation errors it won't be persisted, you get what #create returns in
208
265
  # such situation.
209
266
  def create_or_find_by(attributes, &block)
210
- transaction(requires_new: true) { create(attributes, &block) }
211
- rescue ActiveRecord::RecordNotUnique
212
- 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
275
+ end
213
276
  end
214
277
 
215
278
  # Like #create_or_find_by, but calls
216
279
  # {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
217
280
  # is raised if the created record is invalid.
218
281
  def create_or_find_by!(attributes, &block)
219
- transaction(requires_new: true) { create!(attributes, &block) }
220
- rescue ActiveRecord::RecordNotUnique
221
- 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
290
+ end
222
291
  end
223
292
 
224
293
  # Like #find_or_create_by, but calls {new}[rdoc-ref:Core#new]
@@ -231,13 +300,30 @@ module ActiveRecord
231
300
  # returns the result as a string. The string is formatted imitating the
232
301
  # ones printed by the database shell.
233
302
  #
303
+ # User.all.explain
304
+ # # EXPLAIN SELECT `users`.* FROM `users`
305
+ # # ...
306
+ #
234
307
  # Note that this method actually runs the queries, since the results of some
235
308
  # are needed by the next ones when eager loading is going on.
236
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
+ #
237
323
  # Please see further details in the
238
324
  # {Active Record Query Interface guide}[https://guides.rubyonrails.org/active_record_querying.html#running-explain].
239
- def explain
240
- exec_explain(collecting_queries_for_explain { exec_queries })
325
+ def explain(*options)
326
+ ExplainProxy.new(self, options)
241
327
  end
242
328
 
243
329
  # Converts relation objects to Array.
@@ -267,6 +353,8 @@ module ActiveRecord
267
353
 
268
354
  # Returns true if there are no records.
269
355
  def empty?
356
+ return true if @none
357
+
270
358
  if loaded?
271
359
  records.empty?
272
360
  else
@@ -275,26 +363,49 @@ module ActiveRecord
275
363
  end
276
364
 
277
365
  # Returns true if there are no records.
278
- def none?
279
- return super if block_given?
366
+ #
367
+ # When a pattern argument is given, this method checks whether elements in
368
+ # the Enumerable match the pattern via the case-equality operator (<tt>===</tt>).
369
+ #
370
+ # posts.none?(Comment) # => true or false
371
+ def none?(*args)
372
+ return true if @none
373
+
374
+ return super if args.present? || block_given?
280
375
  empty?
281
376
  end
282
377
 
283
378
  # Returns true if there are any records.
284
- def any?
285
- return super if block_given?
379
+ #
380
+ # When a pattern argument is given, this method checks whether elements in
381
+ # the Enumerable match the pattern via the case-equality operator (<tt>===</tt>).
382
+ #
383
+ # posts.any?(Post) # => true or false
384
+ def any?(*args)
385
+ return false if @none
386
+
387
+ return super if args.present? || block_given?
286
388
  !empty?
287
389
  end
288
390
 
289
391
  # Returns true if there is exactly one record.
290
- def one?
291
- return super if block_given?
392
+ #
393
+ # When a pattern argument is given, this method checks whether elements in
394
+ # the Enumerable match the pattern via the case-equality operator (<tt>===</tt>).
395
+ #
396
+ # posts.one?(Post) # => true or false
397
+ def one?(*args)
398
+ return false if @none
399
+
400
+ return super if args.present? || block_given?
292
401
  return records.one? if loaded?
293
402
  limited_count == 1
294
403
  end
295
404
 
296
405
  # Returns true if there is more than one record.
297
406
  def many?
407
+ return false if @none
408
+
298
409
  return super if block_given?
299
410
  return records.many? if loaded?
300
411
  limited_count > 1
@@ -307,7 +418,7 @@ module ActiveRecord
307
418
  # # => "products/query-1850ab3d302391b85b8693e941286659"
308
419
  #
309
420
  # If ActiveRecord::Base.collection_cache_versioning is turned off, as it was
310
- # in Rails 6.0 and earlier, the cache key will also include a version.
421
+ # in \Rails 6.0 and earlier, the cache key will also include a version.
311
422
  #
312
423
  # ActiveRecord::Base.collection_cache_versioning = false
313
424
  # Product.where("name like ?", "%Cosmic Encounter%").cache_key
@@ -362,28 +473,30 @@ module ActiveRecord
362
473
  else
363
474
  collection = eager_loading? ? apply_join_dependency : self
364
475
 
365
- column = connection.visitor.compile(table[timestamp_column])
366
- 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"
367
479
 
368
- if collection.has_limit_or_offset?
369
- query = collection.select("#{column} AS collection_cache_key_timestamp")
370
- query._select!(table[Arel.star]) if distinct_value && collection.select_values.empty?
371
- subquery_alias = "subquery_for_cache_key"
372
- subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
373
- arel = query.build_subquery(subquery_alias, select_values % subquery_column)
374
- else
375
- query = collection.unscope(:order)
376
- query.select_values = [select_values % column]
377
- arel = query.arel
378
- 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
379
491
 
380
- size, timestamp = connection.select_rows(arel, nil).first
492
+ size, timestamp = c.select_rows(arel, nil).first
381
493
 
382
- if size
383
- column_type = klass.type_for_attribute(timestamp_column)
384
- timestamp = column_type.deserialize(timestamp)
385
- else
386
- 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
387
500
  end
388
501
  end
389
502
 
@@ -409,7 +522,7 @@ module ActiveRecord
409
522
  # Comment.where(post_id: 1).scoping do
410
523
  # Comment.first
411
524
  # end
412
- # # => SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1
525
+ # # SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1
413
526
  #
414
527
  # If <tt>all_queries: true</tt> is passed, scoping will apply to all queries
415
528
  # for the relation including +update+ and +delete+ on instances.
@@ -446,7 +559,8 @@ module ActiveRecord
446
559
  #
447
560
  # ==== Parameters
448
561
  #
449
- # * +updates+ - A string, array, or hash representing the SET part of an SQL statement.
562
+ # * +updates+ - A string, array, or hash representing the SET part of an SQL statement. Any strings provided will
563
+ # be type cast, unless you use +Arel.sql+. (Don't pass user-provided values to +Arel.sql+.)
450
564
  #
451
565
  # ==== Examples
452
566
  #
@@ -461,9 +575,14 @@ module ActiveRecord
461
575
  #
462
576
  # # Update all invoices and set the number column to its id value.
463
577
  # Invoice.update_all('number = id')
578
+ #
579
+ # # Update all books with 'Rails' in their title
580
+ # Book.where('title LIKE ?', '%Rails%').update_all(title: Arel.sql("title + ' - volume 1'"))
464
581
  def update_all(updates)
465
582
  raise ArgumentError, "Empty list of attributes to change" if updates.blank?
466
583
 
584
+ return 0 if @none
585
+
467
586
  if updates.is_a?(Hash)
468
587
  if klass.locking_enabled? &&
469
588
  !updates.key?(klass.locking_column) &&
@@ -476,13 +595,20 @@ module ActiveRecord
476
595
  values = Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
477
596
  end
478
597
 
479
- arel = eager_loading? ? apply_join_dependency.arel : build_arel
480
- 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
481
601
 
482
- group_values_arel_columns = arel_columns(group_values.uniq)
483
- having_clause_ast = having_clause.ast unless having_clause.empty?
484
- stmt = arel.compile_update(values, table[primary_key], having_clause_ast, group_values_arel_columns)
485
- 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
486
612
  end
487
613
 
488
614
  def update(id = :all, attributes) # :nodoc:
@@ -501,6 +627,283 @@ module ActiveRecord
501
627
  end
502
628
  end
503
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
+
504
907
  # Updates the counters of the records in the current relation.
505
908
  #
506
909
  # ==== Parameters
@@ -599,6 +1002,8 @@ module ActiveRecord
599
1002
  # Post.distinct.delete_all
600
1003
  # # => ActiveRecord::ActiveRecordError: delete_all doesn't support distinct
601
1004
  def delete_all
1005
+ return 0 if @none
1006
+
602
1007
  invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select do |method|
603
1008
  value = @values[method]
604
1009
  method == :distinct ? value : value&.any?
@@ -607,14 +1012,79 @@ module ActiveRecord
607
1012
  raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
608
1013
  end
609
1014
 
610
- arel = eager_loading? ? apply_join_dependency.arel : build_arel
611
- 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
612
1031
 
613
- group_values_arel_columns = arel_columns(group_values.uniq)
614
- having_clause_ast = having_clause.ast unless having_clause.empty?
615
- stmt = arel.compile_delete(table[primary_key], having_clause_ast, group_values_arel_columns)
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
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
616
1082
 
617
- 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
618
1088
  end
619
1089
 
620
1090
  # Finds and destroys all records matching the specified conditions.
@@ -662,17 +1132,19 @@ module ActiveRecord
662
1132
  #
663
1133
  # ASYNC Post Load (0.0ms) (db time 2ms) SELECT "posts".* FROM "posts" LIMIT 100
664
1134
  def load_async
665
- return load if !connection.async_enabled?
1135
+ with_connection do |c|
1136
+ return load if !c.async_enabled?
666
1137
 
667
- unless loaded?
668
- result = exec_main_query(async: connection.current_transaction.closed?)
1138
+ unless loaded?
1139
+ result = exec_main_query(async: c.current_transaction.closed?)
669
1140
 
670
- if result.is_a?(Array)
671
- @records = result
672
- else
673
- @future_result = result
1141
+ if result.is_a?(Array)
1142
+ @records = result
1143
+ else
1144
+ @future_result = result
1145
+ end
1146
+ @loaded = true
674
1147
  end
675
- @loaded = true
676
1148
  end
677
1149
 
678
1150
  self
@@ -720,7 +1192,7 @@ module ActiveRecord
720
1192
  # Returns sql statement for the relation.
721
1193
  #
722
1194
  # User.where(name: 'Oscar').to_sql
723
- # # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
1195
+ # # SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
724
1196
  def to_sql
725
1197
  @to_sql ||= if eager_loading?
726
1198
  apply_join_dependency do |relation, join_dependency|
@@ -728,8 +1200,9 @@ module ActiveRecord
728
1200
  relation.to_sql
729
1201
  end
730
1202
  else
731
- conn = klass.connection
732
- conn.unprepared_statement { conn.to_sql(arel) }
1203
+ klass.with_connection do |conn|
1204
+ conn.unprepared_statement { conn.to_sql(arel) }
1205
+ end
733
1206
  end
734
1207
  end
735
1208
 
@@ -774,8 +1247,13 @@ module ActiveRecord
774
1247
  end
775
1248
  end
776
1249
 
777
- def pretty_print(q)
778
- q.pp(records)
1250
+ def pretty_print(pp)
1251
+ subject = loaded? ? records : annotate("loading for pp")
1252
+ entries = subject.take([limit_value, 11].compact.min)
1253
+
1254
+ entries[10] = "..." if entries.size == 11
1255
+
1256
+ pp.pp(entries)
779
1257
  end
780
1258
 
781
1259
  # Returns true if relation is blank.
@@ -809,7 +1287,7 @@ module ActiveRecord
809
1287
  end
810
1288
 
811
1289
  def alias_tracker(joins = [], aliases = nil) # :nodoc:
812
- ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins, aliases)
1290
+ ActiveRecord::Associations::AliasTracker.create(connection_pool, table.name, joins, aliases)
813
1291
  end
814
1292
 
815
1293
  class StrictLoadingScope # :nodoc:
@@ -837,10 +1315,6 @@ module ActiveRecord
837
1315
  @loaded = true
838
1316
  end
839
1317
 
840
- def null_relation? # :nodoc:
841
- is_a?(NullRelation)
842
- end
843
-
844
1318
  private
845
1319
  def already_in_scope?(registry)
846
1320
  @delegate_to_klass && registry.current_scope(klass, true)
@@ -889,7 +1363,11 @@ module ActiveRecord
889
1363
  def _substitute_values(values)
890
1364
  values.map do |name, value|
891
1365
  attr = table[name]
892
- 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
893
1371
  type = klass.type_for_attribute(attr.name)
894
1372
  value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
895
1373
  end
@@ -925,21 +1403,33 @@ module ActiveRecord
925
1403
  end
926
1404
 
927
1405
  def exec_main_query(async: false)
1406
+ if @none
1407
+ if async
1408
+ return FutureResult.wrap([])
1409
+ else
1410
+ return []
1411
+ end
1412
+ end
1413
+
928
1414
  skip_query_cache_if_necessary do
929
1415
  if where_clause.contradiction?
930
1416
  [].freeze
931
1417
  elsif eager_loading?
932
- apply_join_dependency do |relation, join_dependency|
933
- if relation.null_relation?
934
- [].freeze
935
- else
936
- relation = join_dependency.apply_column_aliases(relation)
937
- @_join_dependency = join_dependency
938
- connection.select_all(relation.arel, "SQL", async: async)
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
939
1427
  end
940
1428
  end
941
1429
  else
942
- klass._query_by_sql(arel, async: async)
1430
+ klass.with_connection do |c|
1431
+ klass._query_by_sql(c, arel, async: async)
1432
+ end
943
1433
  end
944
1434
  end
945
1435
  end