activerecord 7.0.8.7 → 7.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (283) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +781 -1777
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +30 -30
  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 +31 -23
  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 +40 -9
  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 +35 -21
  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 +4 -3
  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 +153 -33
  47. data/lib/active_record/attributes.rb +96 -71
  48. data/lib/active_record/autosave_association.rb +81 -39
  49. data/lib/active_record/base.rb +11 -7
  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 +343 -91
  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 +229 -64
  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 +142 -12
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +310 -129
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +539 -111
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +289 -128
  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 +60 -55
  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 +108 -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 +153 -2
  93. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +54 -1
  94. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +371 -64
  95. data/lib/active_record/connection_adapters/postgresql_adapter.rb +374 -203
  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 +57 -45
  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 +51 -8
  104. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +298 -113
  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 +101 -105
  110. data/lib/active_record/core.rb +273 -178
  111. data/lib/active_record/counter_cache.rb +69 -35
  112. data/lib/active_record/database_configurations/connection_url_resolver.rb +10 -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 +56 -27
  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 +46 -22
  129. data/lib/active_record/encryption/encrypted_attribute_type.rb +48 -13
  130. data/lib/active_record/encryption/encryptor.rb +35 -19
  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 +130 -28
  143. data/lib/active_record/errors.rb +154 -34
  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 +48 -10
  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 +236 -118
  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 +96 -52
  176. data/lib/active_record/query_logs_formatter.rb +41 -0
  177. data/lib/active_record/querying.rb +35 -10
  178. data/lib/active_record/railtie.rb +131 -87
  179. data/lib/active_record/railties/controller_runtime.rb +22 -7
  180. data/lib/active_record/railties/databases.rake +147 -155
  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 +270 -108
  187. data/lib/active_record/relation/delegation.rb +30 -19
  188. data/lib/active_record/relation/finder_methods.rb +97 -21
  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 +20 -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 +3 -2
  196. data/lib/active_record/relation/query_methods.rb +585 -109
  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 +15 -21
  200. data/lib/active_record/relation.rb +592 -92
  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 +90 -23
  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 +33 -11
  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 +23 -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 +108 -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 +3 -1
  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/crud.rb +2 -0
  248. data/lib/arel/delete_manager.rb +5 -0
  249. data/lib/arel/errors.rb +10 -0
  250. data/lib/arel/factory_methods.rb +4 -0
  251. data/lib/arel/nodes/binary.rb +6 -7
  252. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  253. data/lib/arel/nodes/cte.rb +36 -0
  254. data/lib/arel/nodes/delete_statement.rb +4 -2
  255. data/lib/arel/nodes/fragments.rb +35 -0
  256. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  257. data/lib/arel/nodes/leading_join.rb +8 -0
  258. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  259. data/lib/arel/nodes/node.rb +115 -5
  260. data/lib/arel/nodes/sql_literal.rb +13 -0
  261. data/lib/arel/nodes/table_alias.rb +4 -0
  262. data/lib/arel/nodes/update_statement.rb +4 -2
  263. data/lib/arel/nodes.rb +6 -2
  264. data/lib/arel/predications.rb +3 -1
  265. data/lib/arel/select_manager.rb +7 -3
  266. data/lib/arel/table.rb +9 -5
  267. data/lib/arel/tree_manager.rb +8 -3
  268. data/lib/arel/update_manager.rb +7 -1
  269. data/lib/arel/visitors/dot.rb +3 -0
  270. data/lib/arel/visitors/mysql.rb +17 -5
  271. data/lib/arel/visitors/postgresql.rb +1 -12
  272. data/lib/arel/visitors/sqlite.rb +25 -0
  273. data/lib/arel/visitors/to_sql.rb +114 -34
  274. data/lib/arel/visitors/visitor.rb +2 -2
  275. data/lib/arel.rb +21 -3
  276. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  277. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  278. data/lib/rails/generators/active_record/migration.rb +3 -1
  279. data/lib/rails/generators/active_record/model/USAGE +113 -0
  280. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  281. metadata +56 -17
  282. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  283. 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,29 +254,53 @@ 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
+ record = nil
269
+ transaction(requires_new: true) do
270
+ record = create(attributes, &block)
271
+ record._last_transaction_return_status || raise(ActiveRecord::Rollback)
272
+ end
273
+ record
274
+ rescue ActiveRecord::RecordNotUnique
275
+ if connection.transaction_open?
276
+ where(attributes).lock.find_by!(attributes)
277
+ else
278
+ find_by!(attributes)
279
+ end
280
+ end
213
281
  end
214
282
 
215
283
  # Like #create_or_find_by, but calls
216
284
  # {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
217
285
  # is raised if the created record is invalid.
218
286
  def create_or_find_by!(attributes, &block)
219
- transaction(requires_new: true) { create!(attributes, &block) }
220
- rescue ActiveRecord::RecordNotUnique
221
- find_by!(attributes)
287
+ with_connection do |connection|
288
+ record = nil
289
+ transaction(requires_new: true) do
290
+ record = create!(attributes, &block)
291
+ record._last_transaction_return_status || raise(ActiveRecord::Rollback)
292
+ end
293
+ record
294
+ rescue ActiveRecord::RecordNotUnique
295
+ if connection.transaction_open?
296
+ where(attributes).lock.find_by!(attributes)
297
+ else
298
+ find_by!(attributes)
299
+ end
300
+ end
222
301
  end
223
302
 
224
- # Like #find_or_create_by, but calls {new}[rdoc-ref:Core#new]
303
+ # Like #find_or_create_by, but calls {new}[rdoc-ref:Core.new]
225
304
  # instead of {create}[rdoc-ref:Persistence::ClassMethods#create].
226
305
  def find_or_initialize_by(attributes, &block)
227
306
  find_by(attributes) || new(attributes, &block)
@@ -231,13 +310,30 @@ module ActiveRecord
231
310
  # returns the result as a string. The string is formatted imitating the
232
311
  # ones printed by the database shell.
233
312
  #
313
+ # User.all.explain
314
+ # # EXPLAIN SELECT `users`.* FROM `users`
315
+ # # ...
316
+ #
234
317
  # Note that this method actually runs the queries, since the results of some
235
318
  # are needed by the next ones when eager loading is going on.
236
319
  #
320
+ # To run EXPLAIN on queries created by +first+, +pluck+ and +count+, call
321
+ # these methods on +explain+:
322
+ #
323
+ # User.all.explain.count
324
+ # # EXPLAIN SELECT COUNT(*) FROM `users`
325
+ # # ...
326
+ #
327
+ # The column name can be passed if required:
328
+ #
329
+ # User.all.explain.maximum(:id)
330
+ # # EXPLAIN SELECT MAX(`users`.`id`) FROM `users`
331
+ # # ...
332
+ #
237
333
  # Please see further details in the
238
334
  # {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 })
335
+ def explain(*options)
336
+ ExplainProxy.new(self, options)
241
337
  end
242
338
 
243
339
  # Converts relation objects to Array.
@@ -267,6 +363,8 @@ module ActiveRecord
267
363
 
268
364
  # Returns true if there are no records.
269
365
  def empty?
366
+ return true if @none
367
+
270
368
  if loaded?
271
369
  records.empty?
272
370
  else
@@ -275,26 +373,49 @@ module ActiveRecord
275
373
  end
276
374
 
277
375
  # Returns true if there are no records.
278
- def none?
279
- return super if block_given?
376
+ #
377
+ # When a pattern argument is given, this method checks whether elements in
378
+ # the Enumerable match the pattern via the case-equality operator (<tt>===</tt>).
379
+ #
380
+ # posts.none?(Comment) # => true or false
381
+ def none?(*args)
382
+ return true if @none
383
+
384
+ return super if args.present? || block_given?
280
385
  empty?
281
386
  end
282
387
 
283
388
  # Returns true if there are any records.
284
- def any?
285
- return super if block_given?
389
+ #
390
+ # When a pattern argument is given, this method checks whether elements in
391
+ # the Enumerable match the pattern via the case-equality operator (<tt>===</tt>).
392
+ #
393
+ # posts.any?(Post) # => true or false
394
+ def any?(*args)
395
+ return false if @none
396
+
397
+ return super if args.present? || block_given?
286
398
  !empty?
287
399
  end
288
400
 
289
401
  # Returns true if there is exactly one record.
290
- def one?
291
- return super if block_given?
402
+ #
403
+ # When a pattern argument is given, this method checks whether elements in
404
+ # the Enumerable match the pattern via the case-equality operator (<tt>===</tt>).
405
+ #
406
+ # posts.one?(Post) # => true or false
407
+ def one?(*args)
408
+ return false if @none
409
+
410
+ return super if args.present? || block_given?
292
411
  return records.one? if loaded?
293
412
  limited_count == 1
294
413
  end
295
414
 
296
415
  # Returns true if there is more than one record.
297
416
  def many?
417
+ return false if @none
418
+
298
419
  return super if block_given?
299
420
  return records.many? if loaded?
300
421
  limited_count > 1
@@ -307,7 +428,7 @@ module ActiveRecord
307
428
  # # => "products/query-1850ab3d302391b85b8693e941286659"
308
429
  #
309
430
  # 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.
431
+ # in \Rails 6.0 and earlier, the cache key will also include a version.
311
432
  #
312
433
  # ActiveRecord::Base.collection_cache_versioning = false
313
434
  # Product.where("name like ?", "%Cosmic Encounter%").cache_key
@@ -362,28 +483,30 @@ module ActiveRecord
362
483
  else
363
484
  collection = eager_loading? ? apply_join_dependency : self
364
485
 
365
- column = connection.visitor.compile(table[timestamp_column])
366
- select_values = "COUNT(*) AS #{connection.quote_column_name("size")}, MAX(%s) AS timestamp"
486
+ with_connection do |c|
487
+ column = c.visitor.compile(table[timestamp_column])
488
+ select_values = "COUNT(*) AS #{adapter_class.quote_column_name("size")}, MAX(%s) AS timestamp"
367
489
 
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
490
+ if collection.has_limit_or_offset?
491
+ query = collection.select("#{column} AS collection_cache_key_timestamp")
492
+ query._select!(table[Arel.star]) if distinct_value && collection.select_values.empty?
493
+ subquery_alias = "subquery_for_cache_key"
494
+ subquery_column = "#{subquery_alias}.collection_cache_key_timestamp"
495
+ arel = query.build_subquery(subquery_alias, select_values % subquery_column)
496
+ else
497
+ query = collection.unscope(:order)
498
+ query.select_values = [select_values % column]
499
+ arel = query.arel
500
+ end
379
501
 
380
- size, timestamp = connection.select_rows(arel, nil).first
502
+ size, timestamp = c.select_rows(arel, nil).first
381
503
 
382
- if size
383
- column_type = klass.type_for_attribute(timestamp_column)
384
- timestamp = column_type.deserialize(timestamp)
385
- else
386
- size = 0
504
+ if size
505
+ column_type = klass.type_for_attribute(timestamp_column)
506
+ timestamp = column_type.deserialize(timestamp)
507
+ else
508
+ size = 0
509
+ end
387
510
  end
388
511
  end
389
512
 
@@ -409,7 +532,7 @@ module ActiveRecord
409
532
  # Comment.where(post_id: 1).scoping do
410
533
  # Comment.first
411
534
  # end
412
- # # => SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1
535
+ # # SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1
413
536
  #
414
537
  # If <tt>all_queries: true</tt> is passed, scoping will apply to all queries
415
538
  # for the relation including +update+ and +delete+ on instances.
@@ -446,7 +569,8 @@ module ActiveRecord
446
569
  #
447
570
  # ==== Parameters
448
571
  #
449
- # * +updates+ - A string, array, or hash representing the SET part of an SQL statement.
572
+ # * +updates+ - A string, array, or hash representing the SET part of an SQL statement. Any strings provided will
573
+ # be type cast, unless you use +Arel.sql+. (Don't pass user-provided values to +Arel.sql+.)
450
574
  #
451
575
  # ==== Examples
452
576
  #
@@ -461,9 +585,14 @@ module ActiveRecord
461
585
  #
462
586
  # # Update all invoices and set the number column to its id value.
463
587
  # Invoice.update_all('number = id')
588
+ #
589
+ # # Update all books with 'Rails' in their title
590
+ # Book.where('title LIKE ?', '%Rails%').update_all(title: Arel.sql("title + ' - volume 1'"))
464
591
  def update_all(updates)
465
592
  raise ArgumentError, "Empty list of attributes to change" if updates.blank?
466
593
 
594
+ return 0 if @none
595
+
467
596
  if updates.is_a?(Hash)
468
597
  if klass.locking_enabled? &&
469
598
  !updates.key?(klass.locking_column) &&
@@ -476,13 +605,20 @@ module ActiveRecord
476
605
  values = Arel.sql(klass.sanitize_sql_for_assignment(updates, table.name))
477
606
  end
478
607
 
479
- arel = eager_loading? ? apply_join_dependency.arel : build_arel
480
- arel.source.left = table
608
+ klass.with_connection do |c|
609
+ arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
610
+ arel.source.left = table
481
611
 
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 }
612
+ group_values_arel_columns = arel_columns(group_values.uniq)
613
+ having_clause_ast = having_clause.ast unless having_clause.empty?
614
+ key = if klass.composite_primary_key?
615
+ primary_key.map { |pk| table[pk] }
616
+ else
617
+ table[primary_key]
618
+ end
619
+ stmt = arel.compile_update(values, key, having_clause_ast, group_values_arel_columns)
620
+ c.update(stmt, "#{klass} Update All").tap { reset }
621
+ end
486
622
  end
487
623
 
488
624
  def update(id = :all, attributes) # :nodoc:
@@ -501,6 +637,283 @@ module ActiveRecord
501
637
  end
502
638
  end
503
639
 
640
+
641
+ # Inserts a single record into the database in a single SQL INSERT
642
+ # statement. It does not instantiate any models nor does it trigger
643
+ # Active Record callbacks or validations. Though passed values
644
+ # go through Active Record's type casting and serialization.
645
+ #
646
+ # See #insert_all for documentation.
647
+ def insert(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
648
+ insert_all([ attributes ], returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
649
+ end
650
+
651
+ # Inserts multiple records into the database in a single SQL INSERT
652
+ # statement. It does not instantiate any models nor does it trigger
653
+ # Active Record callbacks or validations. Though passed values
654
+ # go through Active Record's type casting and serialization.
655
+ #
656
+ # The +attributes+ parameter is an Array of Hashes. Every Hash determines
657
+ # the attributes for a single row and must have the same keys.
658
+ #
659
+ # Rows are considered to be unique by every unique index on the table. Any
660
+ # duplicate rows are skipped.
661
+ # Override with <tt>:unique_by</tt> (see below).
662
+ #
663
+ # Returns an ActiveRecord::Result with its contents based on
664
+ # <tt>:returning</tt> (see below).
665
+ #
666
+ # ==== Options
667
+ #
668
+ # [:returning]
669
+ # (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
670
+ # inserted records, which by default is the primary key.
671
+ # Pass <tt>returning: %w[ id name ]</tt> for both id and name
672
+ # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
673
+ # clause entirely.
674
+ #
675
+ # You can also pass an SQL string if you need more control on the return values
676
+ # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
677
+ #
678
+ # [:unique_by]
679
+ # (PostgreSQL and SQLite only) By default rows are considered to be unique
680
+ # by every unique index on the table. Any duplicate rows are skipped.
681
+ #
682
+ # To skip rows according to just one unique index pass <tt>:unique_by</tt>.
683
+ #
684
+ # Consider a Book model where no duplicate ISBNs make sense, but if any
685
+ # row has an existing id, or is not unique by another unique index,
686
+ # ActiveRecord::RecordNotUnique is raised.
687
+ #
688
+ # Unique indexes can be identified by columns or name:
689
+ #
690
+ # unique_by: :isbn
691
+ # unique_by: %i[ author_id name ]
692
+ # unique_by: :index_books_on_isbn
693
+ #
694
+ # [:record_timestamps]
695
+ # By default, automatic setting of timestamp columns is controlled by
696
+ # the model's <tt>record_timestamps</tt> config, matching typical
697
+ # behavior.
698
+ #
699
+ # To override this and force automatic setting of timestamp columns one
700
+ # way or the other, pass <tt>:record_timestamps</tt>:
701
+ #
702
+ # record_timestamps: true # Always set timestamps automatically
703
+ # record_timestamps: false # Never set timestamps automatically
704
+ #
705
+ # Because it relies on the index information from the database
706
+ # <tt>:unique_by</tt> is recommended to be paired with
707
+ # Active Record's schema_cache.
708
+ #
709
+ # ==== Example
710
+ #
711
+ # # Insert records and skip inserting any duplicates.
712
+ # # Here "Eloquent Ruby" is skipped because its id is not unique.
713
+ #
714
+ # Book.insert_all([
715
+ # { id: 1, title: "Rework", author: "David" },
716
+ # { id: 1, title: "Eloquent Ruby", author: "Russ" }
717
+ # ])
718
+ #
719
+ # # insert_all works on chained scopes, and you can use create_with
720
+ # # to set default attributes for all inserted records.
721
+ #
722
+ # author.books.create_with(created_at: Time.now).insert_all([
723
+ # { id: 1, title: "Rework" },
724
+ # { id: 2, title: "Eloquent Ruby" }
725
+ # ])
726
+ def insert_all(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
727
+ InsertAll.execute(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
728
+ end
729
+
730
+ # Inserts a single record into the database in a single SQL INSERT
731
+ # statement. It does not instantiate any models nor does it trigger
732
+ # Active Record callbacks or validations. Though passed values
733
+ # go through Active Record's type casting and serialization.
734
+ #
735
+ # See #insert_all! for more.
736
+ def insert!(attributes, returning: nil, record_timestamps: nil)
737
+ insert_all!([ attributes ], returning: returning, record_timestamps: record_timestamps)
738
+ end
739
+
740
+ # Inserts multiple records into the database in a single SQL INSERT
741
+ # statement. It does not instantiate any models nor does it trigger
742
+ # Active Record callbacks or validations. Though passed values
743
+ # go through Active Record's type casting and serialization.
744
+ #
745
+ # The +attributes+ parameter is an Array of Hashes. Every Hash determines
746
+ # the attributes for a single row and must have the same keys.
747
+ #
748
+ # Raises ActiveRecord::RecordNotUnique if any rows violate a
749
+ # unique index on the table. In that case, no rows are inserted.
750
+ #
751
+ # To skip duplicate rows, see #insert_all. To replace them, see #upsert_all.
752
+ #
753
+ # Returns an ActiveRecord::Result with its contents based on
754
+ # <tt>:returning</tt> (see below).
755
+ #
756
+ # ==== Options
757
+ #
758
+ # [:returning]
759
+ # (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
760
+ # inserted records, which by default is the primary key.
761
+ # Pass <tt>returning: %w[ id name ]</tt> for both id and name
762
+ # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
763
+ # clause entirely.
764
+ #
765
+ # You can also pass an SQL string if you need more control on the return values
766
+ # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
767
+ #
768
+ # [:record_timestamps]
769
+ # By default, automatic setting of timestamp columns is controlled by
770
+ # the model's <tt>record_timestamps</tt> config, matching typical
771
+ # behavior.
772
+ #
773
+ # To override this and force automatic setting of timestamp columns one
774
+ # way or the other, pass <tt>:record_timestamps</tt>:
775
+ #
776
+ # record_timestamps: true # Always set timestamps automatically
777
+ # record_timestamps: false # Never set timestamps automatically
778
+ #
779
+ # ==== Examples
780
+ #
781
+ # # Insert multiple records
782
+ # Book.insert_all!([
783
+ # { title: "Rework", author: "David" },
784
+ # { title: "Eloquent Ruby", author: "Russ" }
785
+ # ])
786
+ #
787
+ # # Raises ActiveRecord::RecordNotUnique because "Eloquent Ruby"
788
+ # # does not have a unique id.
789
+ # Book.insert_all!([
790
+ # { id: 1, title: "Rework", author: "David" },
791
+ # { id: 1, title: "Eloquent Ruby", author: "Russ" }
792
+ # ])
793
+ def insert_all!(attributes, returning: nil, record_timestamps: nil)
794
+ InsertAll.execute(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps)
795
+ end
796
+
797
+ # Updates or inserts (upserts) a single record into the database in a
798
+ # single SQL INSERT statement. It does not instantiate any models nor does
799
+ # it trigger Active Record callbacks or validations. Though passed values
800
+ # go through Active Record's type casting and serialization.
801
+ #
802
+ # See #upsert_all for documentation.
803
+ def upsert(attributes, **kwargs)
804
+ upsert_all([ attributes ], **kwargs)
805
+ end
806
+
807
+ # Updates or inserts (upserts) multiple records into the database in a
808
+ # single SQL INSERT statement. It does not instantiate any models nor does
809
+ # it trigger Active Record callbacks or validations. Though passed values
810
+ # go through Active Record's type casting and serialization.
811
+ #
812
+ # The +attributes+ parameter is an Array of Hashes. Every Hash determines
813
+ # the attributes for a single row and must have the same keys.
814
+ #
815
+ # Returns an ActiveRecord::Result with its contents based on
816
+ # <tt>:returning</tt> (see below).
817
+ #
818
+ # By default, +upsert_all+ will update all the columns that can be updated when
819
+ # there is a conflict. These are all the columns except primary keys, read-only
820
+ # columns, and columns covered by the optional +unique_by+.
821
+ #
822
+ # ==== Options
823
+ #
824
+ # [:returning]
825
+ # (PostgreSQL, SQLite3, and MariaDB only) An array of attributes to return for all successfully
826
+ # upserted records, which by default is the primary key.
827
+ # Pass <tt>returning: %w[ id name ]</tt> for both id and name
828
+ # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
829
+ # clause entirely.
830
+ #
831
+ # You can also pass an SQL string if you need more control on the return values
832
+ # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
833
+ #
834
+ # [:unique_by]
835
+ # (PostgreSQL and SQLite only) By default rows are considered to be unique
836
+ # by every unique index on the table. Any duplicate rows are skipped.
837
+ #
838
+ # To skip rows according to just one unique index pass <tt>:unique_by</tt>.
839
+ #
840
+ # Consider a Book model where no duplicate ISBNs make sense, but if any
841
+ # row has an existing id, or is not unique by another unique index,
842
+ # ActiveRecord::RecordNotUnique is raised.
843
+ #
844
+ # Unique indexes can be identified by columns or name:
845
+ #
846
+ # unique_by: :isbn
847
+ # unique_by: %i[ author_id name ]
848
+ # unique_by: :index_books_on_isbn
849
+ #
850
+ # Because it relies on the index information from the database
851
+ # <tt>:unique_by</tt> is recommended to be paired with
852
+ # Active Record's schema_cache.
853
+ #
854
+ # [:on_duplicate]
855
+ # Configure the SQL update sentence that will be used in case of conflict.
856
+ #
857
+ # NOTE: If you use this option you must provide all the columns you want to update
858
+ # by yourself.
859
+ #
860
+ # Example:
861
+ #
862
+ # Commodity.upsert_all(
863
+ # [
864
+ # { id: 2, name: "Copper", price: 4.84 },
865
+ # { id: 4, name: "Gold", price: 1380.87 },
866
+ # { id: 6, name: "Aluminium", price: 0.35 }
867
+ # ],
868
+ # on_duplicate: Arel.sql("price = GREATEST(commodities.price, EXCLUDED.price)")
869
+ # )
870
+ #
871
+ # See the related +:update_only+ option. Both options can't be used at the same time.
872
+ #
873
+ # [:update_only]
874
+ # Provide a list of column names that will be updated in case of conflict. If not provided,
875
+ # +upsert_all+ will update all the columns that can be updated. These are all the columns
876
+ # except primary keys, read-only columns, and columns covered by the optional +unique_by+
877
+ #
878
+ # Example:
879
+ #
880
+ # Commodity.upsert_all(
881
+ # [
882
+ # { id: 2, name: "Copper", price: 4.84 },
883
+ # { id: 4, name: "Gold", price: 1380.87 },
884
+ # { id: 6, name: "Aluminium", price: 0.35 }
885
+ # ],
886
+ # update_only: [:price] # Only prices will be updated
887
+ # )
888
+ #
889
+ # See the related +:on_duplicate+ option. Both options can't be used at the same time.
890
+ #
891
+ # [:record_timestamps]
892
+ # By default, automatic setting of timestamp columns is controlled by
893
+ # the model's <tt>record_timestamps</tt> config, matching typical
894
+ # behavior.
895
+ #
896
+ # To override this and force automatic setting of timestamp columns one
897
+ # way or the other, pass <tt>:record_timestamps</tt>:
898
+ #
899
+ # record_timestamps: true # Always set timestamps automatically
900
+ # record_timestamps: false # Never set timestamps automatically
901
+ #
902
+ # ==== Examples
903
+ #
904
+ # # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
905
+ # # Here "Eloquent Ruby" overwrites "Rework" because its ISBN is duplicate.
906
+ #
907
+ # Book.upsert_all([
908
+ # { title: "Rework", author: "David", isbn: "1" },
909
+ # { title: "Eloquent Ruby", author: "Russ", isbn: "1" }
910
+ # ], unique_by: :isbn)
911
+ #
912
+ # Book.find_by(isbn: "1").title # => "Eloquent Ruby"
913
+ def upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
914
+ InsertAll.execute(self, attributes, on_duplicate: on_duplicate, update_only: update_only, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
915
+ end
916
+
504
917
  # Updates the counters of the records in the current relation.
505
918
  #
506
919
  # ==== Parameters
@@ -539,7 +952,7 @@ module ActiveRecord
539
952
  # If attribute names are passed, they are updated along with +updated_at+/+updated_on+ attributes.
540
953
  # If no time argument is passed, the current time is used as default.
541
954
  #
542
- # === Examples
955
+ # ==== Examples
543
956
  #
544
957
  # # Touch all records
545
958
  # Person.all.touch_all
@@ -599,6 +1012,8 @@ module ActiveRecord
599
1012
  # Post.distinct.delete_all
600
1013
  # # => ActiveRecord::ActiveRecordError: delete_all doesn't support distinct
601
1014
  def delete_all
1015
+ return 0 if @none
1016
+
602
1017
  invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select do |method|
603
1018
  value = @values[method]
604
1019
  method == :distinct ? value : value&.any?
@@ -607,14 +1022,79 @@ module ActiveRecord
607
1022
  raise ActiveRecordError.new("delete_all doesn't support #{invalid_methods.join(', ')}")
608
1023
  end
609
1024
 
610
- arel = eager_loading? ? apply_join_dependency.arel : build_arel
611
- arel.source.left = table
1025
+ klass.with_connection do |c|
1026
+ arel = eager_loading? ? apply_join_dependency.arel : build_arel(c)
1027
+ arel.source.left = table
1028
+
1029
+ group_values_arel_columns = arel_columns(group_values.uniq)
1030
+ having_clause_ast = having_clause.ast unless having_clause.empty?
1031
+ key = if klass.composite_primary_key?
1032
+ primary_key.map { |pk| table[pk] }
1033
+ else
1034
+ table[primary_key]
1035
+ end
1036
+ stmt = arel.compile_delete(key, having_clause_ast, group_values_arel_columns)
1037
+
1038
+ c.delete(stmt, "#{klass} Delete All").tap { reset }
1039
+ end
1040
+ end
612
1041
 
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)
1042
+ # Deletes the row with a primary key matching the +id+ argument, using an
1043
+ # SQL +DELETE+ statement, and returns the number of rows deleted. Active
1044
+ # Record objects are not instantiated, so the object's callbacks are not
1045
+ # executed, including any <tt>:dependent</tt> association options.
1046
+ #
1047
+ # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
1048
+ #
1049
+ # Note: Although it is often much faster than the alternative, #destroy,
1050
+ # skipping callbacks might bypass business logic in your application
1051
+ # that ensures referential integrity or performs other essential jobs.
1052
+ #
1053
+ # ==== Examples
1054
+ #
1055
+ # # Delete a single row
1056
+ # Todo.delete(1)
1057
+ #
1058
+ # # Delete multiple rows
1059
+ # Todo.delete([2,3,4])
1060
+ def delete(id_or_array)
1061
+ return 0 if id_or_array.nil? || (id_or_array.is_a?(Array) && id_or_array.empty?)
616
1062
 
617
- klass.connection.delete(stmt, "#{klass} Delete All").tap { reset }
1063
+ where(model.primary_key => id_or_array).delete_all
1064
+ end
1065
+
1066
+
1067
+ # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
1068
+ # therefore all callbacks and filters are fired off before the object is deleted. This method is
1069
+ # less efficient than #delete but allows cleanup methods and other actions to be run.
1070
+ #
1071
+ # This essentially finds the object (or multiple objects) with the given id, creates a new object
1072
+ # from the attributes, and then calls destroy on it.
1073
+ #
1074
+ # ==== Parameters
1075
+ #
1076
+ # * +id+ - This should be the id or an array of ids to be destroyed.
1077
+ #
1078
+ # ==== Examples
1079
+ #
1080
+ # # Destroy a single object
1081
+ # Todo.destroy(1)
1082
+ #
1083
+ # # Destroy multiple objects
1084
+ # todos = [1,2,3]
1085
+ # Todo.destroy(todos)
1086
+ def destroy(id)
1087
+ multiple_ids = if model.composite_primary_key?
1088
+ id.first.is_a?(Array)
1089
+ else
1090
+ id.is_a?(Array)
1091
+ end
1092
+
1093
+ if multiple_ids
1094
+ find(id).each(&:destroy)
1095
+ else
1096
+ find(id).destroy
1097
+ end
618
1098
  end
619
1099
 
620
1100
  # Finds and destroys all records matching the specified conditions.
@@ -662,17 +1142,19 @@ module ActiveRecord
662
1142
  #
663
1143
  # ASYNC Post Load (0.0ms) (db time 2ms) SELECT "posts".* FROM "posts" LIMIT 100
664
1144
  def load_async
665
- return load if !connection.async_enabled?
1145
+ with_connection do |c|
1146
+ return load if !c.async_enabled?
666
1147
 
667
- unless loaded?
668
- result = exec_main_query(async: connection.current_transaction.closed?)
1148
+ unless loaded?
1149
+ result = exec_main_query(async: c.current_transaction.closed?)
669
1150
 
670
- if result.is_a?(Array)
671
- @records = result
672
- else
673
- @future_result = result
1151
+ if result.is_a?(Array)
1152
+ @records = result
1153
+ else
1154
+ @future_result = result
1155
+ end
1156
+ @loaded = true
674
1157
  end
675
- @loaded = true
676
1158
  end
677
1159
 
678
1160
  self
@@ -720,7 +1202,7 @@ module ActiveRecord
720
1202
  # Returns sql statement for the relation.
721
1203
  #
722
1204
  # User.where(name: 'Oscar').to_sql
723
- # # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
1205
+ # # SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
724
1206
  def to_sql
725
1207
  @to_sql ||= if eager_loading?
726
1208
  apply_join_dependency do |relation, join_dependency|
@@ -728,8 +1210,9 @@ module ActiveRecord
728
1210
  relation.to_sql
729
1211
  end
730
1212
  else
731
- conn = klass.connection
732
- conn.unprepared_statement { conn.to_sql(arel) }
1213
+ klass.with_connection do |conn|
1214
+ conn.unprepared_statement { conn.to_sql(arel) }
1215
+ end
733
1216
  end
734
1217
  end
735
1218
 
@@ -774,8 +1257,13 @@ module ActiveRecord
774
1257
  end
775
1258
  end
776
1259
 
777
- def pretty_print(q)
778
- q.pp(records)
1260
+ def pretty_print(pp)
1261
+ subject = loaded? ? records : annotate("loading for pp")
1262
+ entries = subject.take([limit_value, 11].compact.min)
1263
+
1264
+ entries[10] = "..." if entries.size == 11
1265
+
1266
+ pp.pp(entries)
779
1267
  end
780
1268
 
781
1269
  # Returns true if relation is blank.
@@ -809,7 +1297,7 @@ module ActiveRecord
809
1297
  end
810
1298
 
811
1299
  def alias_tracker(joins = [], aliases = nil) # :nodoc:
812
- ActiveRecord::Associations::AliasTracker.create(connection, table.name, joins, aliases)
1300
+ ActiveRecord::Associations::AliasTracker.create(connection_pool, table.name, joins, aliases)
813
1301
  end
814
1302
 
815
1303
  class StrictLoadingScope # :nodoc:
@@ -837,10 +1325,6 @@ module ActiveRecord
837
1325
  @loaded = true
838
1326
  end
839
1327
 
840
- def null_relation? # :nodoc:
841
- is_a?(NullRelation)
842
- end
843
-
844
1328
  private
845
1329
  def already_in_scope?(registry)
846
1330
  @delegate_to_klass && registry.current_scope(klass, true)
@@ -889,7 +1373,11 @@ module ActiveRecord
889
1373
  def _substitute_values(values)
890
1374
  values.map do |name, value|
891
1375
  attr = table[name]
892
- unless Arel.arel_node?(value)
1376
+ if Arel.arel_node?(value)
1377
+ if value.is_a?(Arel::Nodes::SqlLiteral)
1378
+ value = Arel::Nodes::Grouping.new(value)
1379
+ end
1380
+ else
893
1381
  type = klass.type_for_attribute(attr.name)
894
1382
  value = predicate_builder.build_bind_attribute(attr.name, type.cast(value))
895
1383
  end
@@ -925,21 +1413,33 @@ module ActiveRecord
925
1413
  end
926
1414
 
927
1415
  def exec_main_query(async: false)
1416
+ if @none
1417
+ if async
1418
+ return FutureResult.wrap([])
1419
+ else
1420
+ return []
1421
+ end
1422
+ end
1423
+
928
1424
  skip_query_cache_if_necessary do
929
1425
  if where_clause.contradiction?
930
1426
  [].freeze
931
1427
  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)
1428
+ klass.with_connection do |c|
1429
+ apply_join_dependency do |relation, join_dependency|
1430
+ if relation.null_relation?
1431
+ [].freeze
1432
+ else
1433
+ relation = join_dependency.apply_column_aliases(relation)
1434
+ @_join_dependency = join_dependency
1435
+ c.select_all(relation.arel, "SQL", async: async)
1436
+ end
939
1437
  end
940
1438
  end
941
1439
  else
942
- klass._query_by_sql(arel, async: async)
1440
+ klass.with_connection do |c|
1441
+ klass._query_by_sql(c, arel, async: async)
1442
+ end
943
1443
  end
944
1444
  end
945
1445
  end