activerecord 7.1.3.4 → 7.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (186) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +507 -2133
  3. data/README.rdoc +15 -15
  4. data/examples/performance.rb +2 -2
  5. data/lib/active_record/association_relation.rb +1 -1
  6. data/lib/active_record/associations/alias_tracker.rb +25 -19
  7. data/lib/active_record/associations/association.rb +9 -8
  8. data/lib/active_record/associations/belongs_to_association.rb +18 -11
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  10. data/lib/active_record/associations/builder/belongs_to.rb +1 -0
  11. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
  12. data/lib/active_record/associations/builder/has_many.rb +3 -4
  13. data/lib/active_record/associations/builder/has_one.rb +3 -4
  14. data/lib/active_record/associations/collection_association.rb +4 -2
  15. data/lib/active_record/associations/collection_proxy.rb +14 -1
  16. data/lib/active_record/associations/has_many_association.rb +3 -3
  17. data/lib/active_record/associations/has_one_association.rb +2 -2
  18. data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
  19. data/lib/active_record/associations/join_dependency.rb +5 -7
  20. data/lib/active_record/associations/nested_error.rb +47 -0
  21. data/lib/active_record/associations/preloader/association.rb +2 -1
  22. data/lib/active_record/associations/preloader/branch.rb +7 -1
  23. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  24. data/lib/active_record/associations/singular_association.rb +6 -0
  25. data/lib/active_record/associations/through_association.rb +1 -1
  26. data/lib/active_record/associations.rb +34 -11
  27. data/lib/active_record/attribute_assignment.rb +1 -11
  28. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  29. data/lib/active_record/attribute_methods/dirty.rb +1 -1
  30. data/lib/active_record/attribute_methods/primary_key.rb +23 -55
  31. data/lib/active_record/attribute_methods/read.rb +1 -13
  32. data/lib/active_record/attribute_methods/serialization.rb +4 -24
  33. data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -6
  34. data/lib/active_record/attribute_methods.rb +87 -58
  35. data/lib/active_record/attributes.rb +55 -42
  36. data/lib/active_record/autosave_association.rb +14 -30
  37. data/lib/active_record/base.rb +2 -3
  38. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +24 -107
  39. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
  40. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +248 -58
  41. data/lib/active_record/connection_adapters/abstract/database_statements.rb +35 -18
  42. data/lib/active_record/connection_adapters/abstract/query_cache.rb +160 -75
  43. data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
  44. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
  45. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +22 -9
  46. data/lib/active_record/connection_adapters/abstract/transaction.rb +60 -57
  47. data/lib/active_record/connection_adapters/abstract_adapter.rb +32 -61
  48. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +69 -19
  49. data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
  50. data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
  51. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -0
  52. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +11 -5
  53. data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -32
  54. data/lib/active_record/connection_adapters/pool_config.rb +7 -6
  55. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
  56. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  57. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  58. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  59. data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
  60. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -0
  61. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +16 -12
  62. data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -21
  63. data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
  64. data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
  65. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
  66. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
  67. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  68. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
  69. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  70. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
  71. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +109 -77
  72. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +12 -6
  73. data/lib/active_record/connection_adapters/trilogy_adapter.rb +32 -65
  74. data/lib/active_record/connection_adapters.rb +121 -0
  75. data/lib/active_record/connection_handling.rb +56 -41
  76. data/lib/active_record/core.rb +59 -38
  77. data/lib/active_record/counter_cache.rb +23 -10
  78. data/lib/active_record/database_configurations/connection_url_resolver.rb +7 -2
  79. data/lib/active_record/database_configurations/database_config.rb +15 -4
  80. data/lib/active_record/database_configurations/hash_config.rb +44 -36
  81. data/lib/active_record/database_configurations/url_config.rb +20 -1
  82. data/lib/active_record/database_configurations.rb +1 -1
  83. data/lib/active_record/delegated_type.rb +30 -6
  84. data/lib/active_record/destroy_association_async_job.rb +1 -1
  85. data/lib/active_record/dynamic_matchers.rb +2 -2
  86. data/lib/active_record/encryption/encryptable_record.rb +2 -2
  87. data/lib/active_record/encryption/encrypted_attribute_type.rb +24 -4
  88. data/lib/active_record/encryption/encryptor.rb +17 -2
  89. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  90. data/lib/active_record/encryption/message_serializer.rb +4 -0
  91. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  92. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  93. data/lib/active_record/encryption/scheme.rb +8 -4
  94. data/lib/active_record/enum.rb +11 -2
  95. data/lib/active_record/errors.rb +16 -11
  96. data/lib/active_record/explain.rb +13 -24
  97. data/lib/active_record/fixtures.rb +37 -31
  98. data/lib/active_record/future_result.rb +17 -4
  99. data/lib/active_record/gem_version.rb +3 -3
  100. data/lib/active_record/inheritance.rb +4 -2
  101. data/lib/active_record/insert_all.rb +18 -15
  102. data/lib/active_record/integration.rb +4 -1
  103. data/lib/active_record/internal_metadata.rb +48 -34
  104. data/lib/active_record/locking/optimistic.rb +8 -7
  105. data/lib/active_record/log_subscriber.rb +0 -21
  106. data/lib/active_record/marshalling.rb +1 -1
  107. data/lib/active_record/message_pack.rb +2 -2
  108. data/lib/active_record/migration/command_recorder.rb +2 -3
  109. data/lib/active_record/migration/compatibility.rb +11 -3
  110. data/lib/active_record/migration/default_strategy.rb +4 -5
  111. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  112. data/lib/active_record/migration.rb +85 -76
  113. data/lib/active_record/model_schema.rb +34 -69
  114. data/lib/active_record/nested_attributes.rb +11 -3
  115. data/lib/active_record/normalization.rb +3 -7
  116. data/lib/active_record/persistence.rb +32 -354
  117. data/lib/active_record/query_cache.rb +18 -6
  118. data/lib/active_record/query_logs.rb +15 -0
  119. data/lib/active_record/query_logs_formatter.rb +1 -1
  120. data/lib/active_record/querying.rb +21 -9
  121. data/lib/active_record/railtie.rb +52 -64
  122. data/lib/active_record/railties/controller_runtime.rb +13 -4
  123. data/lib/active_record/railties/databases.rake +41 -44
  124. data/lib/active_record/reflection.rb +98 -37
  125. data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
  126. data/lib/active_record/relation/batches.rb +3 -3
  127. data/lib/active_record/relation/calculations.rb +94 -61
  128. data/lib/active_record/relation/delegation.rb +8 -11
  129. data/lib/active_record/relation/finder_methods.rb +16 -2
  130. data/lib/active_record/relation/merger.rb +4 -6
  131. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  132. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +6 -1
  133. data/lib/active_record/relation/predicate_builder.rb +3 -3
  134. data/lib/active_record/relation/query_methods.rb +196 -43
  135. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  136. data/lib/active_record/relation/spawn_methods.rb +2 -18
  137. data/lib/active_record/relation/where_clause.rb +7 -19
  138. data/lib/active_record/relation.rb +500 -66
  139. data/lib/active_record/result.rb +32 -45
  140. data/lib/active_record/runtime_registry.rb +39 -0
  141. data/lib/active_record/sanitization.rb +24 -19
  142. data/lib/active_record/schema.rb +8 -6
  143. data/lib/active_record/schema_dumper.rb +19 -9
  144. data/lib/active_record/schema_migration.rb +30 -14
  145. data/lib/active_record/signed_id.rb +11 -1
  146. data/lib/active_record/statement_cache.rb +7 -7
  147. data/lib/active_record/table_metadata.rb +1 -10
  148. data/lib/active_record/tasks/database_tasks.rb +70 -42
  149. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  150. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  151. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
  152. data/lib/active_record/test_fixtures.rb +82 -91
  153. data/lib/active_record/testing/query_assertions.rb +121 -0
  154. data/lib/active_record/timestamp.rb +4 -2
  155. data/lib/active_record/token_for.rb +22 -12
  156. data/lib/active_record/touch_later.rb +1 -1
  157. data/lib/active_record/transaction.rb +68 -0
  158. data/lib/active_record/transactions.rb +43 -14
  159. data/lib/active_record/translation.rb +0 -2
  160. data/lib/active_record/type/serialized.rb +1 -3
  161. data/lib/active_record/type_caster/connection.rb +4 -4
  162. data/lib/active_record/validations/associated.rb +9 -3
  163. data/lib/active_record/validations/uniqueness.rb +14 -10
  164. data/lib/active_record/validations.rb +4 -1
  165. data/lib/active_record.rb +149 -40
  166. data/lib/arel/alias_predication.rb +1 -1
  167. data/lib/arel/collectors/bind.rb +2 -0
  168. data/lib/arel/collectors/composite.rb +7 -0
  169. data/lib/arel/collectors/sql_string.rb +1 -1
  170. data/lib/arel/collectors/substitute_binds.rb +1 -1
  171. data/lib/arel/nodes/binary.rb +0 -6
  172. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  173. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  174. data/lib/arel/nodes/node.rb +4 -3
  175. data/lib/arel/nodes/sql_literal.rb +7 -0
  176. data/lib/arel/nodes.rb +2 -2
  177. data/lib/arel/predications.rb +1 -1
  178. data/lib/arel/select_manager.rb +1 -1
  179. data/lib/arel/tree_manager.rb +8 -3
  180. data/lib/arel/update_manager.rb +2 -1
  181. data/lib/arel/visitors/dot.rb +1 -0
  182. data/lib/arel/visitors/mysql.rb +9 -4
  183. data/lib/arel/visitors/postgresql.rb +1 -12
  184. data/lib/arel/visitors/to_sql.rb +31 -17
  185. data/lib/arel.rb +7 -3
  186. metadata +17 -12
@@ -86,10 +86,8 @@ module ActiveRecord # :nodoc:
86
86
  #
87
87
  # User.normalize_value_for(:phone, "+1 (555) 867-5309") # => "5558675309"
88
88
  def normalizes(*names, with:, apply_to_nil: false)
89
- names.each do |name|
90
- attribute(name) do |cast_type|
91
- NormalizedValueType.new(cast_type: cast_type, normalizer: with, normalize_nil: apply_to_nil)
92
- end
89
+ decorate_attributes(names) do |name, cast_type|
90
+ NormalizedValueType.new(cast_type: cast_type, normalizer: with, normalize_nil: apply_to_nil)
93
91
  end
94
92
 
95
93
  self.normalized_attributes += names.map(&:to_sym)
@@ -154,9 +152,7 @@ module ActiveRecord # :nodoc:
154
152
  [self.class, cast_type, normalizer, normalize_nil?].hash
155
153
  end
156
154
 
157
- def inspect
158
- Kernel.instance_method(:inspect).bind_call(self)
159
- end
155
+ define_method(:inspect, Kernel.instance_method(:inspect))
160
156
 
161
157
  private
162
158
  def normalize(value)
@@ -87,282 +87,6 @@ module ActiveRecord
87
87
  end
88
88
  end
89
89
 
90
- # Inserts a single record into the database in a single SQL INSERT
91
- # statement. It does not instantiate any models nor does it trigger
92
- # Active Record callbacks or validations. Though passed values
93
- # go through Active Record's type casting and serialization.
94
- #
95
- # See #insert_all for documentation.
96
- def insert(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
97
- insert_all([ attributes ], returning: returning, unique_by: unique_by, record_timestamps: record_timestamps)
98
- end
99
-
100
- # Inserts multiple records into the database in a single SQL INSERT
101
- # statement. It does not instantiate any models nor does it trigger
102
- # Active Record callbacks or validations. Though passed values
103
- # go through Active Record's type casting and serialization.
104
- #
105
- # The +attributes+ parameter is an Array of Hashes. Every Hash determines
106
- # the attributes for a single row and must have the same keys.
107
- #
108
- # Rows are considered to be unique by every unique index on the table. Any
109
- # duplicate rows are skipped.
110
- # Override with <tt>:unique_by</tt> (see below).
111
- #
112
- # Returns an ActiveRecord::Result with its contents based on
113
- # <tt>:returning</tt> (see below).
114
- #
115
- # ==== Options
116
- #
117
- # [:returning]
118
- # (PostgreSQL and SQLite3 only) An array of attributes to return for all successfully
119
- # inserted records, which by default is the primary key.
120
- # Pass <tt>returning: %w[ id name ]</tt> for both id and name
121
- # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
122
- # clause entirely.
123
- #
124
- # You can also pass an SQL string if you need more control on the return values
125
- # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
126
- #
127
- # [:unique_by]
128
- # (PostgreSQL and SQLite only) By default rows are considered to be unique
129
- # by every unique index on the table. Any duplicate rows are skipped.
130
- #
131
- # To skip rows according to just one unique index pass <tt>:unique_by</tt>.
132
- #
133
- # Consider a Book model where no duplicate ISBNs make sense, but if any
134
- # row has an existing id, or is not unique by another unique index,
135
- # ActiveRecord::RecordNotUnique is raised.
136
- #
137
- # Unique indexes can be identified by columns or name:
138
- #
139
- # unique_by: :isbn
140
- # unique_by: %i[ author_id name ]
141
- # unique_by: :index_books_on_isbn
142
- #
143
- # [:record_timestamps]
144
- # By default, automatic setting of timestamp columns is controlled by
145
- # the model's <tt>record_timestamps</tt> config, matching typical
146
- # behavior.
147
- #
148
- # To override this and force automatic setting of timestamp columns one
149
- # way or the other, pass <tt>:record_timestamps</tt>:
150
- #
151
- # record_timestamps: true # Always set timestamps automatically
152
- # record_timestamps: false # Never set timestamps automatically
153
- #
154
- # Because it relies on the index information from the database
155
- # <tt>:unique_by</tt> is recommended to be paired with
156
- # Active Record's schema_cache.
157
- #
158
- # ==== Example
159
- #
160
- # # Insert records and skip inserting any duplicates.
161
- # # Here "Eloquent Ruby" is skipped because its id is not unique.
162
- #
163
- # Book.insert_all([
164
- # { id: 1, title: "Rework", author: "David" },
165
- # { id: 1, title: "Eloquent Ruby", author: "Russ" }
166
- # ])
167
- #
168
- # # insert_all works on chained scopes, and you can use create_with
169
- # # to set default attributes for all inserted records.
170
- #
171
- # author.books.create_with(created_at: Time.now).insert_all([
172
- # { id: 1, title: "Rework" },
173
- # { id: 2, title: "Eloquent Ruby" }
174
- # ])
175
- def insert_all(attributes, returning: nil, unique_by: nil, record_timestamps: nil)
176
- InsertAll.new(self, attributes, on_duplicate: :skip, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).execute
177
- end
178
-
179
- # Inserts a single record into the database in a single SQL INSERT
180
- # statement. It does not instantiate any models nor does it trigger
181
- # Active Record callbacks or validations. Though passed values
182
- # go through Active Record's type casting and serialization.
183
- #
184
- # See #insert_all! for more.
185
- def insert!(attributes, returning: nil, record_timestamps: nil)
186
- insert_all!([ attributes ], returning: returning, record_timestamps: record_timestamps)
187
- end
188
-
189
- # Inserts multiple records into the database in a single SQL INSERT
190
- # statement. It does not instantiate any models nor does it trigger
191
- # Active Record callbacks or validations. Though passed values
192
- # go through Active Record's type casting and serialization.
193
- #
194
- # The +attributes+ parameter is an Array of Hashes. Every Hash determines
195
- # the attributes for a single row and must have the same keys.
196
- #
197
- # Raises ActiveRecord::RecordNotUnique if any rows violate a
198
- # unique index on the table. In that case, no rows are inserted.
199
- #
200
- # To skip duplicate rows, see #insert_all. To replace them, see #upsert_all.
201
- #
202
- # Returns an ActiveRecord::Result with its contents based on
203
- # <tt>:returning</tt> (see below).
204
- #
205
- # ==== Options
206
- #
207
- # [:returning]
208
- # (PostgreSQL and SQLite3 only) An array of attributes to return for all successfully
209
- # inserted records, which by default is the primary key.
210
- # Pass <tt>returning: %w[ id name ]</tt> for both id and name
211
- # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
212
- # clause entirely.
213
- #
214
- # You can also pass an SQL string if you need more control on the return values
215
- # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
216
- #
217
- # [:record_timestamps]
218
- # By default, automatic setting of timestamp columns is controlled by
219
- # the model's <tt>record_timestamps</tt> config, matching typical
220
- # behavior.
221
- #
222
- # To override this and force automatic setting of timestamp columns one
223
- # way or the other, pass <tt>:record_timestamps</tt>:
224
- #
225
- # record_timestamps: true # Always set timestamps automatically
226
- # record_timestamps: false # Never set timestamps automatically
227
- #
228
- # ==== Examples
229
- #
230
- # # Insert multiple records
231
- # Book.insert_all!([
232
- # { title: "Rework", author: "David" },
233
- # { title: "Eloquent Ruby", author: "Russ" }
234
- # ])
235
- #
236
- # # Raises ActiveRecord::RecordNotUnique because "Eloquent Ruby"
237
- # # does not have a unique id.
238
- # Book.insert_all!([
239
- # { id: 1, title: "Rework", author: "David" },
240
- # { id: 1, title: "Eloquent Ruby", author: "Russ" }
241
- # ])
242
- def insert_all!(attributes, returning: nil, record_timestamps: nil)
243
- InsertAll.new(self, attributes, on_duplicate: :raise, returning: returning, record_timestamps: record_timestamps).execute
244
- end
245
-
246
- # Updates or inserts (upserts) a single record into the database in a
247
- # single SQL INSERT statement. It does not instantiate any models nor does
248
- # it trigger Active Record callbacks or validations. Though passed values
249
- # go through Active Record's type casting and serialization.
250
- #
251
- # See #upsert_all for documentation.
252
- def upsert(attributes, **kwargs)
253
- upsert_all([ attributes ], **kwargs)
254
- end
255
-
256
- # Updates or inserts (upserts) multiple records into the database in a
257
- # single SQL INSERT statement. It does not instantiate any models nor does
258
- # it trigger Active Record callbacks or validations. Though passed values
259
- # go through Active Record's type casting and serialization.
260
- #
261
- # The +attributes+ parameter is an Array of Hashes. Every Hash determines
262
- # the attributes for a single row and must have the same keys.
263
- #
264
- # Returns an ActiveRecord::Result with its contents based on
265
- # <tt>:returning</tt> (see below).
266
- #
267
- # By default, +upsert_all+ will update all the columns that can be updated when
268
- # there is a conflict. These are all the columns except primary keys, read-only
269
- # columns, and columns covered by the optional +unique_by+.
270
- #
271
- # ==== Options
272
- #
273
- # [:returning]
274
- # (PostgreSQL and SQLite3 only) An array of attributes to return for all successfully
275
- # inserted records, which by default is the primary key.
276
- # Pass <tt>returning: %w[ id name ]</tt> for both id and name
277
- # or <tt>returning: false</tt> to omit the underlying <tt>RETURNING</tt> SQL
278
- # clause entirely.
279
- #
280
- # You can also pass an SQL string if you need more control on the return values
281
- # (for example, <tt>returning: Arel.sql("id, name as new_name")</tt>).
282
- #
283
- # [:unique_by]
284
- # (PostgreSQL and SQLite only) By default rows are considered to be unique
285
- # by every unique index on the table. Any duplicate rows are skipped.
286
- #
287
- # To skip rows according to just one unique index pass <tt>:unique_by</tt>.
288
- #
289
- # Consider a Book model where no duplicate ISBNs make sense, but if any
290
- # row has an existing id, or is not unique by another unique index,
291
- # ActiveRecord::RecordNotUnique is raised.
292
- #
293
- # Unique indexes can be identified by columns or name:
294
- #
295
- # unique_by: :isbn
296
- # unique_by: %i[ author_id name ]
297
- # unique_by: :index_books_on_isbn
298
- #
299
- # Because it relies on the index information from the database
300
- # <tt>:unique_by</tt> is recommended to be paired with
301
- # Active Record's schema_cache.
302
- #
303
- # [:on_duplicate]
304
- # Configure the SQL update sentence that will be used in case of conflict.
305
- #
306
- # NOTE: If you use this option you must provide all the columns you want to update
307
- # by yourself.
308
- #
309
- # Example:
310
- #
311
- # Commodity.upsert_all(
312
- # [
313
- # { id: 2, name: "Copper", price: 4.84 },
314
- # { id: 4, name: "Gold", price: 1380.87 },
315
- # { id: 6, name: "Aluminium", price: 0.35 }
316
- # ],
317
- # on_duplicate: Arel.sql("price = GREATEST(commodities.price, EXCLUDED.price)")
318
- # )
319
- #
320
- # See the related +:update_only+ option. Both options can't be used at the same time.
321
- #
322
- # [:update_only]
323
- # Provide a list of column names that will be updated in case of conflict. If not provided,
324
- # +upsert_all+ will update all the columns that can be updated. These are all the columns
325
- # except primary keys, read-only columns, and columns covered by the optional +unique_by+
326
- #
327
- # Example:
328
- #
329
- # Commodity.upsert_all(
330
- # [
331
- # { id: 2, name: "Copper", price: 4.84 },
332
- # { id: 4, name: "Gold", price: 1380.87 },
333
- # { id: 6, name: "Aluminium", price: 0.35 }
334
- # ],
335
- # update_only: [:price] # Only prices will be updated
336
- # )
337
- #
338
- # See the related +:on_duplicate+ option. Both options can't be used at the same time.
339
- #
340
- # [:record_timestamps]
341
- # By default, automatic setting of timestamp columns is controlled by
342
- # the model's <tt>record_timestamps</tt> config, matching typical
343
- # behavior.
344
- #
345
- # To override this and force automatic setting of timestamp columns one
346
- # way or the other, pass <tt>:record_timestamps</tt>:
347
- #
348
- # record_timestamps: true # Always set timestamps automatically
349
- # record_timestamps: false # Never set timestamps automatically
350
- #
351
- # ==== Examples
352
- #
353
- # # Inserts multiple records, performing an upsert when records have duplicate ISBNs.
354
- # # Here "Eloquent Ruby" overwrites "Rework" because its ISBN is duplicate.
355
- #
356
- # Book.upsert_all([
357
- # { title: "Rework", author: "David", isbn: "1" },
358
- # { title: "Eloquent Ruby", author: "Russ", isbn: "1" }
359
- # ], unique_by: :isbn)
360
- #
361
- # Book.find_by(isbn: "1").title # => "Eloquent Ruby"
362
- def upsert_all(attributes, on_duplicate: :update, update_only: nil, returning: nil, unique_by: nil, record_timestamps: nil)
363
- InsertAll.new(self, attributes, on_duplicate: on_duplicate, update_only: update_only, returning: returning, unique_by: unique_by, record_timestamps: record_timestamps).execute
364
- end
365
-
366
90
  # Given an attributes hash, +instantiate+ returns a new instance of
367
91
  # the appropriate class. Accepts only keys as strings.
368
92
  #
@@ -456,7 +180,7 @@ module ActiveRecord
456
180
  end
457
181
 
458
182
  # Accepts a list of attribute names to be used in the WHERE clause
459
- # of SELECT / UPDATE / DELETE queries and in the ORDER BY clause for `#first` and `#last` finder methods.
183
+ # of SELECT / UPDATE / DELETE queries and in the ORDER BY clause for +#first+ and +#last+ finder methods.
460
184
  #
461
185
  # class Developer < ActiveRecord::Base
462
186
  # query_constraints :company_id, :id
@@ -469,7 +193,7 @@ module ActiveRecord
469
193
  # developer.update!(name: "Nikita")
470
194
  # # UPDATE "developers" SET "name" = 'Nikita' WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
471
195
  #
472
- # It is possible to update attribute used in the query_by clause:
196
+ # # It is possible to update an attribute used in the query_constraints clause:
473
197
  # developer.update!(company_id: 2)
474
198
  # # UPDATE "developers" SET "company_id" = 2 WHERE "developers"."company_id" = 1 AND "developers"."id" = 1
475
199
  #
@@ -511,62 +235,7 @@ module ActiveRecord
511
235
  @composite_query_constraints_list ||= query_constraints_list || Array(primary_key)
512
236
  end
513
237
 
514
- # Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
515
- # therefore all callbacks and filters are fired off before the object is deleted. This method is
516
- # less efficient than #delete but allows cleanup methods and other actions to be run.
517
- #
518
- # This essentially finds the object (or multiple objects) with the given id, creates a new object
519
- # from the attributes, and then calls destroy on it.
520
- #
521
- # ==== Parameters
522
- #
523
- # * +id+ - This should be the id or an array of ids to be destroyed.
524
- #
525
- # ==== Examples
526
- #
527
- # # Destroy a single object
528
- # Todo.destroy(1)
529
- #
530
- # # Destroy multiple objects
531
- # todos = [1,2,3]
532
- # Todo.destroy(todos)
533
- def destroy(id)
534
- multiple_ids = if composite_primary_key?
535
- id.first.is_a?(Array)
536
- else
537
- id.is_a?(Array)
538
- end
539
-
540
- if multiple_ids
541
- find(id).each(&:destroy)
542
- else
543
- find(id).destroy
544
- end
545
- end
546
-
547
- # Deletes the row with a primary key matching the +id+ argument, using an
548
- # SQL +DELETE+ statement, and returns the number of rows deleted. Active
549
- # Record objects are not instantiated, so the object's callbacks are not
550
- # executed, including any <tt>:dependent</tt> association options.
551
- #
552
- # You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
553
- #
554
- # Note: Although it is often much faster than the alternative, #destroy,
555
- # skipping callbacks might bypass business logic in your application
556
- # that ensures referential integrity or performs other essential jobs.
557
- #
558
- # ==== Examples
559
- #
560
- # # Delete a single row
561
- # Todo.delete(1)
562
- #
563
- # # Delete multiple rows
564
- # Todo.delete([2,3,4])
565
- def delete(id_or_array)
566
- delete_by(primary_key => id_or_array)
567
- end
568
-
569
- def _insert_record(values, returning) # :nodoc:
238
+ def _insert_record(connection, values, returning) # :nodoc:
570
239
  primary_key = self.primary_key
571
240
  primary_key_value = nil
572
241
 
@@ -579,16 +248,18 @@ module ActiveRecord
579
248
 
580
249
  im = Arel::InsertManager.new(arel_table)
581
250
 
582
- if values.empty?
583
- im.insert(connection.empty_insert_statement_value(primary_key))
584
- else
585
- im.insert(values.transform_keys { |name| arel_table[name] })
586
- end
251
+ with_connection do |c|
252
+ if values.empty?
253
+ im.insert(connection.empty_insert_statement_value(primary_key))
254
+ else
255
+ im.insert(values.transform_keys { |name| arel_table[name] })
256
+ end
587
257
 
588
- connection.insert(
589
- im, "#{self} Create", primary_key || false, primary_key_value,
590
- returning: returning
591
- )
258
+ connection.insert(
259
+ im, "#{self} Create", primary_key || false, primary_key_value,
260
+ returning: returning
261
+ )
262
+ end
592
263
  end
593
264
 
594
265
  def _update_record(values, constraints) # :nodoc:
@@ -605,7 +276,9 @@ module ActiveRecord
605
276
  um.set(values.transform_keys { |name| arel_table[name] })
606
277
  um.wheres = constraints
607
278
 
608
- connection.update(um, "#{self} Update")
279
+ with_connection do |c|
280
+ c.update(um, "#{self} Update")
281
+ end
609
282
  end
610
283
 
611
284
  def _delete_record(constraints) # :nodoc:
@@ -621,7 +294,9 @@ module ActiveRecord
621
294
  dm = Arel::DeleteManager.new(arel_table)
622
295
  dm.wheres = constraints
623
296
 
624
- connection.delete(dm, "#{self} Destroy")
297
+ with_connection do |c|
298
+ c.delete(dm, "#{self} Destroy")
299
+ end
625
300
  end
626
301
 
627
302
  private
@@ -1067,7 +742,7 @@ module ActiveRecord
1067
742
  # end
1068
743
  #
1069
744
  def reload(options = nil)
1070
- self.class.connection.clear_query_cache
745
+ self.class.connection_pool.clear_query_cache
1071
746
 
1072
747
  fresh_object = if apply_scoping?(options)
1073
748
  _find_record((options || {}).merge(all_queries: true))
@@ -1247,16 +922,19 @@ module ActiveRecord
1247
922
  def _create_record(attribute_names = self.attribute_names)
1248
923
  attribute_names = attributes_for_create(attribute_names)
1249
924
 
1250
- returning_columns = self.class._returning_columns_for_insert
925
+ self.class.with_connection do |connection|
926
+ returning_columns = self.class._returning_columns_for_insert(connection)
1251
927
 
1252
- returning_values = self.class._insert_record(
1253
- attributes_with_values(attribute_names),
1254
- returning_columns
1255
- )
928
+ returning_values = self.class._insert_record(
929
+ connection,
930
+ attributes_with_values(attribute_names),
931
+ returning_columns
932
+ )
1256
933
 
1257
- returning_columns.zip(returning_values).each do |column, value|
1258
- _write_attribute(column, value) if !_read_attribute(column)
1259
- end if returning_values
934
+ returning_columns.zip(returning_values).each do |column, value|
935
+ _write_attribute(column, value) if !_read_attribute(column)
936
+ end if returning_values
937
+ end
1260
938
 
1261
939
  @new_record = false
1262
940
  @previously_new_record = true
@@ -8,7 +8,13 @@ module ActiveRecord
8
8
  # If it's not, it will execute the given block.
9
9
  def cache(&block)
10
10
  if connected? || !configurations.empty?
11
- connection.cache(&block)
11
+ pool = connection_pool
12
+ was_enabled = pool.query_cache_enabled
13
+ begin
14
+ pool.enable_query_cache(&block)
15
+ ensure
16
+ pool.clear_query_cache unless was_enabled
17
+ end
12
18
  else
13
19
  yield
14
20
  end
@@ -16,9 +22,12 @@ module ActiveRecord
16
22
 
17
23
  # Disable the query cache within the block if Active Record is configured.
18
24
  # If it's not, it will execute the given block.
19
- def uncached(&block)
25
+ #
26
+ # Set <tt>dirties: false</tt> to prevent query caches on all connections from being cleared by write operations.
27
+ # (By default, write operations dirty all connections' query caches in case they are replicas whose cache would now be outdated.)
28
+ def uncached(dirties: true, &block)
20
29
  if connected? || !configurations.empty?
21
- connection.uncached(&block)
30
+ connection_pool.disable_query_cache(dirties: dirties, &block)
22
31
  else
23
32
  yield
24
33
  end
@@ -26,14 +35,17 @@ module ActiveRecord
26
35
  end
27
36
 
28
37
  def self.run
29
- ActiveRecord::Base.connection_handler.each_connection_pool.reject { |p| p.query_cache_enabled }.each { |p| p.enable_query_cache! }
38
+ ActiveRecord::Base.connection_handler.each_connection_pool.reject(&:query_cache_enabled).each(&:enable_query_cache!)
30
39
  end
31
40
 
32
41
  def self.complete(pools)
33
- pools.each { |pool| pool.disable_query_cache! }
42
+ pools.each do |pool|
43
+ pool.disable_query_cache!
44
+ pool.clear_query_cache
45
+ end
34
46
 
35
47
  ActiveRecord::Base.connection_handler.each_connection_pool do |pool|
36
- pool.release_connection if pool.active_connection? && !pool.connection.transaction_open?
48
+ pool.release_connection if pool.active_connection? && !pool.lease_connection.transaction_open?
37
49
  end
38
50
  end
39
51
 
@@ -26,6 +26,7 @@ module ActiveRecord
26
26
  # * +socket+
27
27
  # * +db_host+
28
28
  # * +database+
29
+ # * +source_location+
29
30
  #
30
31
  # Action Controller adds default tags when loaded:
31
32
  #
@@ -108,6 +109,20 @@ module ActiveRecord
108
109
  end
109
110
  end
110
111
 
112
+ if Thread.respond_to?(:each_caller_location)
113
+ def query_source_location # :nodoc:
114
+ Thread.each_caller_location do |location|
115
+ frame = LogSubscriber.backtrace_cleaner.clean_frame(location.path)
116
+ return frame if frame
117
+ end
118
+ nil
119
+ end
120
+ else
121
+ def query_source_location # :nodoc:
122
+ LogSubscriber.backtrace_cleaner.clean(caller_locations(1).each).first
123
+ end
124
+ end
125
+
111
126
  ActiveSupport::ExecutionContext.after_change { ActiveRecord::QueryLogs.clear_cache }
112
127
 
113
128
  private
@@ -28,7 +28,7 @@ module ActiveRecord
28
28
  end
29
29
 
30
30
  def format(pairs)
31
- pairs.sort_by!(&:first)
31
+ pairs.sort_by! { |pair| pair.first.to_s }
32
32
  super
33
33
  end
34
34
 
@@ -10,15 +10,16 @@ module ActiveRecord
10
10
  :first_or_create, :first_or_create!, :first_or_initialize,
11
11
  :find_or_create_by, :find_or_create_by!, :find_or_initialize_by,
12
12
  :create_or_find_by, :create_or_find_by!,
13
- :destroy_all, :delete_all, :update_all, :touch_all, :destroy_by, :delete_by,
13
+ :destroy, :destroy_all, :delete, :delete_all, :update_all, :touch_all, :destroy_by, :delete_by,
14
14
  :find_each, :find_in_batches, :in_batches,
15
15
  :select, :reselect, :order, :regroup, :in_order_of, :reorder, :group, :limit, :offset, :joins, :left_joins, :left_outer_joins,
16
16
  :where, :rewhere, :invert_where, :preload, :extract_associated, :eager_load, :includes, :from, :lock, :readonly,
17
17
  :and, :or, :annotate, :optimizer_hints, :extending,
18
18
  :having, :create_with, :distinct, :references, :none, :unscope, :merge, :except, :only,
19
19
  :count, :average, :minimum, :maximum, :sum, :calculate,
20
- :pluck, :pick, :ids, :async_ids, :strict_loading, :excluding, :without, :with,
20
+ :pluck, :pick, :ids, :async_ids, :strict_loading, :excluding, :without, :with, :with_recursive,
21
21
  :async_count, :async_average, :async_minimum, :async_maximum, :async_sum, :async_pluck, :async_pick,
22
+ :insert, :insert_all, :insert!, :insert_all!, :upsert, :upsert_all
22
23
  ].freeze # :nodoc:
23
24
  delegate(*QUERYING_METHODS, to: :all)
24
25
 
@@ -47,19 +48,26 @@ module ActiveRecord
47
48
  #
48
49
  # Note that building your own SQL query string from user input may expose your application to
49
50
  # injection attacks (https://guides.rubyonrails.org/security.html#sql-injection).
50
- def find_by_sql(sql, binds = [], preparable: nil, &block)
51
- _load_from_sql(_query_by_sql(sql, binds, preparable: preparable), &block)
51
+ def find_by_sql(sql, binds = [], preparable: nil, allow_retry: false, &block)
52
+ result = with_connection do |c|
53
+ _query_by_sql(c, sql, binds, preparable: preparable, allow_retry: allow_retry)
54
+ end
55
+ _load_from_sql(result, &block)
52
56
  end
53
57
 
54
58
  # Same as <tt>#find_by_sql</tt> but perform the query asynchronously and returns an ActiveRecord::Promise.
55
59
  def async_find_by_sql(sql, binds = [], preparable: nil, &block)
56
- _query_by_sql(sql, binds, preparable: preparable, async: true).then do |result|
60
+ result = with_connection do |c|
61
+ _query_by_sql(c, sql, binds, preparable: preparable, async: true)
62
+ end
63
+
64
+ result.then do |result|
57
65
  _load_from_sql(result, &block)
58
66
  end
59
67
  end
60
68
 
61
- def _query_by_sql(sql, binds = [], preparable: nil, async: false) # :nodoc:
62
- connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable, async: async)
69
+ def _query_by_sql(connection, sql, binds = [], preparable: nil, async: false, allow_retry: false) # :nodoc:
70
+ connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable, async: async, allow_retry: allow_retry)
63
71
  end
64
72
 
65
73
  def _load_from_sql(result_set, &block) # :nodoc:
@@ -99,12 +107,16 @@ module ActiveRecord
99
107
  #
100
108
  # * +sql+ - An SQL statement which should return a count query from the database, see the example above.
101
109
  def count_by_sql(sql)
102
- connection.select_value(sanitize_sql(sql), "#{name} Count").to_i
110
+ with_connection do |c|
111
+ c.select_value(sanitize_sql(sql), "#{name} Count").to_i
112
+ end
103
113
  end
104
114
 
105
115
  # Same as <tt>#count_by_sql</tt> but perform the query asynchronously and returns an ActiveRecord::Promise.
106
116
  def async_count_by_sql(sql)
107
- connection.select_value(sanitize_sql(sql), "#{name} Count", async: true).then(&:to_i)
117
+ with_connection do |c|
118
+ c.select_value(sanitize_sql(sql), "#{name} Count", async: true).then(&:to_i)
119
+ end
108
120
  end
109
121
  end
110
122
  end